--- old/make/CopyFiles.gmk 2019-02-08 18:32:18.102792634 +0300 +++ new/make/CopyFiles.gmk 2019-02-08 18:32:17.994796416 +0300 @@ -585,4 +585,22 @@ ########################################################################################## +$(JDK_OUTPUTDIR)/lib/jfr/%: $(JDK_TOPDIR)/src/share/classes/jdk/jfr/conf/% + $(call install-file) + +COPY_FILES += $(subst $(JDK_TOPDIR)/src/share/classes/jdk/jfr/conf, \ + $(JDK_OUTPUTDIR)/lib/jfr, \ + $(wildcard $(JDK_TOPDIR)/src/share/classes/jdk/jfr/conf/*)) + +########################################################################################## + +$(JDK_OUTPUTDIR)/lib/jfr/%: $(JDK_TOPDIR)/src/share/classes/jdk/jfr/conf/% + $(call install-file) + +COPY_FILES += $(subst $(JDK_TOPDIR)/src/share/classes/jdk/jfr/conf, \ + $(JDK_OUTPUTDIR)/lib/jfr, \ + $(wildcard $(JDK_TOPDIR)/src/share/classes/jdk/jfr/conf/*)) + +########################################################################################## + -include $(CUSTOM_MAKE_DIR)/CopyFiles.gmk --- old/make/CopyIntoClasses.gmk 2019-02-08 18:32:18.242787731 +0300 +++ new/make/CopyIntoClasses.gmk 2019-02-08 18:32:18.146791093 +0300 @@ -92,9 +92,11 @@ endif ifeq ($(ENABLE_JFR), true) - JFR_CONFIGURATION_DIR_CLOSED = $(JDK_TOPDIR)/src/closed/share/classes/oracle/jrockit/jfr/settings - COPY_FILES += \ - $(JFR_CONFIGURATION_DIR_CLOSED)/jfc.xsd +# JFR_CONFIGURATION_DIR_CLOSED = $(JDK_TOPDIR)/src/closed/share/classes/oracle/jrockit/jfr/settings +# COPY_FILES += \ +# $(JFR_CONFIGURATION_DIR_CLOSED)/jfc.xsd +# COPY_FILES += \ +# $(HOTSPOT_DIST)/jre/lib/metadata.xml endif SWING_PLAF_BASIC_RESOURCES_DIR = $(JDK_TOPDIR)/src/share/classes/javax/swing/plaf/basic --- old/make/CreateJars.gmk 2019-02-08 18:32:18.386782687 +0300 +++ new/make/CreateJars.gmk 2019-02-08 18:32:18.290786049 +0300 @@ -211,8 +211,7 @@ sun/util/cldr/CLDRLocaleDataMetaInfo.class \ sun/util/resources/cldr \ $(LOCALEDATA_INCLUDES) \ - com/oracle/jrockit/jfr \ - oracle/jrockit/jfr \ + jdk/management/jfr \ jdk/jfr # Find all files in the classes dir to use as dependencies. This could be more fine granular. @@ -400,20 +399,17 @@ ########################################################################################## -ifndef OPENJDK - ifeq ($(ENABLE_JFR), true) - $(eval $(call SetupArchive,BUILD_JFR_JAR, , \ - SRCS := $(JDK_OUTPUTDIR)/classes, \ - SUFFIXES := .class .jfc .xsd, \ - INCLUDES := com/oracle/jrockit/jfr \ - oracle/jrockit/jfr \ - jdk/jfr, \ - JAR := $(IMAGES_OUTPUTDIR)/lib/jfr.jar, \ - SKIP_METAINF := true, \ - MANIFEST := $(MAINMANIFEST), \ - CHECK_COMPRESS_JAR := true)) +ifeq ($(ENABLE_JFR), true) +$(eval $(call SetupArchive,BUILD_JFR_JAR, , \ +SRCS := $(JDK_OUTPUTDIR)/classes $(HOTSPOT_DIST)/jre/lib, \ +SUFFIXES := .class .jfc .xsd .xml, \ +INCLUDES := jdk/management/jfr \ +jdk/jfr, \ +JAR := $(IMAGES_OUTPUTDIR)/lib/jfr.jar, \ +SKIP_METAINF := true, \ +MANIFEST := $(MAINMANIFEST), \ +CHECK_COMPRESS_JAR := true)) - endif endif ########################################################################################## --- old/src/share/classes/sun/management/ExtendedPlatformComponent.java 2019-02-08 18:32:18.538777363 +0300 +++ new/src/share/classes/sun/management/ExtendedPlatformComponent.java 2019-02-08 18:32:18.438780865 +0300 @@ -25,9 +25,12 @@ package sun.management; +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.lang.management.PlatformManagedObject; +import java.lang.reflect.Method; /** * Class to allow for an extended set of platform MXBeans @@ -40,7 +43,12 @@ * platform MBeanServer, or an empty list if there are no such MXBeans. */ public static List getMXBeans() { - return Collections.emptyList(); + PlatformManagedObject o = getFlightRecorderBean(); + if (o != null) { + return Collections.singletonList(o); + } else { + return Collections.emptyList(); + } } /** @@ -49,6 +57,23 @@ */ public static T getMXBean(Class mxbeanInterface) { + + if ("jdk.management.jfr.FlightRecorderMXBean".equals(mxbeanInterface.getName())) { + return (T)getFlightRecorderBean(); + } return null; } + + private static PlatformManagedObject getFlightRecorderBean() { + PlatformManagedObject object = null; + try { + Class provider = Class.forName("jdk.management.jfr.internal.FlightRecorderMXBeanProvider"); + Method m = provider.getDeclaredMethod("getFlightRecorderMXBean"); + + object = (PlatformManagedObject)m.invoke(null); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // no jfr? + } + return object; + } } --- old/test/TEST.ROOT 2019-02-08 18:32:18.674772600 +0300 +++ new/test/TEST.ROOT 2019-02-08 18:32:18.578775962 +0300 @@ -9,7 +9,7 @@ # should be taken to handle test failures of intermittent or # randomness tests. -keys=2d dnd i18n intermittent randomness +keys=2d dnd i18n intermittent randomness jfr # Tests that must run in othervm mode othervm.dirs=java/awt java/beans java/rmi javax/accessibility javax/imageio javax/sound javax/print javax/management com/sun/awt sun/awt sun/java2d sun/pisces sun/rmi --- old/test/TEST.groups 2019-02-08 18:32:18.826767276 +0300 +++ new/test/TEST.groups 2019-02-08 18:32:18.722770919 +0300 @@ -31,6 +31,11 @@ jdk/lambda \ vm +#JFR tests +jdk_jfr = \ + jdk/jfr + + # All of the java.util package jdk_util = \ :jdk_util_other \ --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/AnnotationElement.java 2019-02-08 18:32:18.874765595 +0300 @@ -0,0 +1,420 @@ +/* + * 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; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.StringJoiner; + +import jdk.jfr.internal.Type; +import jdk.jfr.internal.TypeLibrary; +import jdk.jfr.internal.Utils; + +/** + * Describes event metadata, such as labels, descriptions and units. + *

+ * The following example shows how {@code AnnotationElement} can be used to dynamically define events. + * + *

+ * 
+ *   List{@literal <}AnnotationElement{@literal >} typeAnnotations = new ArrayList{@literal <}{@literal >}();
+ *   typeannotations.add(new AnnotationElement(Name.class, "com.example.HelloWorld");
+ *   typeAnnotations.add(new AnnotationElement(Label.class, "Hello World"));
+ *   typeAnnotations.add(new AnnotationElement(Description.class, "Helps programmer getting started"));
+ *
+ *   List{@literal <}AnnotationElement{@literal >} fieldAnnotations = new ArrayList{@literal <}{@literal >}();
+ *   fieldAnnotations.add(new AnnotationElement(Label.class, "Message"));
+ *
+ *   List{@literal <}ValueDescriptor{@literal >} fields = new ArrayList{@literal <}{@literal >}();
+ *   fields.add(new ValueDescriptor(String.class, "message", fieldAnnotations));
+ *
+ *   EventFactory f = EventFactory.create(typeAnnotations, fields);
+ *   Event event = f.newEvent();
+ *   event.commit();
+ * 
+ * 
+ * + * @since 9 + */ +public final class AnnotationElement { + private final Type type; + private final List annotationValues; + private final List annotationNames; + private final boolean inBootClassLoader; + + // package private + AnnotationElement(Type type, List objects, boolean boot) { + Objects.requireNonNull(type); + Objects.requireNonNull(objects); + this.type = type; + if (objects.size() != type.getFields().size()) { + StringJoiner descriptors = new StringJoiner(",", "[", "]"); + for (ValueDescriptor v : type.getFields()) { + descriptors.add(v.getName()); + } + StringJoiner values = new StringJoiner(",", "[", "]"); + for (Object object : objects) { + descriptors.add(String.valueOf(object)); + } + throw new IllegalArgumentException("Annotation " + descriptors + " for " + type.getName() + " doesn't match number of values " + values); + } + + List n = new ArrayList<>(); + List v = new ArrayList<>(); + int index = 0; + for (ValueDescriptor valueDescriptor : type.getFields()) { + Object object = objects.get(index); + if (object == null) { + throw new IllegalArgumentException("Annotation value can't be null"); + } + Class valueType = object.getClass(); + if (valueDescriptor.isArray()) { + valueType = valueType.getComponentType(); + } + checkType(Utils.unboxType(valueType)); + n.add(valueDescriptor.getName()); + v.add(object); + index++; + } + this.annotationValues = Utils.smallUnmodifiable(v); + this.annotationNames = Utils.smallUnmodifiable(n); + this.inBootClassLoader = boot; + } + + /** + * Creates an annotation element to use for dynamically defined events. + *

+ * Supported value types are {@code byte}, {@code int}, {@code short}, + * {@code long}, {@code double}, {@code float}, {@code boolean}, {@code char}, + * and {@code String}. Enums, arrays and classes, are not supported. + *

+ * If {@code annotationType} has annotations (directly present, indirectly + * present, or associated), then those annotation are recursively included. + * However, both the {@code annotationType} and any annotation found recursively + * must have the {@link MetadataDefinition} annotation. + *

+ * To statically define events, see {@link Event} class. + * + * @param annotationType interface extending + * {@code java.lang.annotation.Annotation}, not {@code null} + * @param values a {@code Map} with keys that match method names of the specified + * annotation interface + * @throws IllegalArgumentException if value/key is {@code null}, an unsupported + * value type is used, or a value/key is used that doesn't match the + * signatures in the {@code annotationType} + */ + public AnnotationElement(Class annotationType, Map values) { + Objects.requireNonNull(annotationType); + Objects.requireNonNull(values); + Utils.checkRegisterPermission(); + // copy values to avoid modification after validation + HashMap map = new HashMap<>(values); + for (Map.Entry entry : map.entrySet()) { + if (entry.getKey() == null) { + throw new NullPointerException("Name of annotation method can't be null"); + } + if (entry.getValue() == null) { + throw new NullPointerException("Return value for annotation method can't be null"); + } + } + + if (AnnotationElement.class.isAssignableFrom(annotationType) && annotationType.isInterface()) { + throw new IllegalArgumentException("Must be interface extending " + Annotation.class.getName()); + } + if (!isKnownJFRAnnotation(annotationType) && annotationType.getAnnotation(MetadataDefinition.class) == null) { + throw new IllegalArgumentException("Annotation class must be annotated with jdk.jfr.MetadataDefinition to be valid"); + } + if (isKnownJFRAnnotation(annotationType)) { + this.type = new Type(annotationType.getCanonicalName(), Type.SUPER_TYPE_ANNOTATION, Type.getTypeId(annotationType)); + } else { + this.type = TypeLibrary.createAnnotationType(annotationType); + } + Method[] methods = annotationType.getDeclaredMethods(); + if (methods.length != map.size()) { + throw new IllegalArgumentException("Number of declared methods must match size of value map"); + } + List n = new ArrayList<>(); + List v = new ArrayList<>(); + Set nameSet = new HashSet<>(); + for (Method method : methods) { + String fieldName = method.getName(); + Object object = map.get(fieldName); + if (object == null) { + throw new IllegalArgumentException("No method in annotation interface " + annotationType.getName() + " matching name " + fieldName); + } + Class fieldType = object.getClass(); + + if (fieldType == Class.class) { + throw new IllegalArgumentException("Annotation value for " + fieldName + " can't be class"); + } + if (object instanceof Enum) { + throw new IllegalArgumentException("Annotation value for " + fieldName + " can't be enum"); + } + if (!fieldType.equals(object.getClass())) { + throw new IllegalArgumentException("Return type of annotation " + fieldType.getName() + " must match type of object" + object.getClass()); + } + + if (fieldType.isArray()) { + Class componentType = fieldType.getComponentType(); + checkType(componentType); + if (componentType.equals(String.class)) { + String[] stringArray = (String[]) object; + for (int i = 0; i < stringArray.length; i++) { + if (stringArray[i] == null) { + throw new IllegalArgumentException("Annotation value for " + fieldName + " contains null"); + } + } + } + } else { + fieldType = Utils.unboxType(object.getClass()); + checkType(fieldType); + } + if (nameSet.contains(fieldName)) { + throw new IllegalArgumentException("Value with name '" + fieldName + "' already exists"); + } + if (isKnownJFRAnnotation(annotationType)) { + ValueDescriptor vd = new ValueDescriptor(fieldType, fieldName, Collections.emptyList(), true); + type.add(vd); + } + n.add(fieldName); + v.add(object); + } + this.annotationValues = Utils.smallUnmodifiable(v); + this.annotationNames = Utils.smallUnmodifiable(n); + this.inBootClassLoader = annotationType.getClassLoader() == null; + } + + /** + * Creates an annotation element to use for dynamically defined events. + *

+ * Supported value types are {@code byte}, {@code int}, {@code short}, + * {@code long}, {@code double}, {@code float}, {@code boolean}, {@code char}, + * and {@code String}. Enums, arrays, and classes are not supported. + *

+ * If {@code annotationType} has annotations (directly present, indirectly + * present, or associated), then those annotations are recursively included. + * However, both {@code annotationType} and any annotation found recursively + * must have the {@link MetadataDefinition} annotation. + *

+ * To statically define events, see {@link Event} class. + * + * @param annotationType interface extending + * {@code java.lang.annotation.Annotation,} not {@code null} + * @param value the value that matches the {@code value} method of the specified + * {@code annotationType} + * @throws IllegalArgumentException if value/key is {@code null}, an unsupported + * value type is used, or a value/key is used that doesn't match the + * signatures in the {@code annotationType} + */ + public AnnotationElement(Class annotationType, Object value) { + this(annotationType, Collections.singletonMap("value", Objects.requireNonNull(value))); + } + + /** + * Creates an annotation element to use for dynamically defined events. + *

+ * Supported value types are {@code byte}, {@code short}, {@code int}, + * {@code long}, {@code double}, {@code float}, {@code boolean}, {@code char}, + * and {@code String}. Enums, arrays, and classes are not supported. + *

+ * If {@code annotationType} has annotations (directly present, indirectly + * present or associated), then those annotation are recursively included. + * However, both {@code annotationType} and any annotation found recursively + * must have the {@link MetadataDefinition} annotation. + *

+ * To statically define events, see {@link Event} class. + * + * @param annotationType interface extending java.lang.annotation.Annotation, + * not {@code null} + */ + public AnnotationElement(Class annotationType) { + this(annotationType, Collections.emptyMap()); + } + + /** + * Returns an immutable list of annotation values in an order that matches the + * value descriptors for this {@code AnnotationElement}. + * + * @return list of values, not {@code null} + */ + public List getValues() { + return annotationValues; + } + + /** + * Returns an immutable list of descriptors that describes the annotation values + * for this {@code AnnotationElement}. + * + * @return the list of value descriptors for this {@code Annotation}, not + * {@code null} + */ + public List getValueDescriptors() { + return Collections.unmodifiableList(type.getFields()); + } + + /** + * Returns an immutable list of annotation elements for this + * {@code AnnotationElement}. + * + * @return a list of meta annotation, not {@code null} + */ + public List getAnnotationElements() { + return type.getAnnotationElements(); + } + + /** + * Returns the fully qualified name of the annotation type that corresponds to + * this {@code AnnotationElement} (for example, {@code "jdk.jfr.Label"}). + * + * @return type name, not {@code null} + */ + public String getTypeName() { + return type.getName(); + } + + /** + * Returns a value for this {@code AnnotationElement}. + * + * @param name the name of the method in the annotation interface, not + * {@code null}. + * + * @return the annotation value, not {@code null}. + * + * @throws IllegalArgumentException if a method with the specified name does + * not exist in the annotation + */ + public Object getValue(String name) { + Objects.requireNonNull(name); + int index = 0; + for (String n : annotationNames) { + if (name.equals(n)) { + return annotationValues.get(index); + } + index++; + } + StringJoiner valueNames = new StringJoiner(",", "[", "]"); + for (ValueDescriptor v : type.getFields()) { + valueNames.add(v.getName()); + } + throw new IllegalArgumentException("No value with name '" + name + "'. Valid names are " + valueNames); + } + + /** + * Returns {@code true} if an annotation value with the specified name exists in + * this {@code AnnotationElement}. + * + * @param name name of the method in the annotation interface to find, not + * {@code null} + * + * @return {@code true} if method exists, {@code false} otherwise + */ + public boolean hasValue(String name) { + Objects.requireNonNull(name); + for (String n : annotationNames) { + if (name.equals(n)) { + return true; + } + } + return false; + } + + /** + * Returns the first annotation for the specified type if an + * {@code AnnotationElement} with the same name exists, else {@code null}. + * + * @param the type of the annotation to query for and return if it exists + * @param annotationType the {@code Class object} corresponding to the annotation type, + * not {@code null} + * @return this element's annotation for the specified annotation type if + * it it exists, else {@code null} + */ + public final A getAnnotation(Class annotationType) { + Objects.requireNonNull(annotationType); + return type.getAnnotation(annotationType); + } + + /** + * Returns the type ID for this {@code AnnotationElement}. + *

+ * The ID is a unique identifier for the type in the Java Virtual Machine (JVM). The ID might not + * be the same between JVM instances. + * + * @return the type ID, not negative + */ + public long getTypeId() { + return type.getId(); + } + + // package private + Type getType() { + return type; + } + + private static void checkType(Class type) { + if (type.isPrimitive()) { + return; + } + if (type == String.class) { + return; + } + throw new IllegalArgumentException("Only primitives types or java.lang.String are allowed"); + } + + // Whitelist of annotation classes that are allowed, even though + // they don't have @MetadataDefinition. + private static boolean isKnownJFRAnnotation(Class annotationType) { + if (annotationType == Registered.class) { + return true; + } + if (annotationType == Threshold.class) { + return true; + } + if (annotationType == StackTrace.class) { + return true; + } + if (annotationType == Period.class) { + return true; + } + if (annotationType == Enabled.class) { + return true; + } + return false; + } + + // package private + boolean isInBoot() { + return inBootClassLoader; + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/BooleanFlag.java 2019-02-08 18:32:19.018760552 +0300 @@ -0,0 +1,45 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Event field annotation, specifies that the value is a boolean flag, a {@code true} or + * {@code false} value + * + * @since 9 + */ +@MetadataDefinition +@ContentType +@Label("Flag") +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE, ElementType.METHOD}) +public @interface BooleanFlag { +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Category.java 2019-02-08 18:32:19.162755509 +0300 @@ -0,0 +1,130 @@ +/* + * 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; + +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; + +/** + * Event annotation, to associate the event type with a category, in the format + * of a human-readable path. + *

+ * The category determines how an event is presented to the user. Events that + * are in the same category are typically displayed together in graphs and + * trees. To avoid the overlap of durational events in graphical + * representations, overlapping events must be in separate categories. + *

+ * For example, to monitor image uploads to a web server with a separate thread + * for each upload, an event called File Upload starts when the user uploads a + * file and ends when the upload is complete. For advanced diagnostics about + * image uploads, more detailed events are created (for example, Image Read, + * Image Resize, and Image Write). During these detailed events. other low + * level-events could occur (for example, Socket Read and File Write). + *

+ * The following example shows a visualization that avoids overlaps: + * + *

+ * -------------------------------------------------------------------
+ *   |                         File Upload                        |
+ * ------------------------------------------------------------------
+ *   |       Image Read          | Image Resize |   Image Write   |
+ * ------------------------------------------------------------------
+ *   | Socket Read | Socket Read |              |    File Write   |
+ * -------------------------------------------------------------------
+ * 
+ * + * The example can be achieved by using the following categories: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Recording options and their purpose.
Event NameAnnotation
File Upload@Category("Upload")
Image Read@Category({"Upload", "Image Upload"})
Image Resize@Category({"Upload", "Image Upload"})
Image Write@Category({"Upload", "Image Upload"})
Socket Read@Category("Java Application")
File Write@Category("Java Application")
+ *

+ * The File Upload, Image Read, and Socket Read events happen concurrently (in + * the same thread), but the events are in different categories so they do not + * overlap in the visualization. + *

+ * The following examples shows how the category is used to determine how events + * are visualized in a tree: + * + *

+ *  |- Java Application
+ *  |  |- Socket Read
+ *  |  |- File Write
+ *  |- Upload
+ *     |- File Upload
+ *     |- Image Upload
+ *        |- Image Read
+ *        |- Image Resize
+ *        |- File Write
+ * 
+ * + * @since 9 + */ +@MetadataDefinition +@Target({ ElementType.TYPE }) +@Inherited +@Retention(RetentionPolicy.RUNTIME) +public @interface Category { + /** + * Returns the category names for this annotation, starting with the root. + * + * @return the category names + */ + String[] value(); +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Configuration.java 2019-02-08 18:32:19.306750466 +0300 @@ -0,0 +1,197 @@ +/* + * 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; + +import java.io.IOException; +import java.io.Reader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import jdk.jfr.internal.JVMSupport; +import jdk.jfr.internal.jfc.JFC; + +/** + * A collection of settings and metadata describing the configuration. + * + * @since 9 + */ +public final class Configuration { + private final Map settings; + private final String label; + private final String description; + private final String provider; + private final String contents; + private final String name; + + // package private + Configuration(String name, String label, String description, String provider, Map settings, String contents) { + this.name = name; + this.label = label; + this.description = description; + this.provider = provider; + this.settings = settings; + this.contents = contents; + } + + /** + * Returns the settings that specifies how a recording is configured. + *

+ * Modifying the returned {@code Map} object doesn't change the + * configuration. + * + * @return settings, not {@code null} + */ + public Map getSettings() { + return new LinkedHashMap(settings); + } + + /** + * Returns an identifying name (for example, {@code "default" or "profile")}. + * + * @return the name, or {@code null} if it doesn't exist + */ + public String getName() { + return this.name; + } + + /** + * Returns a human-readable name (for example, {@code "Continuous" or "Profiling"}}. + * + * @return the label, or {@code null} if it doesn't exist + */ + public String getLabel() { + return this.label; + } + + /** + * Returns a short sentence that describes the configuration (for example + * {@code "Low + * overhead configuration safe for continuous use in production + * environments"}) + * + * @return the description, or {@code null} if it doesn't exist + */ + public String getDescription() { + return description; + } + + /** + * Returns who created the configuration (for example {@code "OpenJDK"}). + * + * @return the provider, or {@code null} if it doesn't exist + */ + public String getProvider() { + return provider; + } + + /** + * Returns a textual representation of the configuration (for example, the + * contents of a JFC file). + * + * @return contents, or {@code null} if it doesn't exist + * + * @see Configuration#getContents() + */ + public String getContents() { + return contents; + } + + /** + * Reads a configuration from a file. + * + * @param path the file that contains the configuration, not {@code null} + * @return the read {@link Configuration}, not {@code null} + * @throws ParseException if the file can't be parsed + * @throws IOException if the file can't be read + * @throws SecurityException if a security manager exists and its + * {@code checkRead} method denies read access to the file. + * + * @see java.io.File#getPath() + * @see java.lang.SecurityManager#checkRead(java.lang.String) + */ + public static Configuration create(Path path) throws IOException, ParseException { + Objects.requireNonNull(path); + JVMSupport.ensureWithIOException(); + try (Reader reader = Files.newBufferedReader(path)) { + return JFC.create(JFC.nameFromPath(path), reader); + } + } + + /** + * Reads a configuration from a character stream. + * + * @param reader a {@code Reader} that provides the configuration contents, not + * {@code null} + * @return a configuration, not {@code null} + * @throws IOException if an I/O error occurs while trying to read contents + * from the {@code Reader} + * @throws ParseException if the file can't be parsed + */ + public static Configuration create(Reader reader) throws IOException, ParseException { + Objects.requireNonNull(reader); + JVMSupport.ensureWithIOException(); + return JFC.create(null, reader); + } + + /** + * Returns a predefined configuration. + *

+ * See {@link Configuration#getConfigurations()} for available configuration + * names. + * + * @param name the name of the configuration (for example, {@code "default"} or + * {@code "profile"}) + * @return a configuration, not {@code null} + * + * @throws IOException if a configuration with the given name does not + * exist, or if an I/O error occurs while reading the + * configuration file + * @throws ParseException if the configuration file can't be parsed + */ + public static Configuration getConfiguration(String name) throws IOException, ParseException { + JVMSupport.ensureWithIOException(); + return JFC.getPredefined(name); + } + + /** + * Returns an immutable list of predefined configurations for this Java Virtual Machine (JVM). + * + * @return the list of predefined configurations, not {@code null} + */ + public static List getConfigurations() { + if (JVMSupport.isNotAvailable()) { + return new ArrayList<>(); + } + return Collections.unmodifiableList(JFC.getConfigurations()); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/ContentType.java 2019-02-08 18:32:19.450745423 +0300 @@ -0,0 +1,45 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Meta annotation, specifies that an annotation represents a content type, such + * as a time span or a frequency. + * + * @since 9 + */ +@MetadataDefinition +@Label("Content Type") +@Description("Semantic meaning of a value") +@Target(ElementType.ANNOTATION_TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface ContentType { +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/DataAmount.java 2019-02-08 18:32:19.598740240 +0300 @@ -0,0 +1,60 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Event field annotation, specifies that a value represents an amount of data (for example, bytes). + * + * @since 9 + */ +@MetadataDefinition +@ContentType +@Label("Data Amount") +@Description("Amount of data") +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE, ElementType.METHOD}) +public @interface DataAmount { + /** + * Unit for bits + */ + public static final String BITS = "BITS"; + /** + * Unit for bytes + */ + public static final String BYTES = "BYTES"; + + /** + * Returns the unit for the data amount, by default bytes. + * + * @return the data amount unit, default {@code BYTES}, not {@code null} + */ + String value() default BYTES; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Description.java 2019-02-08 18:32:19.746735056 +0300 @@ -0,0 +1,52 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation that describes an element by using a sentence or two. + *

+ * Use sentence-style capitalization, capitalize the first letter of the first + * word, and any proper names such as the word Java. If the description is one + * sentence, a period should not be included. + * + * @since 9 + */ +@MetadataDefinition +@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface Description { + /** + * Returns a sentence or two that describes the annotated element. + * + * @return a description, not {@code null} + */ + String value(); +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Enabled.java 2019-02-08 18:32:19.890730014 +0300 @@ -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; + +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; + +/** + * Event annotation, determines if an event should be enabled by default. + *

+ * If an event doesn't have the annotation, then by default the event is enabled. + * + * @since 9 + */ +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@MetadataDefinition +public @interface Enabled { + /** + * Setting name {@code "enabled"}, signifies that the event should be + * recorded. + */ + public final static String NAME = "enabled"; + + /** + * Returns {@code true} if by default the event should be enabled, {@code false} otherwise. + * + * @return {@code true} if by default the event should be enabled by default, {@code false} otherwise + */ + boolean value() default true; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Event.java 2019-02-08 18:32:20.034724971 +0300 @@ -0,0 +1,171 @@ +/* + * 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; + +/** + * Base class for events, to be subclassed in order to define events and their + * fields. + *

+ * The following example shows how to implement an {@code Event} class. + * + *

+ * 
+ * import jdk.jfr.Event;
+ * import jdk.jfr.Description;
+ * import jdk.jfr.Label;
+ *
+ * public class Example {
+ *
+ *   @Label("Hello World")
+ *   @Description("Helps programmer getting started")
+ *   static class HelloWorld extends Event {
+ *       @Label("Message")
+ *       String message;
+ *   }
+ *
+ *   public static void main(String... args) {
+ *       HelloWorld event = new HelloWorld();
+ *       event.message = "hello, world!";
+ *       event.commit();
+ *   }
+ * }
+ * 
+ * 
+ *

+ * After an event is allocated and its field members are populated, it can be + * written to the Flight Recorder system by using the {@code #commit()} method. + *

+ * By default, an event is enabled. To disable an event annotate the + * {@link Event} class with {@code @Enabled(false)}. + *

+ * Supported field types are the Java primitives: {@code boolean}, {@code char}, + * {@code byte}, {@code short}, {@code int}, {@code long}, {@code float}, and + * {@code double}. Supported reference types are: {@code String}, {@code Thread} + * and {@code Class}. Arrays, enums, and other reference types are silently + * ignored and not included. Fields that are of the supported types can be + * excluded by using the transient modifier. Static fields, even of the + * supported types, are not included. + *

+ * Tools can visualize data in a meaningful way when annotations are used (for + * example, {@code Label}, {@code Description}, and {@code Timespan}). + * Annotations applied to an {@link Event} class or its fields are included if + * they are present (indirectly, directly, or associated), have the + * {@code MetadataDefinition} annotation, and they do not contain enums, arrays, + * or classes. + *

+ * Gathering data to store in an event can be expensive. The + * {@link Event#shouldCommit()} method can be used to verify whether an event + * instance would actually be written to the system when the + * {@code Event#commit()commit} method is invoked. If + * {@link Event#shouldCommit()} returns false, then those operations can be + * avoided. + * + * @since 9 + */ +@Enabled(true) +@StackTrace(true) +@Registered(true) +abstract public class Event { + /** + * Sole constructor, for invocation by subclass constructors, typically + * implicit. + */ + protected Event() { + } + + /** + * Starts the timing of this event. + */ + final public void begin() { + } + + /** + * Ends the timing of this event. + * + * The {@code end} method must be invoked after the {@code begin} method. + */ + final public void end() { + } + + /** + * Writes the field values, time stamp, and event duration to the Flight + * Recorder system. + *

+ * If the event starts with an invocation of the {@code begin} method, but does + * not end with an explicit invocation of the {@code end} method, then the event + * ends when the {@code commit} method is invoked. + */ + final public void commit() { + } + + /** + * Returns {@code true} if at least one recording is running, and the + * enabled setting for this event is set to {@code true}, otherwise + * {@code false} is returned. + * + * @return {@code true} if event is enabled, {@code false} otherwise + */ + final public boolean isEnabled() { + return false; + } + + /** + * Returns {@code true} if the enabled setting for this event is set to + * {@code true} and if the duration is within the threshold for the event, + * {@code false} otherwise. The threshold is the minimum threshold for all + * running recordings. + * + * @return {@code true} if the event can be written to the Flight Recorder + * system, {@code false} otherwise + */ + final public boolean shouldCommit() { + return false; + } + + /** + * Sets a field value. + *

+ * Applicable only if the event is dynamically defined using the + * {@code EventFactory} class. + *

+ * The supplied {@code index} corresponds to the index of the + * {@link ValueDescriptor} object passed to the factory method of the + * {@code EventFactory} class. + * + * @param index the index of the field that is passed to + * {@code EventFactory#create(String, java.util.List, java.util.List)} + * @param value value to set, can be {@code null} + * @throws UnsupportedOperationException if it's not a dynamically generated + * event + * @throws IndexOutOfBoundsException if {@code index} is less than {@code 0} or + * greater than or equal to the number of fields specified for the event + * + * @see EventType#getFields() + * @see EventFactory + */ + final public void set(int index, Object value) { + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/EventFactory.java 2019-02-08 18:32:20.178719928 +0300 @@ -0,0 +1,251 @@ +/* + * 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; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import jdk.jfr.internal.EventClassBuilder; +import jdk.jfr.internal.JVMSupport; +import jdk.jfr.internal.MetadataRepository; +import jdk.jfr.internal.Type; +import jdk.jfr.internal.Utils; + +/** + * Class for defining an event at runtime. + *

+ * It's highly recommended that the event is defined at compile time, if the + * field layout is known, so the Java Virtual Machine (JVM) can optimize the + * code, possibly remove all instrumentation if Flight Recorder is inactive or + * if the enabled setting for this event is set to {@code false}. + *

+ * To define an event at compile time, see {@link Event}. + *

+ * The following example shows how to implement a dynamic {@code Event} class. + * + *

+ * {@code
+ * List fields = new ArrayList<>();
+ * List messageAnnotations = Collections.singletonList(new AnnotationElement(Label.class, "Message"));
+ * fields.add(new ValueDescriptor(String.class, "message", messageAnnotations));
+ * List numberAnnotations = Collections.singletonList(new AnnotationElement(Label.class, "Number"));
+ * fields.add(new ValueDescriptor(int.class, "number", numberAnnotations));
+ *
+ * String[] category = { "Example", "Getting Started" };
+ * List eventAnnotations = new ArrayList<>();
+ * eventAnnotations.add(new AnnotationElement(Name.class, "com.example.HelloWorld"));
+ * eventAnnotations.add(new AnnotationElement(Label.class, "Hello World"));
+ * eventAnnotations.add(new AnnotationElement(Description.class, "Helps programmer getting started"));
+ * eventAnnotations.add(new AnnotationElement(Category.class, category));
+ *
+ * EventFactory f = EventFactory.create(eventAnnotations, fields);
+ *
+ * Event event = f.newEvent();
+ * event.set(0, "hello, world!");
+ * event.set(1, 4711);
+ * event.commit();
+ * }
+ * 
+ * + * @since 9 + */ +public final class EventFactory { + + private static final long REGISTERED_ID = Type.getTypeId(Registered.class); + + private final Class eventClass; + private final MethodHandle constructorHandle; + private final List sanitizedAnnotation; + private final List sanitizedFields; + + private EventFactory(Class eventClass, List sanitizedAnnotation, List sanitizedFields) throws IllegalAccessException, NoSuchMethodException, SecurityException { + this.constructorHandle = MethodHandles.lookup().unreflectConstructor(eventClass.getConstructor()); + this.eventClass = eventClass; + this.sanitizedAnnotation = sanitizedAnnotation; + this.sanitizedFields = sanitizedFields; + } + + /** + * Creates an {@code EventFactory} object. + *

+ * The order of the value descriptors specifies the index to use when setting + * event values. + * + * @param annotationElements list of annotation elements that describes the + * annotations on the event, not {@code null} + * + * @param fields list of descriptors that describes the fields of the event, not + * {@code null} + * + * @return event factory, not {@code null} + * + * @throws IllegalArgumentException if the input is not valid. For example, + * input might not be valid if the field type or name is not valid in + * the Java language or an annotation element references a type that + * can't be found. + * + * @throws SecurityException if a security manager exists and the caller does + * not have {@code FlightRecorderPermission("registerEvent")} + * + * @see Event#set(int, Object) + */ + public static EventFactory create(List annotationElements, List fields) { + Objects.requireNonNull(fields); + Objects.requireNonNull(annotationElements); + JVMSupport.ensureWithInternalError(); + + Utils.checkRegisterPermission(); + + List sanitizedAnnotation = Utils.sanitizeNullFreeList(annotationElements, AnnotationElement.class); + List sanitizedFields = Utils.sanitizeNullFreeList(fields, ValueDescriptor.class); + Set nameSet = new HashSet<>(); + for (ValueDescriptor v : sanitizedFields) { + String name = v.getName(); + if (v.isArray()) { + throw new IllegalArgumentException("Array types are not allowed for fields"); + } + if (!Type.isValidJavaFieldType(v.getTypeName())) { + throw new IllegalArgumentException(v.getTypeName() + " is not a valid type for an event field"); + } + if (!Type.isValidJavaIdentifier(v.getName())) { + throw new IllegalArgumentException(name + " is not a valid name for an event field"); + } + if (nameSet.contains(name)) { + throw new IllegalArgumentException("Name of fields must be unique. Found two instances of " + name); + } + nameSet.add(name); + } + + // Prevent event from being registered in + // and only use annotations that can be resolved (those in boot class loader) + boolean needRegister = true; + List bootAnnotations = new ArrayList<>(); + for (AnnotationElement ae : sanitizedAnnotation) { + long id = ae.getTypeId(); + if (ae.isInBoot()) { + if (id == REGISTERED_ID) { + if (Boolean.FALSE.equals(ae.getValue("value"))) { + needRegister = false; + } + } else { + bootAnnotations.add(ae); + } + } + } + bootAnnotations.add(new AnnotationElement(Registered.class, false)); + + EventClassBuilder ecb = new EventClassBuilder(bootAnnotations, sanitizedFields); + Class eventClass = ecb.build(); + + if (needRegister) { + MetadataRepository.getInstance().register(eventClass, sanitizedAnnotation, sanitizedFields); + } + try { + return new EventFactory(eventClass, sanitizedAnnotation, sanitizedFields); + } catch (IllegalAccessException e) { + throw new IllegalAccessError("Could not accees constructor of generated event handler, " + e.getMessage()); + } catch (NoSuchMethodException e) { + throw new InternalError("Could not find constructor in generated event handler, " + e.getMessage()); + } + } + + /** + * Instantiates an event, so it can be populated with data and written to the + * Flight Recorder system. + *

+ * Use the {@link Event#set(int, Object)} method to set a value. + * + * @return an event instance, not {@code null} + */ + public Event newEvent() { + try { + return (Event) constructorHandle.invoke(); + } catch (Throwable e) { + throw new InstantiationError("Could not instantaite dynamically generated event class " + eventClass.getName() + ". " + e.getMessage()); + } + } + + /** + * Returns the event type that is associated with this event factory. + * + * @return event type that is associated with this event factory, not + * {@code null} + * + * @throws java.lang.IllegalStateException if the event factory is created with + * the {@code Registered(false)} annotation and the event class is not + * manually registered before the invocation of this method + */ + public EventType getEventType() { + return EventType.getEventType(eventClass); + } + + /** + * Registers an unregistered event. + *

+ * By default, the event class associated with this event factory is registered + * when the event factory is created, unless the event has the + * {@link Registered} annotation. + *

+ * A registered event class can write data to Flight Recorder and event metadata + * can be obtained by invoking {@link FlightRecorder#getEventTypes()}. + *

+ * If the event class associated with this event factory is already registered, + * the call to this method is ignored. + * + * @throws SecurityException if a security manager exists and the caller + * does not have {@code FlightRecorderPermission("registerEvent")} + * @see Registered + * @see FlightRecorder#register(Class) + */ + public void register() { + MetadataRepository.getInstance().register(eventClass, sanitizedAnnotation, sanitizedFields); + } + + /** + * Unregisters the event that is associated with this event factory. + *

+ * A unregistered event class can't write data to Flight Recorder and event + * metadata can't be obtained by invoking + * {@link FlightRecorder#getEventTypes()}. + *

+ * If the event class associated with this event factory is not already + * registered, the call to this method is ignored. + * + * @throws SecurityException if a security manager exists and the caller does + * not have {@code FlightRecorderPermission("registerEvent")} + * @see Registered + * @see FlightRecorder#unregister(Class) + */ + public void unregister() { + MetadataRepository.getInstance().unregister(eventClass); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/EventSettings.java 2019-02-08 18:32:20.322714886 +0300 @@ -0,0 +1,141 @@ +/* + * 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; + +import java.time.Duration; +import java.util.Map; + +/** + * Convenience class for applying event settings to a recording. + *

+ * An {@code EventSettings} object for a recording can be obtained by invoking + * the {@link Recording#enable(String)} method which is configured using method + * chaining. + *

+ * The following example shows how to use the {@code EventSettings} class. + *

+ * {@code
+ * Recording r = new Recording();
+ * r.enable("jdk.CPULoad")
+ *    .withPeriod(Duration.ofSeconds(1));
+ * r.enable("jdk.FileWrite")
+ *    .withoutStackTrace()
+ *    .withThreshold(Duration.ofNanos(10));
+ * r.start();
+ * Thread.sleep(10_000);
+ * r.stop();
+ * r.dump(Files.createTempFile("recording", ".jfr"));
+ *
+ * }
+ * 
+ * @since 9 + */ +public abstract class EventSettings { + + // package private + EventSettings() { + } + + /** + * Enables stack traces for the event that is associated with this event setting. + *

+ * Equivalent to invoking the {@code with("stackTrace", "true")} method. + * + * @return event settings object for further configuration, not {@code null} + */ + final public EventSettings withStackTrace() { + return with(StackTrace.NAME, "true"); + } + + /** + * Disables stack traces for the event that is associated with this event setting. + *

+ * Equivalent to invoking the {@code with("stackTrace", "false")} method. + * + * @return event settings object for further configuration, not {@code null} + */ + final public EventSettings withoutStackTrace() { + return with(StackTrace.NAME, "false"); + } + + /** + * Specifies that a threshold is not used. + *

+ * This is a convenience method, equivalent to invoking the + * {@code with("threshold", "0 s")} method. + * + * @return event settings object for further configuration, not {@code null} + */ + final public EventSettings withoutThreshold() { + return with(Threshold.NAME, "0 s"); + } + + /** + * Sets the interval for the event that is associated with this event setting. + * + * @param duration the duration, not {@code null} + * + * @return event settings object for further configuration, not {@code null} + */ + final public EventSettings withPeriod(Duration duration) { + return with(Period.NAME, duration.toNanos() + " ns"); + } + + /** + * Sets the threshold for the event that is associated with this event setting. + * + * @param duration the duration, or {@code null} if no duration is used + * + * @return event settings object for further configuration, not {@code null} + */ + final public EventSettings withThreshold(Duration duration) { + if (duration == null) { + return with(Threshold.NAME, "0 ns"); + } else { + return with(Threshold.NAME, duration.toNanos() + " ns"); + } + } + + /** + * Sets a setting value for the event that is associated with this event setting. + * + * @param name the name of the setting (for example, {@code "threshold"}) + * + * @param value the value to set (for example {@code "20 ms"} not + * {@code null}) + * + * @return event settings object for further configuration, not {@code null} + */ + abstract public EventSettings with(String name, String value); + + /** + * Creates a settings {@code Map} for the event that is associated with this + * event setting. + * + * @return a settings {@code Map}, not {@code null} + */ + abstract Map toMap(); +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/EventType.java 2019-02-08 18:32:20.466709843 +0300 @@ -0,0 +1,237 @@ +/* + * 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; + +import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import jdk.jfr.internal.JVMSupport; +import jdk.jfr.internal.MetadataRepository; +import jdk.jfr.internal.PlatformEventType; +import jdk.jfr.internal.Type; +import jdk.jfr.internal.Utils; + +/** + * Describes an event, its fields, settings and annotations. + * + * @since 9 + */ +public final class EventType { + private final PlatformEventType platformEventType; + private final List UNCATEGORIZED = Collections.singletonList("Uncategorized"); + private Map cache; // create lazy to avoid memory overhead + // helper constructor + EventType(PlatformEventType platformEventType) { + this.platformEventType = platformEventType; + } + + /** + * Returns an immutable list of descriptors that describe the event fields of + * this event type. + * + * @return the list of field descriptors, not {@code null} + */ + public List getFields() { + return platformEventType.getFields(); + } + + /** + * Returns the field with the specified name, or {@code null} if it doesn't + * exist. + * + * @return a value descriptor that describes the field, or null if + * the field with the specified name doesn't exist + * + * @return a value descriptor, or null if it doesn't exist + */ + public ValueDescriptor getField(String name) { + Objects.requireNonNull(name); + if (cache == null) { + List fields = getFields(); + Map newCache = new LinkedHashMap(fields.size()); + for (ValueDescriptor v :fields) { + newCache.put(v.getName(), v); + } + cache = newCache; + } + return cache.get(name); + } + + /** + * Returns an identifier for the event (for example, + * {@code "jdk.CPULoad"}). + *

+ * The identifier is the fully qualified name of the event class, if not set using + * the {@link Name} annotation. + * + * @return the name, not {@code null} + * + * @see Name + */ + public String getName() { + return platformEventType.getName(); + } + + /** + * Returns a human-readable name (for example, {@code "CPU Load"}). + *

+ * The label of an event class can be set with {@link Label}. + * + * @return the label, or {@code null} if a label is not set + * + * @see Label + */ + public String getLabel() { + return platformEventType.getLabel(); + } + + /** + * Returns a unique ID for this event type in the Java Virtual Machine (JVM). + * + * @return the ID that is used in the JVM + */ + public long getId() { + return platformEventType.getId(); + } + + /** + * Returns an immutable list of annotation elements for this event type. + * + * @return an immutable list of annotations or an empty list if no + * annotations exists, not {@code null} + */ + public List getAnnotationElements() { + return platformEventType.getAnnotationElements(); + } + + /** + * Returns {@code true} if the event is enabled and at least one recording is + * running, {@code false} otherwise. + *

+ * By default, the event is enabled. The event can be enabled or disabled by + * setting the enabled setting to {@code true} or {@code false}, programmatically or by using a + * configuration file. The event can also be disabled by annotating event with + * the {@code @Enabled(false)} annotation. + * + * @return true if event is enabled, false otherwise + * + * @see Enabled + * @see Recording#enable(Class) + */ + public boolean isEnabled() { + return platformEventType.isEnabled(); + } + + /** + * Returns a short sentence that describes the event class. + *

+ * The description of an event class can be set with {@link Description}. + * + * @return the description, or {@code null} if no description exists + * + * @see Description + */ + public String getDescription() { + return platformEventType.getDescription(); + } + + /** + * Returns the first annotation for the specified type if an annotation + * element with the same name is directly present, otherwise {@code null}. + * + * @param the type of the annotation to query for and return if present + * @param annotationClass the {@code Class} object that corresponds to the + * annotation type, not {@code null} + * @return this element's annotation for the specified annotation type if + * directly present, else {@code null} + */ + public A getAnnotation(Class annotationClass) { + Objects.requireNonNull(annotationClass); + return platformEventType.getAnnotation(annotationClass); + } + + /** + * Returns the event type for an event class, or {@code null} if it doesn't + * exist. + * + * @param eventClass the event class, not {@code null} + * @return the event class, or null if class doesn't exist + * + * @throws IllegalArgumentException if {@code eventClass} is an abstract class + * + * @throws IllegalStateException if the class is annotated with + * {@code Registered(false)}, but not manually registered + */ + public static EventType getEventType(Class eventClass) { + Objects.requireNonNull(eventClass); + Utils.ensureValidEventSubclass(eventClass); + JVMSupport.ensureWithInternalError(); + return MetadataRepository.getInstance().getEventType(eventClass); + } + + /** + * Returns an immutable list of the setting descriptors that describe the available + * event settings for this event type. + * + * @return the list of setting descriptors for this event type, not + * {@code null} + */ + public List getSettingDescriptors() { + return Collections.unmodifiableList(platformEventType.getSettings()); + } + + /** + * Returns the list of human-readable names that makes up the categories for + * this event type (for example, {@code "Java Application"}, {@code "Statistics"}). + * + * @return an immutable list of category names, or a list with the name + * {@code "Uncategorized"} if no category is set + * + * @see Category + */ + public List getCategoryNames() { + Category c = platformEventType.getAnnotation(Category.class); + if (c == null) { + return UNCATEGORIZED; + } + return Collections.unmodifiableList(Arrays.asList(c.value())); + } + + // package private + Type getType() { + return platformEventType; + } + + // package private + PlatformEventType getPlatformEventType() { + return platformEventType; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Experimental.java 2019-02-08 18:32:20.610704800 +0300 @@ -0,0 +1,59 @@ +/* + * 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; + +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; + +/** + * Annotation that specifies that an element is experimental and may change + * without notice. + *

+ * Clients that visualize Flight Recorder events should not show the + * events or fields annotated with the {@code Experimental} annotation by + * default. This annotation allows event producers the freedom to try out new + * events without committing to them. + *

+ * Clients may provide a check box (for example, in a preference page) where a + * user can opt-in to display experimental data. If the user decide to do so, + * the user interface should mark experimental events or fields so users can + * distinguish them from non-experimental events. + *

+ * This annotation is inherited. + * + * @since 9 + */ +@MetadataDefinition +@Label("Experimental") +@Description("Element is not to be shown to a user by default") +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE }) +public @interface Experimental { +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/FlightRecorder.java 2019-02-08 18:32:20.758699618 +0300 @@ -0,0 +1,353 @@ +/* + * 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; + +import static jdk.jfr.internal.LogLevel.DEBUG; +import static jdk.jfr.internal.LogLevel.INFO; +import static jdk.jfr.internal.LogTag.JFR; + +import java.security.AccessControlContext; +import java.security.AccessController; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import jdk.jfr.internal.JVM; +import jdk.jfr.internal.JVMSupport; +import jdk.jfr.internal.LogLevel; +import jdk.jfr.internal.Logger; +import jdk.jfr.internal.MetadataRepository; +import jdk.jfr.internal.Options; +import jdk.jfr.internal.PlatformRecorder; +import jdk.jfr.internal.PlatformRecording; +import jdk.jfr.internal.Repository; +import jdk.jfr.internal.RequestEngine; +import jdk.jfr.internal.Utils; + +/** + * Class for accessing, controlling, and managing Flight Recorder. + *

+ * This class provides the methods necessary for creating, starting, stopping, + * and destroying recordings. + * + * @since 9 + */ +public final class FlightRecorder { + private static volatile FlightRecorder platformRecorder; + private static volatile boolean initialized; + private final PlatformRecorder internal; + + private FlightRecorder(PlatformRecorder internal) { + this.internal = internal; + } + + /** + * Returns an immutable list of the available recordings. + *

+ * A recording becomes available when it is created. It becomes unavailable when it + * is in the {@code CLOSED} state, typically after a call to + * {@link Recording#close()}. + * + * @return a list of recordings, not {@code null} + */ + public List getRecordings() { + List recs = new ArrayList<>(); + for (PlatformRecording r : internal.getRecordings()) { + recs.add(r.getRecording()); + } + return Collections.unmodifiableList(recs); + } + + /** + * Creates a snapshot of all available recorded data. + *

+ * A snapshot is a synthesized recording in a {@code STOPPPED} state. If no data is + * available, a recording with size {@code 0} is returned. + *

+ * A snapshot provides stable access to data for later operations (for example, + * operations to change the interval or to reduce the data size). + *

+ * The following example shows how to create a snapshot and write a subset of the data to a file. + * + *

+     * 
+     * try (Recording snapshot = FlightRecorder.getFlightRecorder().takeSnapshot()) {
+     *   if (snapshot.getSize() > 0) {
+     *     snapshot.setMaxSize(100_000_000);
+     *     snapshot.setMaxAge(Duration.ofMinutes(5));
+     *     snapshot.dump(Paths.get("snapshot.jfr"));
+     *   }
+     * }
+     * 
+     * 
+ * + * The caller must close the recording when access to the data is no longer + * needed. + * + * @return a snapshot of all available recording data, not {@code null} + */ + public Recording takeSnapshot() { + Recording snapshot = new Recording(); + snapshot.setName("Snapshot"); + internal.fillWithRecordedData(snapshot.getInternal(), null); + return snapshot; + } + + /** + * Registers an event class. + *

+ * If the event class is already registered, then the invocation of this method is + * ignored. + * + * @param eventClass the event class to register, not {@code null} + * + * @throws IllegalArgumentException if class is abstract or not a subclass + * of {@link Event} + * @throws SecurityException if a security manager exists and the caller + * does not have {@code FlightRecorderPermission("registerEvent")} + */ + public static void register(Class eventClass) { + Objects.requireNonNull(eventClass); + if (JVMSupport.isNotAvailable()) { + return; + } + Utils.ensureValidEventSubclass(eventClass); + MetadataRepository.getInstance().register(eventClass); + } + + /** + * Unregisters an event class. + *

+ * If the event class is not registered, then the invocation of this method is + * ignored. + * + * @param eventClass the event class to unregistered, not {@code null} + * @throws IllegalArgumentException if a class is abstract or not a subclass + * of {@link Event} + * + * @throws SecurityException if a security manager exists and the caller + * does not have {@code FlightRecorderPermission("registerEvent")} + */ + public static void unregister(Class eventClass) { + Objects.requireNonNull(eventClass); + if (JVMSupport.isNotAvailable()) { + return; + } + Utils.ensureValidEventSubclass(eventClass); + MetadataRepository.getInstance().unregister(eventClass); + } + + /** + * Returns the Flight Recorder for the platform. + * + * @return a Flight Recorder instance, not {@code null} + * + * @throws IllegalStateException if Flight Recorder can't be created (for + * example, if the Java Virtual Machine (JVM) lacks Flight Recorder + * support, or if the file repository can't be created or accessed) + * + * @throws SecurityException if a security manager exists and the caller does + * not have {@code FlightRecorderPermission("accessFlightRecorder")} + */ + public static FlightRecorder getFlightRecorder() throws IllegalStateException, SecurityException { + synchronized (PlatformRecorder.class) { + Utils.checkAccessFlightRecorder(); + JVMSupport.ensureWithIllegalStateException(); + if (platformRecorder == null) { + try { + platformRecorder = new FlightRecorder(new PlatformRecorder()); + } catch (IllegalStateException ise) { + throw ise; + } catch (Exception e) { + throw new IllegalStateException("Can't create Flight Recorder. " + e.getMessage(), e); + } + // Must be in synchronized block to prevent instance leaking out + // before initialization is done + initialized = true; + Logger.log(JFR, INFO, "Flight Recorder initialized"); + Logger.log(JFR, DEBUG, "maxchunksize: " + Options.getMaxChunkSize()+ " bytes"); + Logger.log(JFR, DEBUG, "memorysize: " + Options.getMemorySize()+ " bytes"); + Logger.log(JFR, DEBUG, "globalbuffersize: " + Options.getGlobalBufferSize()+ " bytes"); + Logger.log(JFR, DEBUG, "globalbuffercount: " + Options.getGlobalBufferCount()); + Logger.log(JFR, DEBUG, "dumppath: " + Options.getDumpPath()); + Logger.log(JFR, DEBUG, "samplethreads: " + Options.getSampleThreads()); + Logger.log(JFR, DEBUG, "stackdepth: " + Options.getStackDepth()); + Logger.log(JFR, DEBUG, "threadbuffersize: " + Options.getThreadBufferSize()); + Logger.log(JFR, LogLevel.INFO, "Created repository " + Repository.getRepository().getRepositoryPath().toString()); + PlatformRecorder.notifyRecorderInitialized(platformRecorder); + } + } + return platformRecorder; + } + + /** + * Adds a hook for a periodic event. + *

+ * The implementation of the hook should return as soon as possible, to + * avoid blocking other Flight Recorder operations. The hook should emit + * one or more events of the specified type. When a hook is added, the + * interval at which the call is invoked is configurable using the + * {@code "period"} setting. + * + * @param eventClass the class that the hook should run for, not {@code null} + * @param hook the hook, not {@code null} + * @throws IllegalArgumentException if a class is not a subclass of + * {@link Event}, is abstract, or the hook is already added + * @throws IllegalStateException if the event class has the + * {@code Registered(false)} annotation and is not registered manually + * @throws SecurityException if a security manager exists and the caller + * does not have {@code FlightRecorderPermission("registerEvent")} + */ + public static void addPeriodicEvent(Class eventClass, Runnable hook) throws SecurityException { + Objects.requireNonNull(eventClass); + Objects.requireNonNull(hook); + if (JVMSupport.isNotAvailable()) { + return; + } + + Utils.ensureValidEventSubclass(eventClass); + Utils.checkRegisterPermission(); + AccessControlContext acc = AccessController.getContext(); + RequestEngine.addHook(acc, EventType.getEventType(eventClass).getPlatformEventType(), hook); + } + + /** + * Removes a hook for a periodic event. + * + * @param hook the hook to remove, not {@code null} + * @return {@code true} if hook is removed, {@code false} otherwise + * @throws SecurityException if a security manager exists and the caller + * does not have {@code FlightRecorderPermission("registerEvent")} + */ + public static boolean removePeriodicEvent(Runnable hook) throws SecurityException { + Objects.requireNonNull(hook); + Utils.checkRegisterPermission(); + if (JVMSupport.isNotAvailable()) { + return false; + } + return RequestEngine.removeHook(hook); + } + + /** + * Returns an immutable list that contains all currently registered events. + *

+ * By default, events are registered when they are first used, typically + * when an event object is allocated. To ensure an event is visible early, + * registration can be triggered by invoking the + * {@link FlightRecorder#register(Class)} method. + * + * @return list of events, not {@code null} + */ + public List getEventTypes() { + return Collections.unmodifiableList(MetadataRepository.getInstance().getRegisteredEventTypes()); + } + + /** + * Adds a recorder listener and captures the {@code AccessControlContext} to + * use when invoking the listener. + *

+ * If Flight Recorder is already initialized when the listener is added, then the method + * {@link FlightRecorderListener#recorderInitialized(FlightRecorder)} method is + * invoked before returning from this method. + * + * @param changeListener the listener to add, not {@code null} + * + * @throws SecurityException if a security manager exists and the caller + * does not have + * {@code FlightRecorderPermission("accessFlightRecorder")} + */ + public static void addListener(FlightRecorderListener changeListener) { + Objects.requireNonNull(changeListener); + Utils.checkAccessFlightRecorder(); + if (JVMSupport.isNotAvailable()) { + return; + } + PlatformRecorder.addListener(changeListener); + } + + /** + * Removes a recorder listener. + *

+ * If the same listener is added multiple times, only one instance is + * removed. + * + * @param changeListener listener to remove, not {@code null} + * + * @throws SecurityException if a security manager exists and the caller + * does not have + * {@code FlightRecorderPermission("accessFlightRecorder")} + * + * @return {@code true}, if the listener could be removed, {@code false} + * otherwise + */ + public static boolean removeListener(FlightRecorderListener changeListener) { + Objects.requireNonNull(changeListener); + Utils.checkAccessFlightRecorder(); + if (JVMSupport.isNotAvailable()) { + return false; + } + + return PlatformRecorder.removeListener(changeListener); + } + + /** + * Returns {@code true} if the Java Virtual Machine (JVM) has Flight Recorder capabilities. + *

+ * This method can quickly check whether Flight Recorder can be + * initialized, without actually doing the initialization work. The value may + * change during runtime and it is not safe to cache it. + * + * @return {@code true}, if Flight Recorder is available, {@code false} + * otherwise + * + * @see FlightRecorderListener for callback when Flight Recorder is + * initialized + */ + public static boolean isAvailable() { + if (JVMSupport.isNotAvailable()) { + return false; + } + return JVM.getJVM().isAvailable(); + } + + /** + * Returns {@code true} if Flight Recorder is initialized. + * + * @return {@code true}, if Flight Recorder is initialized, + * {@code false} otherwise + * + * @see FlightRecorderListener for callback when Flight Recorder is + * initialized + */ + public static boolean isInitialized() { + return initialized; + } + + PlatformRecorder getInternal() { + return internal; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/FlightRecorderListener.java 2019-02-08 18:32:20.906694435 +0300 @@ -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; + + +/** + * Callback interface to monitor Flight Recorder's life cycle. + * + * @since 9 + */ +public interface FlightRecorderListener { + + /** + * Receives notification when Flight Recorder is initialized. + *

+ * This method is also be invoked when a listener is added to an already + * initialized Flight Recorder. + *

+ * This method allows clients to implement their own initialization mechanism + * that is executed before a {@code FlightRecorder} instance is returned by + * {@code FlightRecorder#getFlightRecorder()}. + * + * @implNote This method should return as soon as possible, to avoid blocking + * initialization of Flight Recorder. To avoid deadlocks or unexpected + * behavior, this method should not call + * {@link FlightRecorder#getFlightRecorder()} or start new recordings. + * + * @implSpec The default implementation of this method is empty. + * + * @param recorder Flight Recorder instance, not {@code null} + * + * @see FlightRecorder#addListener(FlightRecorderListener) + */ + default void recorderInitialized(FlightRecorder recorder) { + } + + /** + * Receives notification when the state of a recording changes. + *

+ * Callback is invoked when a recording reaches the {@code RUNNING}, + * {@code STOPPED} and {@code CLOSED} state. + * + * @implNote The implementation of this method should return as soon as possible + * to avoid blocking normal operation of Flight Recorder. + * + * @implSpec The default implementation of this method is empty. + * + * @param recording the recording where the state change occurred, not + * {@code null} + * + * @see FlightRecorder#addListener(FlightRecorderListener) + * @see RecordingState + * + */ + default void recordingStateChanged(Recording recording) { + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/FlightRecorderPermission.java 2019-02-08 18:32:21.046689533 +0300 @@ -0,0 +1,211 @@ +/* + * 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; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import jdk.jfr.internal.PlatformEventType; +import jdk.jfr.internal.PlatformRecorder; +import jdk.jfr.internal.PlatformRecording; +import jdk.jfr.internal.PrivateAccess; +import jdk.jfr.internal.Type; +import jdk.jfr.internal.Utils; + +/** + * Permission for controlling access to Flight Recorder. + *

+ * The following table provides a summary of what the permission + * allows, and the risks of granting code the permission. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Table shows permission target name, + * what the permission allows, and associated risks
Permission Target NameWhat the Permission AllowsRisks of Allowing this Permission
{@code accessFlightRecorder}Ability to create a Flight Recorder instance, register callbacks to + * monitor the Flight Recorder life cycle, and control an existing instance + * of Flight Recorder, which can record and dump runtime information, such as + * stack traces, class names, and data in user defined events.A malicious user may be able to extract sensitive information that is stored in + * events and interrupt Flight Recorder by installing listeners or hooks that + * never finish.
{@code registerEvent}Ability to register events, write data to the Flight Recorder buffers, + * and execute code in a callback function for periodic events. + * + * A malicious user may be able to write sensitive information to Flight + * Recorder buffers.
+ * + *

+ * Typically, programmers do not create {@code FlightRecorderPermission} objects + * directly. Instead the objects are created by the security policy code that is based on + * reading the security policy file. + * + * @since 9 + * + * @see java.security.BasicPermission + * @see java.security.Permission + * @see java.security.Permissions + * @see java.security.PermissionCollection + * @see java.lang.SecurityManager + * + */ +@SuppressWarnings("serial") +public final class FlightRecorderPermission extends java.security.BasicPermission { + + // Purpose of InternalAccess is to give classes in jdk.jfr.internal + // access to package private methods in this package (jdk.jfr). + // + // The initialization could be done in any class in this package, + // but this one was chosen because it is light weight and + // lacks dependencies on other public classes. + static { + PrivateAccess.setPrivateAccess(new InternalAccess()); + } + + private final static class InternalAccess extends PrivateAccess { + + @Override + public Type getType(Object o) { + if (o instanceof AnnotationElement) { + return ((AnnotationElement) o).getType(); + } + if (o instanceof EventType) { + return ((EventType) o).getType(); + } + if (o instanceof ValueDescriptor) { + return ((ValueDescriptor) o).getType(); + } + if (o instanceof SettingDescriptor) { + return ((SettingDescriptor) o).getType(); + } + throw new Error("Unknown type " + o.getClass()); + } + + @Override + public Configuration newConfiguration(String name, String label, String description, String provider, Map settings, String contents) { + return new Configuration(name, label, description, provider, settings, contents); + } + + @Override + public EventType newEventType(PlatformEventType platformEventType) { + return new EventType(platformEventType); + } + + @Override + public AnnotationElement newAnnotation(Type annotationType, List values, boolean boot) { + return new AnnotationElement(annotationType, values, boot); + } + + @Override + public ValueDescriptor newValueDescriptor(String name, Type fieldType, List annos, int dimension, boolean constantPool, String fieldName) { + return new ValueDescriptor(fieldType, name, annos, dimension, constantPool, fieldName); + } + + @Override + public PlatformRecording getPlatformRecording(Recording r) { + return r.getInternal(); + } + + @Override + public PlatformEventType getPlatformEventType(EventType eventType) { + return eventType.getPlatformEventType(); + } + + @Override + public boolean isConstantPool(ValueDescriptor v) { + return v.isConstantPool(); + } + + @Override + public void setAnnotations(ValueDescriptor v, List a) { + v.setAnnotations(a); + } + + @Override + public void setAnnotations(SettingDescriptor s, List a) { + s.setAnnotations(a); + } + + @Override + public String getFieldName(ValueDescriptor v) { + return v.getJavaFieldName(); + } + + @Override + public ValueDescriptor newValueDescriptor(Class type, String name) { + return new ValueDescriptor(type, name, Collections.emptyList(), true); + } + + @Override + public SettingDescriptor newSettingDescriptor(Type type, String name, String defaultValue, List annotations) { + return new SettingDescriptor(type, name, defaultValue, annotations); + } + + @Override + public boolean isUnsigned(ValueDescriptor v) { + return v.isUnsigned(); + } + + @Override + public PlatformRecorder getPlatformRecorder() { + return FlightRecorder.getFlightRecorder().getInternal(); + } + } + + /** + * Constructs a {@code FlightRecorderPermission} with the specified name. + * + * @param name the permission name, must be either + * {@code "accessFlightRecorder"} or {@code "registerEvent"}, not + * {@code null} + * + * @throws IllegalArgumentException if {@code name} is empty or not valid + */ + public FlightRecorderPermission(String name) { + super(Objects.requireNonNull(name)); + if (!name.equals(Utils.ACCESS_FLIGHT_RECORDER) && !name.equals(Utils.REGISTER_EVENT)) { + throw new IllegalArgumentException("name: " + name); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Frequency.java 2019-02-08 18:32:21.190684491 +0300 @@ -0,0 +1,45 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Event field annotation, specifies that the value is a frequency, measured in Hz. + * + * @since 9 + */ +@MetadataDefinition +@ContentType +@Label("Frequency") +@Description("Measure of how often something occurs, in Hertz") +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD}) +public @interface Frequency { +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Label.java 2019-02-08 18:32:21.342679168 +0300 @@ -0,0 +1,55 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation that sets a human-readable name for an element (for example, + * {@code "Maximum Throughput"}). + *

+ * Use headline-style capitalization, capitalize the first and last words, and + * all nouns, pronouns, adjectives, verbs and adverbs. Do not include ending + * punctuation. + *

+ * The label should not be used as an identifier, see {@link Name}. + * + * @since 9 + */ +@MetadataDefinition +@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface Label { + /** + * Returns a human-readable name for the annotated element. + * + * @return a human-readable name, not {@code null} + */ + String value(); +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/MemoryAddress.java 2019-02-08 18:32:21.486674126 +0300 @@ -0,0 +1,45 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Event field annotation, specifies that the value is a memory address. + * + * @since 9 + */ +@MetadataDefinition +@ContentType +@Label("Memory Address") +@Description("Represents a physical memory address") +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE, ElementType.METHOD }) +public @interface MemoryAddress { +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/MetadataDefinition.java 2019-02-08 18:32:21.634668944 +0300 @@ -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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Meta annotation for defining new types of event metadata. + *

+ * In the following example, a transaction event is defined with two + * user-defined annotations, {@code @Severity} and {@code @TransactionId}. + * + *

+ * 
+ *{@literal @}MetadataDefinition
+ *{@literal @}Label("Severity")
+ *{@literal @}Description("Value between 0 and 100 that indicates severity. 100 is most severe.")
+ *{@literal @}Retention(RetentionPolicy.RUNTIME)
+ *{@literal @}Target({ ElementType.TYPE })
+ * public {@literal @}interface {@literal @}Severity {
+ *   int value() default 50;
+ * }
+ *
+ *{@literal @}MetadataDefinition
+ *{@literal @}Label("Transaction Id")
+ *{@literal @}Relational
+ *{@literal @}Retention(RetentionPolicy.RUNTIME)
+ *{@literal @}Target({ ElementType.FIELD })
+ * public {@literal @}interface {@literal @}Severity {
+ * }
+ *
+ *{@literal @}Severity(80)
+ *{@literal @}Label("Transaction Blocked");
+ * class TransactionBlocked extends Event {
+ *  {@literal @}TransactionId
+ *  {@literal @}Label("Transaction");
+ *   long transactionId;
+ *
+ *  {@literal @}TransactionId
+ *  {@literal @}Label("Transaction Blocker");
+ *   long transactionId;
+ * }
+ *
+ * 
+ * 
+ * + * Adding {@code @MetadataDefinition} to the declaration of {@code @Severity} and {@code @TransactionId} + * ensures the information is saved by Flight Recorder. + * + * @since 9 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE }) +public @interface MetadataDefinition { +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Name.java 2019-02-08 18:32:21.782663761 +0300 @@ -0,0 +1,53 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation that overrides the default name for an element (for example, when + * the default package for an event is not appropriate). + *

+ * The name must be a valid identifiers in the Java language (for example, + * {@code "com.example.MyEvent"} for an event class or {@code "message"} for an + * event field). + * + * @since 9 + */ +@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@MetadataDefinition +public @interface Name { + /** + * Returns the name. + * + * @return the name + */ + String value(); +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Percentage.java 2019-02-08 18:32:21.926658719 +0300 @@ -0,0 +1,50 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Event field annotation to use on fractions, typically between {@code 0.0} + * and {@code 1.0}, to specify that the value is a percentage. + *

+ * For example, a field with the value {@code 0.5} annotated by this annotation, + * should be interpreted as {@code 50%} and rendered in a graphical user + * interface with a percentage sign to avoid confusion with {@code 0.005%}. + * + * @since 9 + */ +@MetadataDefinition +@ContentType +@Label("Percentage") +@Description("Percentage, represented as a number between 0 and 1") +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE, ElementType.METHOD}) +public @interface Percentage { +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Period.java 2019-02-08 18:32:22.070653677 +0300 @@ -0,0 +1,72 @@ +/* + * 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; + +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; + +/** + * Event annotation, specifies the default setting value for a periodic event. + * + * @since 9 + */ +@MetadataDefinition +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Target(ElementType.TYPE) +public @interface Period { + /** + * Settings name {@code "period"} for configuring periodic events + */ + public final static String NAME = "period"; + + /** + * Returns the default setting value for a periodic setting. + *

+ * 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"}. + *

+ * A period may also be "everyChunk" to specify that it occurs at + * least once for every recording file. The number of events that are emitted + * depends on how many times the file rotations occur when data is recorded. + * + * @return the default setting value, not {@code null} + */ + String value() default "everyChunk"; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Recording.java 2019-02-08 18:32:22.218648496 +0300 @@ -0,0 +1,676 @@ +/* + * 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; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.time.Duration; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import jdk.jfr.internal.PlatformRecorder; +import jdk.jfr.internal.PlatformRecording; +import jdk.jfr.internal.Type; +import jdk.jfr.internal.Utils; +import jdk.jfr.internal.WriteableUserPath; + +/** + * Provides means to configure, start, stop and dump recording data to disk. + *

+ * The following example shows how configure, start, stop and dump recording data to disk. + * + *

+ * 
+ *   Configuration c = Configuration.getConfiguration("default");
+ *   Recording r = new Recording(c);
+ *   r.start();
+ *   System.gc();
+ *   Thread.sleep(5000);
+ *   r.stop();
+ *   r.copyTo(Files.createTempFile("my-recording", ".jfr"));
+ * 
+ * 
+ * + * @since 9 + */ +public final class Recording implements Closeable { + + private static class RecordingSettings extends EventSettings { + + private final Recording recording; + private final String identifier; + + RecordingSettings(Recording r, String identifier) { + this.recording = r; + this.identifier = identifier; + } + + RecordingSettings(Recording r, Class eventClass) { + Utils.ensureValidEventSubclass(eventClass); + this.recording = r; + this.identifier = String.valueOf(Type.getTypeId(eventClass)); + } + + @Override + public EventSettings with(String name, String value) { + Objects.requireNonNull(value); + recording.setSetting(identifier + "#" + name, value); + return this; + } + + @Override + public Map toMap() { + return recording.getSettings(); + } + } + + private final PlatformRecording internal; + + public Recording(Map settings) { + PlatformRecorder r = FlightRecorder.getFlightRecorder().getInternal(); + synchronized (r) { + this.internal = r.newRecording(settings); + this.internal.setRecording(this); + if (internal.getRecording() != this) { + throw new InternalError("Internal recording not properly setup"); + } + } + } + + /** + * Creates a recording without any settings. + *

+ * A newly created recording is in the {@link RecordingState#NEW} state. To start + * the recording, invoke the {@link Recording#start()} method. + * + * @throws IllegalStateException if Flight Recorder can't be created (for + * example, if the Java Virtual Machine (JVM) lacks Flight Recorder + * support, or if the file repository can't be created or accessed) + * + * @throws SecurityException If a security manager is used and + * FlightRecorderPermission "accessFlightRecorder" is not set. + */ + public Recording() { + this(new HashMap()); + } + + /** + * Creates a recording with settings from a configuration. + *

+ * The following example shows how create a recording that uses a predefined configuration. + * + *

+     * 
+     * Recording r = new Recording(Configuration.getConfiguration("default"));
+     * 
+     * 
+ * + * The newly created recording is in the {@link RecordingState#NEW} state. To + * start the recording, invoke the {@link Recording#start()} method. + * + * @param configuration configuration that contains the settings to be use, not + * {@code null} + * + * @throws IllegalStateException if Flight Recorder can't be created (for + * example, if the Java Virtual Machine (JVM) lacks Flight Recorder + * support, or if the file repository can't be created or accessed) + * + * @throws SecurityException if a security manager is used and + * FlightRecorderPermission "accessFlightRecorder" is not set. + * + * @see Configuration + */ + public Recording(Configuration configuration) { + this(configuration.getSettings()); + } + + /** + * Starts this recording. + *

+ * It's recommended that the recording options and event settings are configured + * before calling this method. The benefits of doing so are a more consistent + * state when analyzing the recorded data, and improved performance because the + * configuration can be applied atomically. + *

+ * After a successful invocation of this method, this recording is in the + * {@code RUNNING} state. + * + * @throws IllegalStateException if recording is already started or is in the + * {@code CLOSED} state + */ + public void start() { + internal.start(); + } + + /** + * Starts this recording after a delay. + *

+ * After a successful invocation of this method, this recording is in the + * {@code DELAYED} state. + * + * @param delay the time to wait before starting this recording, not + * {@code null} + * @throws IllegalStateException if the recording is not it the {@code NEW} state + */ + public void scheduleStart(Duration delay) { + Objects.requireNonNull(delay); + internal.scheduleStart(delay); + } + + /** + * Stops this recording. + *

+ * When a recording is stopped it can't be restarted. If this + * recording has a destination, data is written to that destination and + * the recording is closed. After a recording is closed, the data is no longer + * available. + *

+ * After a successful invocation of this method, this recording will be + * in the {@code STOPPED} state. + * + * @return {@code true} if recording is stopped, {@code false} otherwise + * + * @throws IllegalStateException if the recording is not started or is already stopped + * + * @throws SecurityException if a security manager exists and the caller + * doesn't have {@code FilePermission} to write to the destination + * path + * + * @see #setDestination(Path) + * + */ + public boolean stop() { + return internal.stop("Stopped by user"); + } + + /** + * Returns settings used by this recording. + *

+ * Modifying the returned {@code Map} will not change the settings for this recording. + *

+ * If no settings are set for this recording, an empty {@code Map} is + * returned. + * + * @return recording settings, not {@code null} + */ + public Map getSettings() { + return new HashMap<>(internal.getSettings()); + } + + /** + * Returns the current size of this recording in the disk repository, + * measured in bytes. + *

+ * The size is updated when recording buffers are flushed. If the recording is + * not written to the disk repository the returned size is always {@code 0}. + * + * @return amount of recorded data, measured in bytes, or {@code 0} if the + * recording is not written to the disk repository + */ + public long getSize() { + return internal.getSize(); + } + + /** + * Returns the time when this recording was stopped. + * + * @return the time, or {@code null} if this recording is not stopped + */ + public Instant getStopTime() { + return internal.getStopTime(); + } + + /** + * Returns the time when this recording was started. + * + * @return the the time, or {@code null} if this recording is not started + */ + public Instant getStartTime() { + return internal.getStartTime(); + } + + /** + * Returns the maximum size, measured in bytes, at which data is no longer kept in the disk repository. + * + * @return maximum size in bytes, or {@code 0} if no maximum size is set + */ + public long getMaxSize() { + return internal.getMaxSize(); + } + + /** + * Returns the length of time that the data is kept in the disk repository + * before it is removed. + * + * @return maximum length of time, or {@code null} if no maximum length of time + * has been set + */ + public Duration getMaxAge() { + return internal.getMaxAge(); + } + + /** + * Returns the name of this recording. + *

+ * By default, the name is the same as the recording ID. + * + * @return the recording name, not {@code null} + */ + public String getName() { + return internal.getName(); + } + + /** + * Replaces all settings for this recording. + *

+ * The following example shows how to set event settings for a recording. + * + *

+     * 
+     *     Map{@literal <}String, String{@literal >} settings = new HashMap{@literal <}{@literal >}();
+     *     settings.putAll(EventSettings.enabled("jdk.CPUSample").withPeriod(Duration.ofSeconds(2)).toMap());
+     *     settings.putAll(EventSettings.enabled(MyEvent.class).withThreshold(Duration.ofSeconds(2)).withoutStackTrace().toMap());
+     *     settings.put("jdk.ExecutionSample#period", "10 ms");
+     *     recording.setSettings(settings);
+     * 
+     * 
+ * + * The following example shows how to merge settings. + * + *
+     *     {@code
+     *     Map settings = recording.getSettings();
+     *     settings.putAll(additionalSettings);
+     *     recording.setSettings(settings);
+     * }
+     * 
+ * + * @param settings the settings to set, not {@code null} + */ + public void setSettings(Map settings) { + Objects.requireNonNull(settings); + Map sanitized = Utils.sanitizeNullFreeStringMap(settings); + internal.setSettings(sanitized); + } + + /** + * Returns the recording state that this recording is currently in. + * + * @return the recording state, not {@code null} + * + * @see RecordingState + */ + public RecordingState getState() { + return internal.getState(); + } + + /** + * Releases all data that is associated with this recording. + *

+ * After a successful invocation of this method, this recording is in the + * {@code CLOSED} state. + */ + @Override + public void close() { + internal.close(); + } + + /** + * Returns a clone of this recording, with a new recording ID and name. + * + * Clones are useful for dumping data without stopping the recording. After + * a clone is created, the amount of data to copy is constrained + * with the {@link #setMaxAge(Duration)} method and the {@link #setMaxSize(long)}method. + * + * @param stop {@code true} if the newly created copy should be stopped + * immediately, {@code false} otherwise + * @return the recording copy, not {@code null} + */ + public Recording copy(boolean stop) { + return internal.newCopy(stop); + } + + /** + * Writes recording data to a file. + *

+ * Recording must be started, but not necessarily stopped. + * + * @param destination the location where recording data is written, not + * {@code null} + * + * @throws IOException if the recording can't be copied to the specified + * location + * + * @throws SecurityException if a security manager exists and the caller doesn't + * have {@code FilePermission} to write to the destination path + */ + public void dump(Path destination) throws IOException { + Objects.requireNonNull(destination); + internal.dump(new WriteableUserPath(destination)); + + } + + /** + * Returns {@code true} if this recording uses the disk repository, {@code false} otherwise. + *

+ * If no value is set, {@code true} is returned. + * + * @return {@code true} if the recording uses the disk repository, {@code false} + * otherwise + */ + public boolean isToDisk() { + return internal.isToDisk(); + } + + /** + * Determines how much data is kept in the disk repository. + *

+ * To control the amount of recording data that is stored on disk, the maximum + * amount of data to retain can be specified. When the maximum limit is + * exceeded, the Java Virtual Machine (JVM) removes the oldest chunk to make + * room for a more recent chunk. + *

+ * If neither maximum limit or the maximum age is set, the size of the + * recording may grow indefinitely. + * + * @param maxSize the amount of data to retain, {@code 0} if infinite + * + * @throws IllegalArgumentException if maxSize is negative + * + * @throws IllegalStateException if the recording is in {@code CLOSED} state + */ + public void setMaxSize(long maxSize) { + if (maxSize < 0) { + throw new IllegalArgumentException("Max size of recording can't be negative"); + } + internal.setMaxSize(maxSize); + } + + /** + * Determines how far back data is kept in the disk repository. + *

+ * To control the amount of recording data stored on disk, the maximum length of + * time to retain the data can be specified. Data stored on disk that is older + * than the specified length of time is removed by the Java Virtual Machine (JVM). + *

+ * If neither maximum limit or the maximum age is set, the size of the + * recording may grow indefinitely. + * + * @param maxAge the length of time that data is kept, or {@code null} if infinite + * + * @throws IllegalArgumentException if maxAge is negative + * + * @throws IllegalStateException if the recording is in the {@code CLOSED} state + */ + public void setMaxAge(Duration maxAge) { + if (maxAge != null && maxAge.isNegative()) { + throw new IllegalArgumentException("Max age of recording can't be negative"); + } + internal.setMaxAge(maxAge); + } + + /** + * Sets a location where data is written on recording stop, or + * {@code null} if data is not to be dumped. + *

+ * If a destination is set, this recording is automatically closed + * after data is successfully copied to the destination path. + *

+ * If a destination is not set, Flight Recorder retains the + * recording data until this recording is closed. Use the {@link #dump(Path)} method to + * manually write data to a file. + * + * @param destination the destination path, or {@code null} if recording should + * not be dumped at stop + * + * @throws IllegalStateException if recording is in the {@code STOPPED} or + * {@code CLOSED} state. + * + * @throws SecurityException if a security manager exists and the caller + * doesn't have {@code FilePermission} to read, write, and delete the + * {@code destination} file + * + * @throws IOException if the path is not writable + */ + public void setDestination(Path destination) throws IOException { + internal.setDestination(destination != null ? new WriteableUserPath(destination) : null); + } + + /** + * Returns the destination file, where recording data is written when the + * recording stops, or {@code null} if no destination is set. + * + * @return the destination file, or {@code null} if not set. + */ + public Path getDestination() { + WriteableUserPath usp = internal.getDestination(); + if (usp == null) { + return null; + } else { + return usp.getPotentiallyMaliciousOriginal(); + } + } + + /** + * Returns a unique ID for this recording. + * + * @return the recording ID + */ + public long getId() { + return internal.getId(); + } + + /** + * Sets a human-readable name (for example, {@code "My Recording"}). + * + * @param name the recording name, not {@code null} + * + * @throws IllegalStateException if the recording is in {@code CLOSED} state + */ + public void setName(String name) { + Objects.requireNonNull(name); + internal.setName(name); + } + + /** + * Sets whether this recording is dumped to disk when the JVM exits. + * + * @param dumpOnExit if this recording should be dumped when the JVM exits + */ + public void setDumpOnExit(boolean dumpOnExit) { + internal.setDumpOnExit(dumpOnExit); + } + + /** + * Returns whether this recording is dumped to disk when the JVM exits. + *

+ * If dump on exit is not set, {@code false} is returned. + * + * @return {@code true} if the recording is dumped on exit, {@code false} + * otherwise. + */ + public boolean getDumpOnExit() { + return internal.getDumpOnExit(); + } + + /** + * Determines whether this recording is continuously flushed to the disk + * repository or data is constrained to what is available in memory buffers. + * + * @param disk {@code true} if this recording is written to disk, + * {@code false} if in-memory + * + */ + public void setToDisk(boolean disk) { + internal.setToDisk(disk); + } + + /** + * Creates a data stream for a specified interval. + *

+ * The stream may contain some data outside the specified range. + * + * @param the start start time for the stream, or {@code null} to get data from + * start time of the recording + * + * @param the end end time for the stream, or {@code null} to get data until the + * present time. + * + * @return an input stream, or {@code null} if no data is available in the + * interval. + * + * @throws IllegalArgumentException if {@code end} happens before + * {@code start} + * + * @throws IOException if a stream can't be opened + */ + public InputStream getStream(Instant start, Instant end) throws IOException { + if (start != null && end != null && end.isBefore(start)) { + throw new IllegalArgumentException("End time of requested stream must not be before start time"); + } + return internal.open(start, end); + } + + /** + * Returns the specified duration for this recording, or {@code null} if no + * duration is set. + *

+ * The duration can be set only when the recording is in the + * {@link RecordingState#NEW} state. + * + * @return the desired duration of the recording, or {@code null} if no duration + * has been set. + */ + public Duration getDuration() { + return internal.getDuration(); + } + + /** + * Sets a duration for how long a recording runs before it stops. + *

+ * By default, a recording has no duration ({@code null}). + * + * @param duration the duration, or {@code null} if no duration is set + * + * @throws IllegalStateException if recording is in the {@code STOPPED} or {@code CLOSED} state + */ + public void setDuration(Duration duration) { + internal.setDuration(duration); + } + + /** + * Enables the event with the specified name. + *

+ * If multiple events have the same name (for example, the same class is loaded + * in different class loaders), then all events that match the name are enabled. To + * enable a specific class, use the {@link #enable(Class)} method or a {@code String} + * representation of the event type ID. + * + * @param name the settings for the event, not {@code null} + * + * @return an event setting for further configuration, not {@code null} + * + * @see EventType + */ + public EventSettings enable(String name) { + Objects.requireNonNull(name); + RecordingSettings rs = new RecordingSettings(this, name); + rs.with("enabled", "true"); + return rs; + } + + /** + * Disables event with the specified name. + *

+ * If multiple events with same name (for example, the same class is loaded + * in different class loaders), then all events that match the + * name is disabled. To disable a specific class, use the + * {@link #disable(Class)} method or a {@code String} representation of the event + * type ID. + * + * @param name the settings for the event, not {@code null} + * + * @return an event setting for further configuration, not {@code null} + * + */ + public EventSettings disable(String name) { + Objects.requireNonNull(name); + RecordingSettings rs = new RecordingSettings(this, name); + rs.with("enabled", "false"); + return rs; + } + + /** + * Enables event. + * + * @param eventClass the event to enable, not {@code null} + * + * @throws IllegalArgumentException if {@code eventClass} is an abstract + * class or not a subclass of {@link Event} + * + * @return an event setting for further configuration, not {@code null} + */ + public EventSettings enable(Class eventClass) { + Objects.requireNonNull(eventClass); + RecordingSettings rs = new RecordingSettings(this, eventClass); + rs.with("enabled", "true"); + return rs; + } + + /** + * Disables event. + * + * @param eventClass the event to enable, not {@code null} + * + * @throws IllegalArgumentException if {@code eventClass} is an abstract + * class or not a subclass of {@link Event} + * + * @return an event setting for further configuration, not {@code null} + * + */ + public EventSettings disable(Class eventClass) { + Objects.requireNonNull(eventClass); + RecordingSettings rs = new RecordingSettings(this, eventClass); + rs.with("enabled", "false"); + return rs; + } + + // package private + PlatformRecording getInternal() { + return internal; + } + + private void setSetting(String id, String value) { + Objects.requireNonNull(id); + Objects.requireNonNull(value); + internal.setSetting(id, value); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/RecordingState.java 2019-02-08 18:32:22.370643174 +0300 @@ -0,0 +1,71 @@ +/* + * 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; + +/** + * Indicates a state in the life cycle of a recording. + * + * @since 9 + */ +public enum RecordingState { + + /** + * The initial state when a {@code Recording} is created. + */ + NEW, + + /** + * The recording is scheduled to start with a start time in the future. + *

+ * An invocation of the {@link Recording#start()} method will transition the + * recording to the {@code RUNNING} state. + */ + DELAYED, + + /** + * The recording is recording data and an invocation of the {@link Recording#stop()} + * method will transition the recording to the {@code STOPPED} state. + */ + RUNNING, + + /** + * The recording is stopped and is holding recorded data that can be dumped to + * disk. + *

+ * An invocation of the {@link Recording#close()} method will release the + * data and transition the recording to the {@code CLOSED} state. + */ + STOPPED, + + /** + * The recording is closed and all resources that are associated with the + * recording are released. + *

+ * Nothing that can be done with a recording from this point, and it's + * no longer retrievable from the {@code FlightRrecorder.getRecordings()} method. + */ + CLOSED; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Registered.java 2019-02-08 18:32:22.514638131 +0300 @@ -0,0 +1,55 @@ +/* + * 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; + +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; + +/** + * Event annotation, for programmatic event registration. + *

+ * Events are automatically registered when they are first used. This annotation + * can be used to override that registration. To register + * events programmatically, use {@link FlightRecorder#register(Class)}. + * + * @since 9 + */ +@Target({ ElementType.TYPE }) +@Inherited +@Retention(RetentionPolicy.RUNTIME) +public @interface Registered { + /** + * Returns {@code true} if the event is to be registered when the event class is + * first used, {@code false} otherwise. + * + * @return {@code true} if the event is to be registered when the event class is + * first used, {@code false} otherwise. + */ + public boolean value() default true; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Relational.java 2019-02-08 18:32:22.662632950 +0300 @@ -0,0 +1,43 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Meta annotation for relational annotations, to be used on an annotation. + * + * @since 9 + */ +@MetadataDefinition +@Label("Relation") +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +public @interface Relational { +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/SettingControl.java 2019-02-08 18:32:22.810627768 +0300 @@ -0,0 +1,220 @@ +/* + * 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; + +import java.security.AccessController; +import java.util.Set; + +import jdk.jfr.internal.Control; + +/** + * Base class to extend to create setting controls. + *

+ * The following example shows a naive implementation of a setting control for + * regular expressions: + * + *

+ * 
+ * final class RegExpControl extends SettingControl {
+ *   private Pattern pattern = Pattern.compile(".*");
+ *
+ *   {@literal @}Override
+ *   public void setValue(String value) {
+ *     this.pattern = Pattern.compile(value);
+ *   }
+ *
+ *   {@literal @}Override
+ *   public String combine(Set{@literal <}String{@literal >} values) {
+ *     return String.join("|", values);
+ *   }
+ *
+ *   {@literal @}Override
+ *   public String getValue() {
+ *     return pattern.toString();
+ *   }
+ *
+ *   public String matches(String s) {
+ *     return pattern.matcher(s).find();
+ *   }
+ * }
+ * 
+ * 
+ * + * The {@code setValue(String)}, {@code getValue()} and + * {@code combine(Set)} methods are invoked when a setting value + * changes, which typically happens when a recording is started or stopped. The + * {@code combine(Set)} method is invoked to resolve what value to use + * when multiple recordings are running at the same time. + *

+ * The setting control must have a default constructor that can be invoked when + * the event is registered. + *

+ * To use a setting control with an event, add a method that returns a + * {@code boolean} value and takes the setting control as a parameter. Annotate + * the method with the {@code @SettingDefinition} annotation. By default, the + * method name is used as the setting name, but the name can be set explicitly + * by using the {@code @Name} annotation. If the method returns {@code true}, + * the event will be committed. + *

+ * It is recommended that the {@code setValue(String)} method updates an + * efficient data structure that can be quickly checked when the event is + * committed. + *

+ * The following example shows how to create an event that uses the + * regular expression filter defined above. + * + *

+ * 
+ * abstract class HTTPRequest extends Event {
+ *   {@literal @}Label("Request URI")
+ *   protected String uri;
+ *
+ *   {@literal @}Label("Servlet URI Filter")
+ *   {@literal @}SettingDefinition
+ *   protected boolean uriFilter(RegExpControl regExp) {
+ *     return regExp.matches(uri);
+ *   }
+ * }
+ *
+ * {@literal @}Label("HTTP Get Request")
+ * class HTTPGetRequest extends HTTPRequest {
+ * }
+ *
+ * {@literal @}Label("HTTP Post Request")
+ * class HTTPPostRequest extends HTTPRequest {
+ * }
+ *
+ * class ExampleServlet extends HTTPServlet {
+ *   protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
+ *     HTTPGetRequest request = new HTTPGetRequest();
+ *     request.begin();
+ *     request.uri = req.getRequestURI();
+ *     ...
+ *     request.commit();
+ *   }
+ *
+ *   protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
+ *     HTTPPostRequest request = new HTTPPostRequest();
+ *     request.begin();
+ *     request.uri = req.getRequestURI();
+ *     ...
+ *     request.commit();
+ *   }
+ * }
+ * 
+ * 
+ * + * The following example shows how an event can be filtered by assigning the + * {@code "uriFilter"} setting with the specified regular expressions. + * + *
+ * 
+ * Recording r = new Recording();
+ * r.enable("HTTPGetRequest").with("uriFilter", "https://www.example.com/list/.*");
+ * r.enable("HTTPPostRequest").with("uriFilter", "https://www.example.com/login/.*");
+ * r.start();
+ * 
+ * 
+ * + * + * + * @see SettingDefinition + * + * @since 9 + */ +@MetadataDefinition +public abstract class SettingControl extends Control { + + /** + * Constructor for invocation by subclass constructors. + */ + protected SettingControl() { + super(AccessController.getContext()); + + } + + /** + * Combines the setting values for all running recordings into one value when + * multiple recordings are running at the same time, + *

+ * The semantics of how setting values are combined depends on the setting + * control that is implemented, but all recordings should get at least all the + * events they request. + *

+ * This method should have no side effects, because the caller might cache values. + * This method should never return {@code null} or throw an exception. If a + * value is not valid for this setting control, the value should be ignored. + *

+ * Examples: + *

+ * if the setting control represents a threshold and three recordings are + * running at the same time with the setting values {@code "10 ms"}, + * {@code "8 s"}, and {@code "1 ms"}, this method returns {@code "1 ms"} + * because it means that all recordings get at least all the requested data. + *

+ * If the setting control represents a set of names and two recordings are + * running at the same time with the setting values {@code "Smith, Jones"} and {@code "Jones, + * Williams"} the returned value is {@code "Smith, Jones, Williams"} because all names would be accepted. + *

+ * If the setting control represents a boolean condition and four recordings are + * running at the same time with the following values {@code "true"}, {@code "false"}, {@code "false"}, and + * {@code "incorrect"}, this method returns {@code "true"}, because all + * recordings get at least all the requested data. + * + * @param settingValues the set of values, not {@code null} + * + * @return the value to use, not {@code null} + */ + @Override + public abstract String combine(Set settingValues); + + /** + * Sets the value for this setting. + *

+ * If the setting value is not valid for this setting, this method + * does not throw an exception. Instead, the value is ignored. + * + * @param settingValue the string value, not {@code null} + */ + @Override + public abstract void setValue(String settingValue); + + /** + * Returns the currently used value for this setting, not {@code null}. + *

+ * The value returned by this method is valid as an argument to both + * the {@code setValue(String)} method and {@code combine(Set)} method. + *

+ * This method is invoked when an event is registered to obtain the + * default value. It is therefore important that a valid value can be + * returned immediately after an instance of this class is created. It is + * not valid to return {@code null}. + * + * @return the setting value, not {@code null} + */ + @Override + public abstract String getValue(); +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/SettingDefinition.java 2019-02-08 18:32:22.954622726 +0300 @@ -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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation that specifies that a method in an event class should be used to + * filter out events. + *

+ * For the method to be valid it must return a {@code SettingControl} and only have one + * parameter, which should be a non-abstract subclass of {@link SettingControl} + *

+ * The return value of the method specifies whether the event is to be + * written to the Flight Recorder system or not. + *

+ * The following example shows how to annotate a method in an event class. + * + *

+ * 
+ * class HelloWorld extend Event {
+ *
+ *   {@literal @}Label("Message");
+ *   String message;
+ *
+ *   {@literal @}SettingDefinition;
+ *   {@literal @}Label("Message Filter");
+ *   public boolean filter(RegExpControl regExp) {
+ *     return regExp.matches(message);
+ *   }
+ * }
+ * 
+ * 
+ * + * For an example of how the setting controls are defined, see + * {@link SettingControl}. + * + * @see SettingControl + * + * @since 9 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD }) +public @interface SettingDefinition { +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/SettingDescriptor.java 2019-02-08 18:32:23.094617825 +0300 @@ -0,0 +1,215 @@ +/* + * 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; + +import java.lang.annotation.Annotation; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import jdk.jfr.internal.AnnotationConstruct; +import jdk.jfr.internal.Type; + +/** + * Describes an event setting. + * + * @since 9 + */ +public final class SettingDescriptor { + + private final AnnotationConstruct annotationConstruct; + private final Type type; + private final String name; + private final String defaultValue; + + // package private, invoked by jdk.internal. + SettingDescriptor(Type type, String name, String defaultValue, List annotations) { + Objects.requireNonNull(annotations); + this.name = Objects.requireNonNull(name, "Name of value descriptor can't be null"); + this.type = Objects.requireNonNull(type); + this.annotationConstruct = new AnnotationConstruct(annotations); + this.defaultValue = Objects.requireNonNull(defaultValue); + } + + // package private + void setAnnotations(List as) { + annotationConstruct.setAnnotationElements(as); + } + + /** + * Returns the name of the setting (for example, {@code "threshold"}). + * + * @return the name, not {@code null} + */ + public String getName() { + return name; + } + + /** + * Returns a human-readable name that describes the setting (for example, + * {@code "Threshold"}). + *

+ * If the setting lacks a label, the label for the type that is associated with this + * setting is returned, or {@code null} if doesn't exist + * + * @return a human-readable name, or {@code null} if doesn't exist + */ + public String getLabel() { + String label = annotationConstruct.getLabel(); + if (label == null) { + label = type.getLabel(); + } + return label; + } + + /** + * Returns a sentence that describes the setting (for example + * {@code "Record event with duration + * above or equal to threshold"}). + *

+ * If the setting lacks a description, the description for the type that is + * associated with this setting is returned, or {@code null} if doesn't exist. + * + * @return the description, or {@code null} if doesn't exist + */ + public String getDescription() { + String description = annotationConstruct.getDescription(); + if (description == null) { + description = type.getDescription(); + } + return description; + } + + /** + * Returns a textual identifier that specifies how a value that is represented by + * this {@code SettingDescriptor} object is interpreted or formatted. + *

+ * For example, if the setting descriptor represents a percentage, then + * {@code "jdk.jfr.Percentage"} hints to a client that a value of "0.5" + * is formatted as "50%". + *

+ * The JDK provides the following predefined content types: + *

    + *
  • jdk.jfr.Percentage
  • + *
  • jdk.jfr.Timespan
  • + *
  • jdk.jfr.Timestamp
  • + *
  • jdk.jfr.Frequency
  • + *
  • jdk.jfr.Flag
  • + *
  • jdk.jfr.MemoryAddress
  • + *
  • jdk.jfr.DataAmount
  • + *
  • jdk.jfr.NetworkAddress
  • + *
+ *

+ * User-defined content types can be created by using {@link ContentType}. + *

+ * If the setting lacks a content type, the content type for the type + * that is associated with this setting is returned, or {@code null} if not + * available. + * + * @return the content type, or {@code null} if doesn't exist + * + * @see ContentType + */ + public String getContentType() { + for (AnnotationElement anno : getAnnotationElements()) { + for (AnnotationElement meta : anno.getAnnotationElements()) { + if (meta.getTypeName().equals(ContentType.class.getName())) { + return anno.getTypeName(); + } + } + } + for (AnnotationElement anno : type.getAnnotationElements()) { + for (AnnotationElement meta : anno.getAnnotationElements()) { + if (meta.getTypeName().equals(ContentType.class.getName())) { + return anno.getTypeName(); + } + } + } + return null; + } + + /** + * Returns the fully qualified class name of the type that is associated with this + * setting descriptor. + * + * @return the type name, not {@code null} + * + * @see SettingDescriptor#getTypeId() + */ + public String getTypeName() { + return type.getName(); + } + + /** + * Returns a unique ID for the type in the Java Virtual Machine (JVM). + *

+ * The ID might not be the same between JVM instances. + * + * @return the type ID, not negative + */ + public long getTypeId() { + return type.getId(); + } + + /** + * Returns the first annotation for the specified type if an annotation + * element with the same name is available, {@code null} otherwise. + * + * @param the type of the annotation to query for and return if available + * @param annotationType the Class object that corresponds to the annotation + * type, not {@code null} + * @return this element's annotation for the specified annotation type if + * available, {@code null} otherwise + */ + public A getAnnotation(Class annotationType) { + Objects.requireNonNull(annotationType); + return annotationConstruct.getAnnotation(annotationType); + } + + /** + * Returns an immutable list of annotation elements for this value + * descriptor. + * + * @return a list of annotations, not {@code null} + */ + public List getAnnotationElements() { + return Collections.unmodifiableList(annotationConstruct.getUnmodifiableAnnotationElements()); + } + + /** + * Returns the default value for this setting descriptor. + * + * @return the default value, not {@code null} + */ + public String getDefaultValue() { + return defaultValue; + } + + // package private + Type getType() { + return type; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/StackTrace.java 2019-02-08 18:32:23.242612644 +0300 @@ -0,0 +1,57 @@ +/* + * 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; + +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; + +/** + * Event annotation, determines whether an event by default has a stack trace + * or not. + * + * @since 9 + */ +@MetadataDefinition +@Target({ ElementType.TYPE }) +@Inherited +@Retention(RetentionPolicy.RUNTIME) +public @interface StackTrace { + /** + * Settings name {@code "stackTrace"} to be used for enabling event stack traces. + */ + public final static String NAME = "stackTrace"; + + /** + * Returns if the stack trace from the {@code Event#commit()} method should be recorded. + * + * @return {@code true} if the stack trace should be recorded, {@code false} + * otherwise + */ + boolean value() default true; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Threshold.java 2019-02-08 18:32:23.386607602 +0300 @@ -0,0 +1,69 @@ +/* + * 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; + +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; + +/** + * Event annotation, specifies the default duration below which an event is not + * recorded (for example, {@code "20 ms"}). + * + * @since 9 + */ +@MetadataDefinition +@Target({ ElementType.TYPE }) +@Inherited +@Retention(RetentionPolicy.RUNTIME) +public @interface Threshold { + /** + * Setting name {@code "threshold"} for configuring event thresholds. + */ + public final static String NAME = "threshold"; + + /** + * The threshold (for example, {@code "20 ms"}). + *

+ * A {@code 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 are {@code "0 ns"}, {@code "10 ms"}, and {@code "1 s"}. + * + * @return the threshold, default {@code "0 ns"}, not {@code null} + */ + String value() default "0 ns"; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Timespan.java 2019-02-08 18:32:23.534602421 +0300 @@ -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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Event field annotation, specifies that the value is a duration. + * + * @since 9 + */ +@MetadataDefinition +@ContentType +@Label("Timespan") +@Description("A duration, measured in nanoseconds by default") +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE, ElementType.METHOD}) +public @interface Timespan { + /** + * Unit for ticks. + */ + public static final String TICKS = "TICKS"; + /** + * Unit for seconds. + */ + public static final String SECONDS = "SECONDS"; + /** + * Unit for milliseconds. + */ + public static final String MILLISECONDS = "MILLISECONDS"; + /** + * Unit for nanoseconds. + */ + public static final String NANOSECONDS = "NANOSECONDS"; + + /** + * Unit for microseconds. + */ + public static final String MICROSECONDS = "MICROSECONDS"; + + /** + * Returns the unit of measure for the time span. + *

+ * By default, the unit is nanoseconds. + * + * @return the time span unit, default {@code #NANOSECONDS}, not {@code null} + */ + String value() default NANOSECONDS; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Timestamp.java 2019-02-08 18:32:23.682597240 +0300 @@ -0,0 +1,63 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Event field annotation, specifies that the value is a point in time. + * + * @since 9 + */ +@MetadataDefinition +@ContentType +@Label("Timestamp") +@Description("A point in time") +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE, ElementType.METHOD}) +public @interface Timestamp { + /** + * The unit for the difference, measured in milliseconds, between the current + * time and midnight, January 1, 1970 UTC. + */ + public final static String MILLISECONDS_SINCE_EPOCH = "MILLISECONDS_SINCE_EPOCH"; + + /** + * The unit for the number of ticks that have transpired since some arbitrary + * starting date. + */ + public final static String TICKS = "TICKS"; + + /** + * Unit for the time stamp. + * + * @return time stamp unit, not {@code null} + */ + String value() default Timestamp.MILLISECONDS_SINCE_EPOCH; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/TransitionFrom.java 2019-02-08 18:32:23.826592198 +0300 @@ -0,0 +1,43 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Event field annotation, specifies that the event transitioned from a thread. + * + * @since 9 + */ +@MetadataDefinition +@Label("Transition From") +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface TransitionFrom { +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/TransitionTo.java 2019-02-08 18:32:23.974587017 +0300 @@ -0,0 +1,43 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Event field annotation, specifies that the event will soon transition to a thread. + * + * @since 9 + */ +@MetadataDefinition +@Label("Transition To") +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface TransitionTo { +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/Unsigned.java 2019-02-08 18:32:24.118581975 +0300 @@ -0,0 +1,45 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Event field annotation, specifies that the value is of an unsigned data type. + * + * @since 9 + */ +@MetadataDefinition +@ContentType +@Label("Unsigned Value") +@Description("Value should be interpreted as unsigned data type") +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.FIELD, ElementType.TYPE }) +public @interface Unsigned { +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/ValueDescriptor.java 2019-02-08 18:32:24.266576795 +0300 @@ -0,0 +1,322 @@ +/* + * 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; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import jdk.jfr.internal.AnnotationConstruct; +import jdk.jfr.internal.Type; +import jdk.jfr.internal.Utils; + +/** + * Describes the event fields and annotation elements. + * + * @since 9 + */ +public final class ValueDescriptor { + + private final AnnotationConstruct annotationConstruct; + private final Type type; + private final String name; + private final boolean isArray; + private final boolean constantPool; + private final String javaFieldName; + + // package private, invoked by jdk.internal. + ValueDescriptor(Type type, String name, List annotations, int dimension, boolean constantPool, String fieldName) { + Objects.requireNonNull(annotations); + if (dimension < 0) { + throw new IllegalArgumentException("Dimension must be positive"); + } + this.name = Objects.requireNonNull(name, "Name of value descriptor can't be null"); + this.type = Objects.requireNonNull(type); + this.isArray = dimension > 0; + this.constantPool = constantPool; + this.annotationConstruct = new AnnotationConstruct(annotations); + this.javaFieldName = fieldName; + } + + /** + *

+ * Constructs a value descriptor, useful for dynamically creating event types and + * annotations. + *

+ * The following types are supported: + *

+ * + *

+ * The name must be a valid Java identifier (for example, {@code "maxThroughput"}). See 3.8 + * Java Language Specification for more information. + * + * @param type the type, not {@code null} + * @param name the name, not {@code null} + * + * @throws SecurityException if a security manager is present and the caller + * doesn't have {@code FlightRecorderPermission("registerEvent")} + * + */ + public ValueDescriptor(Class type, String name) { + this(type, name, Collections. emptyList()); + } + + /** + *

+ * Constructs a value descriptor, useful for dynamically creating event types and + * annotations. + *

+ * The following types are supported: + *

    + *
  • {@code byte.class} + *
  • {@code short.class} + *
  • {@code int.class} + *
  • {@code long.class} + *
  • {@code char.class} + *
  • {@code float.class} + *
  • {@code double.class} + *
  • {@code boolean.class} + *
  • {@code String.class} + *
  • {@code Class.class} + *
  • {@code Thread.class} + *
+ * + *

+ * The name must be a valid Java identifier (for example, {@code "maxThroughput"}). See 3.8 + * Java Language Specification for more information. + * + * @param type the type, not {@code null} + * @param name the name, not {@code null} + * @param annotations the annotations on the value descriptors, not + * {@code null} + * + * @throws SecurityException if a security manager is present and the caller + * doesn't have {@code FlightRecorderPermission("registerEvent")} + */ + public ValueDescriptor(Class type, String name, List annotations) { + this(type, name, new ArrayList<>(annotations), false); + } + + + ValueDescriptor(Class type, String name, List annotations, boolean allowArray) { + Objects.requireNonNull(annotations); + Utils.checkRegisterPermission(); + if (!allowArray) { + if (type.isArray()) { + throw new IllegalArgumentException("Array types are not allowed"); + } + } + this.name = Objects.requireNonNull(name, "Name of value descriptor can't be null"); + this.type = Objects.requireNonNull(Utils.getValidType(Objects.requireNonNull(type), Objects.requireNonNull(name))); + this.annotationConstruct = new AnnotationConstruct(annotations); + this.javaFieldName = name; // Needed for dynamic events + this.isArray = type.isArray(); + // Assume we always want to store String and Thread in constant pool + this.constantPool = type == Class.class || type == Thread.class; + } + + /** + * Returns a human-readable name that describes the value (for example, + * {@code "Maximum Throughput"}). + * + * @return a human-readable name, or {@code null} if doesn't exist + */ + public String getLabel() { + return annotationConstruct.getLabel(); + } + + /** + * Returns the name of the value (for example, {@code "maxThroughput"}). + * + * @return the name, not {@code null} + */ + public String getName() { + return name; + } + + /** + * Returns a sentence describing the value (for example, {@code "Maximum + * throughput in the transaction system. Value is reset after each new + * batch."}). + * + * @return the description, or {@code null} if doesn't exist + */ + public String getDescription() { + return annotationConstruct.getDescription(); + } + + /** + * Returns a textual identifier that specifies how a value represented by + * this {@link ValueDescriptor} is interpreted or formatted. + *

+ * For example, if the value descriptor's type is {@code float} and the + * event value is {@code 0.5f}, a content type of + * {@code "jdk.jfr.Percentage"} hints to a client that the value is a + * percentage and that it should be rendered as {@code "50%"}. + *

+ * The JDK provides the following predefined content types: + *

    + *
  • jdk.jfr.Percentage
  • + *
  • jdk.jfr.Timespan
  • + *
  • jdk.jfr.Timestamp
  • + *
  • jdk.jfr.Frequency
  • + *
  • jdk.jfr.Flag
  • + *
  • jdk.jfr.MemoryAddress
  • + *
  • jdk.jfr.DataAmount
  • + *
  • jdk.jfr.NetworkAddress
  • + *
+ *

+ * User-defined content types can be created by using the {@link ContentType} class. + * + * @return the content type, or {@code null} if doesn't exist + * + * @see ContentType + */ + public String getContentType() { + for (AnnotationElement anno : getAnnotationElements()) { + for (AnnotationElement meta : anno.getAnnotationElements()) { + if (meta.getTypeName().equals(ContentType.class.getName())) { + return anno.getTypeName(); + } + } + } + return null; + } + + /** + * Returns the fully qualified class name of the type that is associated with + * this value descriptor. + * + * @return the type name, not {@code null} + * + * @see ValueDescriptor#getTypeId() + */ + public String getTypeName() { + if (type.isSimpleType()) { + return type.getFields().get(0).getTypeName(); + } + return type.getName(); + } + + /** + * Returns a unique ID for the type in the Java virtual Machine (JVM). + * + * The ID might not be the same between JVM instances. + * + * @return the type ID, not negative + */ + public long getTypeId() { + return type.getId(); + } + + /** + * Returns if this value descriptor is an array type. + * + * @return {@code true} if it is an array type, {@code false} otherwise + */ + public boolean isArray() { + return isArray; + } + + /** + * Returns the first annotation for the specified type if an annotation + * element with the same name is directly present for this value descriptor, + * {@code null} otherwise. + * + * @param the type of the annotation to query for and return if present + * @param annotationType the Class object that corresponds to the annotation + * type, not {@code null} + * @return this element's annotation for the specified annotation type if + * directly present, else {@code null} + */ + public A getAnnotation(Class annotationType) { + Objects.requireNonNull(annotationType); + return annotationConstruct.getAnnotation(annotationType); + } + + /** + * Returns an immutable list of annotation elements for this value + * descriptor. + * + * @return a list of annotations, not {@code null} + */ + public List getAnnotationElements() { + return annotationConstruct.getUnmodifiableAnnotationElements(); + } + + /** + * Returns an immutable list of value descriptors if the type is complex, + * else an empty list. + * + * @return a list of value descriptors, not {@code null} + */ + public List getFields() { + if (type.isSimpleType()) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(type.getFields()); + } + + // package private + Type getType() { + return type; + } + + // package private + void setAnnotations(List anno) { + annotationConstruct.setAnnotationElements(anno); + } + + // package private + boolean isConstantPool() { + return constantPool; + } + + // package private + String getJavaFieldName() { + return javaFieldName; + } + + // package private + boolean isUnsigned() { + return annotationConstruct.hasUnsigned(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/conf/default.jfc 2019-02-08 18:32:24.410571753 +0300 @@ -0,0 +1,816 @@ + + + + + + + + true + everyChunk + + + + true + 1000 ms + + + + true + everyChunk + + + + true + 1000 ms + + + + true + + + + true + + + + true + true + 20 ms + + + + true + true + 20 ms + + + + true + true + 20 ms + + + + true + true + 20 ms + + + + false + true + 20 ms + + + + true + true + 0 ms + + + + true + true + 0 ms + + + + true + true + 0 ms + + + + true + true + + + + false + true + 0 ms + + + + false + true + + + + false + + + + true + beginChunk + + + + true + beginChunk + + + + true + 20 ms + + + + true + 20 ms + + + + true + 10 ms + + + + false + 10 ms + + + + false + 10 ms + + + + false + 10 ms + + + + false + 10 ms + + + + false + 10 ms + + + + true + 10 ms + + + + true + true + + + + true + everyChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + false + everyChunk + + + + true + everyChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + false + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + true + + + + true + true + + + + true + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + false + 0 ms + + + + false + 0 ms + + + + true + 0 ms + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + false + + + + false + + + + true + + + + false + true + + + + true + + + + false + everyChunk + + + + false + + + + true + false + 0 ns + + + + true + beginChunk + + + + true + 1000 ms + + + + true + 1000 ms + + + + true + 60 s + + + + false + + + + false + + + + true + beginChunk + + + + true + everyChunk + + + + true + 100 ms + + + + true + beginChunk + + + + true + everyChunk + + + + true + + + + true + beginChunk + + + + true + beginChunk + + + + true + 10 s + + + + true + 1000 ms + + + + true + 10 s + + + + true + beginChunk + + + + true + endChunk + + + + true + 5 s + + + + true + beginChunk + + + + true + everyChunk + + + + false + true + + + + false + true + + + + true + everyChunk + + + + true + true + 20 ms + + + + true + true + 20 ms + + + + true + true + 20 ms + + + + true + true + 20 ms + + + + true + true + 20 ms + + + + false + true + + + + true + true + + + + true + 1000 ms + + + + true + + + + true + + + + true + + + + true + + + + true + 10 ms + + + + true + 0 ms + + + + true + 10 ms + + + + true + 10 ms + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 20 ms + + 20 ms + + 20 ms + + false + + + + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/conf/profile.jfc 2019-02-08 18:32:24.554566713 +0300 @@ -0,0 +1,817 @@ + + + + + + + + true + everyChunk + + + + true + 1000 ms + + + + true + everyChunk + + + + true + 1000 ms + + + + true + + + + true + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + true + true + 0 ms + + + + true + true + 0 ms + + + + true + true + 0 ms + + + + true + true + + + + false + true + 0 ms + + + + false + true + + + + false + + + + true + beginChunk + + + + true + beginChunk + + + + true + 10 ms + + + + true + 10 ms + + + + true + 0 ms + + + + false + 0 ms + + + + false + 0 ms + + + + false + 0 ms + + + + false + 0 ms + + + + false + 0 ms + + + + true + 0 ms + + + + true + true + + + + true + 60 s + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + false + everyChunk + + + + true + everyChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + true + beginChunk + + + + false + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + true + + + + true + true + + + + true + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + true + 0 ms + + + + false + 0 ms + + + + false + 0 ms + + + + true + 0 ms + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + true + + + + false + true + + + + true + + + + false + everyChunk + + + + false + + + + true + true + 0 ns + + + + true + beginChunk + + + + true + 1000 ms + + + + true + 100 ms + + + + true + 10 s + + + + true + + + + false + + + + true + beginChunk + + + + true + everyChunk + + + + true + 100 ms + + + + true + beginChunk + + + + true + everyChunk + + + + true + + + + true + beginChunk + + + + true + beginChunk + + + + true + 10 s + + + + true + 1000 ms + + + + true + 10 s + + + + true + beginChunk + + + + true + endChunk + + + + true + 5 s + + + + true + beginChunk + + + + true + everyChunk + + + + true + true + + + + true + true + + + + true + everyChunk + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + true + true + 10 ms + + + + false + true + + + + true + true + + + + true + 1000 ms + + + + true + + + + true + + + + true + + + + true + + + + true + 10 ms + + + + true + 0 ms + + + + 10 ms + true + + + + true + 10 ms + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10 ms + + 10 ms + + 10 ms + + false + + + + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/ChunkParser.java 2019-02-08 18:32:24.702561532 +0300 @@ -0,0 +1,176 @@ +/* + * 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.util.Collection; +import java.util.List; + +import jdk.jfr.EventType; +import jdk.jfr.internal.LogLevel; +import jdk.jfr.internal.LogTag; +import jdk.jfr.internal.Logger; +import jdk.jfr.internal.MetadataDescriptor; +import jdk.jfr.internal.Type; +import jdk.jfr.internal.consumer.ChunkHeader; +import jdk.jfr.internal.consumer.RecordingInput; + +/** + * Parses a chunk. + * + */ +final class ChunkParser { + private static final long CONSTANT_POOL_TYPE_ID = 1; + private final RecordingInput input; + private final LongMap parsers; + private final ChunkHeader chunkHeader; + private final long absoluteChunkEnd; + private final MetadataDescriptor metadata; + private final LongMap typeMap; + private final TimeConverter timeConverter; + + public ChunkParser(RecordingInput input) throws IOException { + this(new ChunkHeader(input)); + } + + private ChunkParser(ChunkHeader header) throws IOException { + this.input = header.getInput(); + this.chunkHeader = header; + this.metadata = header.readMetadata(); + this.absoluteChunkEnd = header.getEnd(); + this.timeConverter = new TimeConverter(chunkHeader); + + ParserFactory factory = new ParserFactory(metadata, timeConverter); + LongMap constantPools = factory.getConstantPools(); + parsers = factory.getParsers(); + typeMap = factory.getTypeMap(); + + fillConstantPools(parsers, constantPools); + constantPools.forEach(ConstantMap::setIsResolving); + constantPools.forEach(ConstantMap::resolve); + constantPools.forEach(ConstantMap::setResolved); + + input.position(chunkHeader.getEventStart()); + } + + public RecordedEvent readEvent() throws IOException { + while (input.position() < absoluteChunkEnd) { + long pos = input.position(); + int size = input.readInt(); + if (size == 0) { + throw new IOException("Event can't have zero size"); + } + long typeId = input.readLong(); + if (typeId > CONSTANT_POOL_TYPE_ID) { // also skips metadata (id=0) + Parser ep = parsers.get(typeId); + if (ep instanceof EventParser) { + return (RecordedEvent) ep.parse(input); + } + } + input.position(pos + size); + } + return null; + } + + private void fillConstantPools(LongMap typeParser, LongMap constantPools) throws IOException { + long nextCP = chunkHeader.getAbsoluteChunkStart(); + long deltaToNext = chunkHeader.getConstantPoolPosition(); + while (deltaToNext != 0) { + nextCP += deltaToNext; + input.position(nextCP); + final long position = nextCP; + int size = input.readInt(); // size + long typeId = input.readLong(); + if (typeId != CONSTANT_POOL_TYPE_ID) { + throw new IOException("Expected check point event (id = 1) at position " + nextCP + ", but found type id = " + typeId); + } + input.readLong(); // timestamp + input.readLong(); // duration + deltaToNext = input.readLong(); + final long delta = deltaToNext; + boolean flush = input.readBoolean(); + int poolCount = input.readInt(); + Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> { + return "New constant pool: startPosition=" + position + + ", size=" + size + ", deltaToNext=" + delta + + ", flush=" + flush + ", poolCount=" + poolCount; + }); + + for (int i = 0; i < poolCount; i++) { + long id = input.readLong(); // type id + ConstantMap pool = constantPools.get(id); + Type type = typeMap.get(id); + if (pool == null) { + Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found constant pool(" + id + ") that is never used"); + if (type == null) { + throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]"); + } + pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName()); + constantPools.put(type.getId(), pool); + } + Parser parser = typeParser.get(id); + if (parser == null) { + throw new IOException("Could not find constant pool type with id = " + id); + } + try { + int count = input.readInt(); + Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> "Constant: " + getName(id) + "[" + count + "]"); + for (int j = 0; j < count; j++) { + long key = input.readLong(); + Object value = parser.parse(input); + pool.put(key, value); + } + } catch (Exception e) { + throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]", e); + } + } + if (input.position() != nextCP + size) { + throw new IOException("Size of check point event doesn't match content"); + } + } + } + + private String getName(long id) { + Type type = typeMap.get(id); + return type == null ? ("unknown(" + id +")") : type.getName(); + } + + public Collection getTypes() { + return metadata.getTypes(); + } + + public List getEventTypes() { + return metadata.getEventTypes(); + } + + public boolean isLastChunk() { + return chunkHeader.isLastChunk(); + } + + public ChunkParser nextChunkParser() throws IOException { + return new ChunkParser(chunkHeader.nextHeader()); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/ConstantMap.java 2019-02-08 18:32:24.846556491 +0300 @@ -0,0 +1,139 @@ +/* + * 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.ArrayList; +import java.util.List; + +/** + * Holds mapping between a set of keys and their corresponding object. + * + * If the type is a known type, i.e. {@link RecordedThread}, an + * {@link ObjectFactory} can be supplied which will instantiate a typed object. + */ +final class ConstantMap { + private final static class Reference { + private final long key; + private final ConstantMap pool; + + Reference(ConstantMap pool, long key) { + this.pool = pool; + this.key = key; + } + + Object resolve() { + return pool.get(key); + } + } + + private final ObjectFactory factory; + private final LongMap objects; + + private LongMap isResolving; + private boolean allResolved; + private String name; + + ConstantMap(ObjectFactory factory, String name) { + this.name = name; + this.objects = new LongMap<>(); + this.factory = factory; + } + + Object get(long id) { + // fast path, all objects in pool resolved + if (allResolved) { + return objects.get(id); + } + // referenced from a pool, deal with this later + if (isResolving == null) { + return new Reference(this, id); + } + + Boolean beingResolved = isResolving.get(id); + + // we are resolved (but not the whole pool) + if (Boolean.FALSE.equals(beingResolved)) { + return objects.get(id); + } + + // resolving ourself, abort to avoid infinite recursion + if (Boolean.TRUE.equals(beingResolved)) { + return null; + } + + // resolve me! + isResolving.put(id, Boolean.TRUE); + Object resolved = resolve(objects.get(id)); + isResolving.put(id, Boolean.FALSE); + if (factory != null) { + Object factorized = factory.createObject(id, resolved); + objects.put(id, factorized); + return factorized; + } else { + objects.put(id, resolved); + return resolved; + } + } + + private static Object resolve(Object o) { + if (o instanceof Reference) { + return resolve(((Reference) o).resolve()); + } + if (o != null && o.getClass().isArray()) { + final Object[] array = (Object[]) o; + for (int i = 0; i < array.length; i++) { + array[i] = resolve(array[i]); + } + return array; + } + return o; + } + + public void resolve() { + List keyList = new ArrayList<>(); + objects.keys().forEachRemaining(keyList::add); + for (Long l : keyList) { + get(l); + } + } + + public void put(long key, Object value) { + objects.put(key, value); + } + + public void setIsResolving() { + isResolving = new LongMap<>(); + } + + public void setResolved() { + allResolved = true; + isResolving = null; // pool finished, release memory + } + + public String getName() { + return name; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/EventParser.java 2019-02-08 18:32:24.990551450 +0300 @@ -0,0 +1,72 @@ +/* + * 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 static jdk.jfr.internal.EventInstrumentation.FIELD_DURATION; + +import java.io.IOException; +import java.util.List; + +import jdk.jfr.EventType; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.internal.consumer.RecordingInput; + +/** + * Parses an event and returns a {@link RecordedEvent}. + * + */ +final class EventParser extends Parser { + private final Parser[] parsers; + private final EventType eventType; + private final TimeConverter timeConverter; + private final boolean hasDuration; + private final List valueDescriptors; + + EventParser(TimeConverter timeConverter, EventType type, Parser[] parsers) { + this.timeConverter = timeConverter; + this.parsers = parsers; + this.eventType = type; + this.hasDuration = type.getField(FIELD_DURATION) != null; + this.valueDescriptors = type.getFields(); + } + + @Override + public Object parse(RecordingInput input) throws IOException { + Object[] values = new Object[parsers.length]; + for (int i = 0; i < parsers.length; i++) { + values[i] = parsers[i].parse(input); + } + Long startTicks = (Long) values[0]; + long startTime = timeConverter.convertTimestamp(startTicks); + if (hasDuration) { + long durationTicks = (Long) values[1]; + long endTime = timeConverter.convertTimestamp(startTicks + durationTicks); + return new RecordedEvent(eventType, valueDescriptors, values, startTime, endTime, timeConverter); + } else { + return new RecordedEvent(eventType, valueDescriptors, values, startTime, startTime, timeConverter); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/LongMap.java 2019-02-08 18:32:25.138546269 +0300 @@ -0,0 +1,61 @@ +/* + * 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.HashMap; +import java.util.Iterator; + +/** + * Commonly used data structure for looking up objects given an id (long value) + * + * TODO: Implement without using Map and Long objects, to minimize allocation + * + * @param + */ +final class LongMap implements Iterable { + private final HashMap map; + + LongMap() { + map = new HashMap<>(101); + } + + void put(long id, T object) { + map.put(id, object); + } + + T get(long id) { + return map.get(id); + } + + @Override + public Iterator iterator() { + return map.values().iterator(); + } + + Iterator keys() { + return map.keySet().iterator(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/ObjectFactory.java 2019-02-08 18:32:25.282541229 +0300 @@ -0,0 +1,85 @@ +/* + * 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; + +/** + * Abstract factory for creating specialized types + */ +abstract class ObjectFactory { + + final static String TYPE_PREFIX_VERSION_1 = "com.oracle.jfr.types."; + final static String TYPE_PREFIX_VERSION_2 = Type.TYPES_PREFIX; + final static String STACK_FRAME_VERSION_1 = TYPE_PREFIX_VERSION_1 + "StackFrame"; + final static String STACK_FRAME_VERSION_2 = TYPE_PREFIX_VERSION_2 + "StackFrame"; + + public static ObjectFactory create(Type type, TimeConverter timeConverter) { + switch (type.getName()) { + case "java.lang.Thread": + return RecordedThread.createFactory(type, timeConverter); + case TYPE_PREFIX_VERSION_1 + "StackFrame": + case TYPE_PREFIX_VERSION_2 + "StackFrame": + return RecordedFrame.createFactory(type, timeConverter); + case TYPE_PREFIX_VERSION_1 + "Method": + case TYPE_PREFIX_VERSION_2 + "Method": + return RecordedMethod.createFactory(type, timeConverter); + case TYPE_PREFIX_VERSION_1 + "ThreadGroup": + case TYPE_PREFIX_VERSION_2 + "ThreadGroup": + return RecordedThreadGroup.createFactory(type, timeConverter); + case TYPE_PREFIX_VERSION_1 + "StackTrace": + case TYPE_PREFIX_VERSION_2 + "StackTrace": + return RecordedStackTrace.createFactory(type, timeConverter); + case TYPE_PREFIX_VERSION_1 + "ClassLoader": + case TYPE_PREFIX_VERSION_2 + "ClassLoader": + return RecordedClassLoader.createFactory(type, timeConverter); + case "java.lang.Class": + return RecordedClass.createFactory(type, timeConverter); + } + return null; + } + + private final List valueDescriptors; + + ObjectFactory(Type type) { + this.valueDescriptors = type.getFields(); + } + + T createObject(long id, Object value) { + if (value == null) { + return null; + } + if (value instanceof Object[]) { + return createTyped(valueDescriptors, id, (Object[]) value); + } + throw new InternalError("Object factory must have struct type"); + } + + abstract T createTyped(List valueDescriptors, long id, Object[] values); +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/Parser.java 2019-02-08 18:32:25.426536188 +0300 @@ -0,0 +1,45 @@ +/* + * 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 jdk.jfr.internal.consumer.RecordingInput; + +/** + * Base class for parsing data from a {@link RecordingInput}. + */ +abstract class Parser { + /** + * Parses data from a {@link RecordingInput} and return an object. + * + * @param input input to read from + * @return an object + * @throws IOException if operation couldn't be completed due to I/O + * problems + */ + abstract Object parse(RecordingInput input) throws IOException; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/ParserFactory.java 2019-02-08 18:32:25.570531148 +0300 @@ -0,0 +1,303 @@ +/* + * 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.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import jdk.jfr.EventType; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.internal.MetadataDescriptor; +import jdk.jfr.internal.PrivateAccess; +import jdk.jfr.internal.Type; +import jdk.jfr.internal.consumer.RecordingInput; + +/** + * Class that create parsers suitable for reading events and constant pools + */ +final class ParserFactory { + private final LongMap parsers = new LongMap<>(); + private final TimeConverter timeConverter; + private final LongMap types = new LongMap<>(); + private final LongMap constantPools; + + public ParserFactory(MetadataDescriptor metadata, TimeConverter timeConverter) throws IOException { + this.constantPools = new LongMap<>(); + this.timeConverter = timeConverter; + for (Type t : metadata.getTypes()) { + types.put(t.getId(), t); + } + for (Type t : types) { + if (!t.getFields().isEmpty()) { // Avoid primitives + CompositeParser cp = createCompositeParser(t); + if (t.isSimpleType()) { // Reduce to nested parser + parsers.put(t.getId(), cp.parsers[0]); + } + + } + } + // Override event types with event parsers + for (EventType t : metadata.getEventTypes()) { + parsers.put(t.getId(), createEventParser(t)); + } + } + + public LongMap getParsers() { + return parsers; + } + + public LongMap getConstantPools() { + return constantPools; + } + + public LongMap getTypeMap() { + return types; + } + + private EventParser createEventParser(EventType eventType) throws IOException { + List parsers = new ArrayList(); + for (ValueDescriptor f : eventType.getFields()) { + parsers.add(createParser(f)); + } + return new EventParser(timeConverter, eventType, parsers.toArray(new Parser[0])); + } + + private Parser createParser(ValueDescriptor v) throws IOException { + boolean constantPool = PrivateAccess.getInstance().isConstantPool(v); + if (v.isArray()) { + Type valueType = PrivateAccess.getInstance().getType(v); + ValueDescriptor element = PrivateAccess.getInstance().newValueDescriptor(v.getName(), valueType, v.getAnnotationElements(), 0, constantPool, null); + return new ArrayParser(createParser(element)); + } + long id = v.getTypeId(); + Type type = types.get(id); + if (type == null) { + throw new IOException("Type '" + v.getTypeName() + "' is not defined"); + } + if (constantPool) { + ConstantMap pool = constantPools.get(id); + if (pool == null) { + pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName()); + constantPools.put(id, pool); + } + return new ConstantMapValueParser(pool); + } + Parser parser = parsers.get(id); + if (parser == null) { + if (!v.getFields().isEmpty()) { + return createCompositeParser(type); + } else { + return registerParserType(type, createPrimitiveParser(type)); + } + } + return parser; + } + + private Parser createPrimitiveParser(Type type) throws IOException { + switch (type.getName()) { + case "int": + return new IntegerParser(); + case "long": + return new LongParser(); + case "float": + return new FloatParser(); + case "double": + return new DoubleParser(); + case "char": + return new CharacterParser(); + case "boolean": + return new BooleanParser(); + case "short": + return new ShortParser(); + case "byte": + return new ByteParser(); + case "java.lang.String": + ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName()); + constantPools.put(type.getId(), pool); + return new StringParser(pool); + default: + throw new IOException("Unknown primitive type " + type.getName()); + } + } + + private Parser registerParserType(Type t, Parser parser) { + Parser p = parsers.get(t.getId()); + // check if parser exists (known type) + if (p != null) { + return p; + } + parsers.put(t.getId(), parser); + return parser; + } + + private CompositeParser createCompositeParser(Type type) throws IOException { + List vds = type.getFields(); + Parser[] parsers = new Parser[vds.size()]; + CompositeParser composite = new CompositeParser(parsers); + // need to pre-register so recursive types can be handled + registerParserType(type, composite); + + int index = 0; + for (ValueDescriptor vd : vds) { + parsers[index++] = createParser(vd); + } + return composite; + } + + private static final class BooleanParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE; + } + } + + private static final class ByteParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return Byte.valueOf(input.readByte()); + } + } + + private static final class LongParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return Long.valueOf(input.readLong()); + } + } + + private static final class IntegerParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return Integer.valueOf(input.readInt()); + } + } + + private static final class ShortParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return Short.valueOf(input.readShort()); + } + } + + private static final class CharacterParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return Character.valueOf(input.readChar()); + } + } + + private static final class FloatParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return Float.valueOf(input.readFloat()); + } + } + + private static final class DoubleParser extends Parser { + @Override + public Object parse(RecordingInput input) throws IOException { + return Double.valueOf(input.readDouble()); + } + } + + private static final class StringParser extends Parser { + private final ConstantMap stringConstantMap; + private String last; + + StringParser(ConstantMap stringConstantMap) { + this.stringConstantMap = stringConstantMap; + } + + @Override + public Object parse(RecordingInput input) throws IOException { + String s = parseEncodedString(input); + if (!Objects.equals(s, last)) { + last = s; + } + return last; + } + + private String parseEncodedString(RecordingInput input) throws IOException { + byte encoding = input.readByte(); + if (encoding == RecordingInput.STRING_ENCODING_CONSTANT_POOL) { + long id = input.readLong(); + return (String) stringConstantMap.get(id); + } else { + return input.readEncodedString(encoding); + } + } + } + + private final static class ArrayParser extends Parser { + private final Parser elementParser; + + public ArrayParser(Parser elementParser) { + this.elementParser = elementParser; + } + + @Override + public Object parse(RecordingInput input) throws IOException { + final int size = input.readInt(); + final Object[] array = new Object[size]; + for (int i = 0; i < size; i++) { + array[i] = elementParser.parse(input); + } + return array; + } + } + + private final static class CompositeParser extends Parser { + private final Parser[] parsers; + + public CompositeParser(Parser[] valueParsers) { + this.parsers = valueParsers; + } + + @Override + public Object parse(RecordingInput input) throws IOException { + final Object[] values = new Object[parsers.length]; + for (int i = 0; i < values.length; i++) { + values[i] = parsers[i].parse(input); + } + return values; + } + } + + private static final class ConstantMapValueParser extends Parser { + private final ConstantMap pool; + + ConstantMapValueParser(ConstantMap pool) { + this.pool = pool; + } + + @Override + public Object parse(RecordingInput input) throws IOException { + return pool.get(input.readLong()); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/RecordedClass.java 2019-02-08 18:32:25.714526107 +0300 @@ -0,0 +1,103 @@ +/* + * 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 Java type, such as a class or an interface. + * + * @since 9 + */ +public final class RecordedClass extends RecordedObject { + + static ObjectFactory createFactory(Type type, TimeConverter timeConverter) { + return new ObjectFactory(type) { + @Override + RecordedClass createTyped(List desc, long id, Object[] object) { + return new RecordedClass(desc, id, object, timeConverter); + } + }; + } + + private final long uniqueId; + + // package private + private RecordedClass(List 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; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/RecordedClassLoader.java 2019-02-08 18:32:25.858521067 +0300 @@ -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; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/RecordedEvent.java 2019-02-08 18:32:26.002516026 +0300 @@ -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(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/RecordedFrame.java 2019-02-08 18:32:26.150510847 +0300 @@ -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); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/RecordedMethod.java 2019-02-08 18:32:26.302505526 +0300 @@ -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); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/RecordedObject.java 2019-02-08 18:32:26.442500626 +0300 @@ -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); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/RecordedStackTrace.java 2019-02-08 18:32:26.590495446 +0300 @@ -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); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/RecordedThread.java 2019-02-08 18:32:26.734490406 +0300 @@ -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; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/RecordedThreadGroup.java 2019-02-08 18:32:26.878485366 +0300 @@ -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); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/RecordingFile.java 2019-02-08 18:32:27.022480326 +0300 @@ -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"); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/consumer/TimeConverter.java 2019-02-08 18:32:27.166475286 +0300 @@ -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); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/events/AbstractJDKEvent.java 2019-02-08 18:32:27.310470247 +0300 @@ -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 { +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/events/ActiveRecordingEvent.java 2019-02-08 18:32:27.458465066 +0300 @@ -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; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/events/ActiveSettingEvent.java 2019-02-08 18:32:27.602460027 +0300 @@ -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; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/events/ErrorThrownEvent.java 2019-02-08 18:32:27.746454987 +0300 @@ -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; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/events/ExceptionStatisticsEvent.java 2019-02-08 18:32:27.894449808 +0300 @@ -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; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/events/ExceptionThrownEvent.java 2019-02-08 18:32:28.034444908 +0300 @@ -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; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/events/FileForceEvent.java 2019-02-08 18:32:28.178439869 +0300 @@ -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; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/events/FileReadEvent.java 2019-02-08 18:32:28.322434829 +0300 @@ -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; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/events/FileWriteEvent.java 2019-02-08 18:32:28.470429651 +0300 @@ -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; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/events/SocketReadEvent.java 2019-02-08 18:32:28.618424471 +0300 @@ -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; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/events/SocketWriteEvent.java 2019-02-08 18:32:28.766419292 +0300 @@ -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; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/ASMToolkit.java 2019-02-08 18:32:28.910414253 +0300 @@ -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(); + }); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/AnnotationConstruct.java 2019-02-08 18:32:29.058409074 +0300 @@ -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 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 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; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/Bits.java 2019-02-08 18:32:29.202404035 +0300 @@ -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; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/ChunkInputStream.java 2019-02-08 18:32:29.350398855 +0300 @@ -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(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/ChunksChannel.java 2019-02-08 18:32:29.498393677 +0300 @@ -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(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/Control.java 2019-02-08 18:32:29.646388498 +0300 @@ -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"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/Cutoff.java 2019-02-08 18:32:29.790383459 +0300 @@ -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"; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/EventClassBuilder.java 2019-02-08 18:32:29.934378420 +0300 @@ -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 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. + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/EventControl.java 2019-02-08 18:32:30.082373242 +0300 @@ -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 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 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) settingClass, m, type, name); + } + } + } + } + } + eventClass = eventClass.getSuperclass(); + allowPrivateMethod = false; + } + } + + private void defineSetting(Class 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 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; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/EventHandlerCreator.java 2019-02-08 18:32:30.230368064 +0300 @@ -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 eventClass) { + this(id, settingInfos, createFieldInfos(eventClass, type)); + } + + private static List createFieldInfos(Class 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 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 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(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/EventInstrumentation.java 2019-02-08 18:32:30.378362885 +0300 @@ -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; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/EventWriter.java 2019-02-08 18:32:30.522357846 +0300 @@ -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; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/EventWriterMethod.java 2019-02-08 18:32:30.670352668 +0300 @@ -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); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/JVM.java 2019-02-08 18:32:30.814347630 +0300 @@ -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); +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/JVMSupport.java 2019-02-08 18:32:30.958342591 +0300 @@ -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; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/JVMUpcalls.java 2019-02-08 18:32:31.102337553 +0300 @@ -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); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/LogLevel.java 2019-02-08 18:32:31.246332515 +0300 @@ -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; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/LogTag.java 2019-02-08 18:32:31.390327477 +0300 @@ -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); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/Logger.java 2019-02-08 18:32:31.538322298 +0300 @@ -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)); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/MetadataDescriptor.java 2019-02-08 18:32:31.686317121 +0300 @@ -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("\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 types, DataOutput output) throws IOException { + MetadataDescriptor m = new MetadataDescriptor(); + m.locale = Locale.getDefault().toString(); + m.gmtOffset = TimeZone.getDefault().getRawOffset(); + m.types.addAll(types); + MetadataWriter w = new MetadataWriter(m); + w.writeBinary(output); + } + + @Override + public String toString() { + return root.toString(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/MetadataHandler.java 2019-02-08 18:32:31.826312222 +0300 @@ -0,0 +1,403 @@ +/* + * 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.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import jdk.internal.org.xml.sax.Attributes; +import jdk.internal.org.xml.sax.EntityResolver; +import jdk.internal.org.xml.sax.SAXException; +import jdk.internal.org.xml.sax.helpers.DefaultHandler; +import jdk.internal.util.xml.SAXParser; +import jdk.internal.util.xml.impl.SAXParserImpl; +import jdk.jfr.AnnotationElement; +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Enabled; +import jdk.jfr.Experimental; +import jdk.jfr.Label; +import jdk.jfr.Period; +import jdk.jfr.Relational; +import jdk.jfr.StackTrace; +import jdk.jfr.Threshold; +import jdk.jfr.TransitionFrom; +import jdk.jfr.TransitionTo; +import jdk.jfr.Unsigned; + +final class MetadataHandler extends DefaultHandler implements EntityResolver { + + static class TypeElement { + List fields = new ArrayList<>(); + String name; + String label; + String description; + String category; + String superType; + String period; + boolean thread; + boolean startTime; + boolean stackTrace; + boolean cutoff; + boolean isEvent; + boolean experimental; + boolean valueType; + } + + static class FieldElement { + TypeElement referenceType; + String name; + String label; + String description; + String contentType; + String typeName; + String transition; + String relation; + boolean struct; + boolean array; + boolean experimental; + boolean unsigned; + } + + static class XmlType { + String name; + String javaType; + String contentType; + boolean unsigned; + } + + final Map types = new LinkedHashMap<>(200); + final Map xmlTypes = new HashMap<>(20); + final Map xmlContentTypes = new HashMap<>(20); + final List relations = new ArrayList<>(); + long eventTypeId = 255; + long structTypeId = 33; + FieldElement currentField; + TypeElement currentType; + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + switch (qName) { + case "XmlType": + XmlType xmlType = new XmlType(); + xmlType.name = attributes.getValue("name"); + xmlType.javaType = attributes.getValue("javaType"); + xmlType.contentType = attributes.getValue("contentType"); + xmlType.unsigned = Boolean.valueOf(attributes.getValue("unsigned")); + xmlTypes.put(xmlType.name, xmlType); + break; + case "Type": + case "Event": + currentType = new TypeElement(); + currentType.name = attributes.getValue("name"); + currentType.label = attributes.getValue("label"); + currentType.description = attributes.getValue("description"); + currentType.category = attributes.getValue("category"); + currentType.thread = getBoolean(attributes, "thread", false); + currentType.stackTrace = getBoolean(attributes, "stackTrace", false); + currentType.startTime = getBoolean(attributes, "startTime", true); + currentType.period = attributes.getValue("period"); + currentType.cutoff = getBoolean(attributes, "cutoff", false); + currentType.experimental = getBoolean(attributes, "experimental", false); + currentType.isEvent = qName.equals("Event"); + break; + case "Field": + currentField = new FieldElement(); + currentField.struct = getBoolean(attributes, "struct", false); + currentField.array = getBoolean(attributes, "array", false); + currentField.name = attributes.getValue("name"); + currentField.label = attributes.getValue("label"); + currentField.typeName = attributes.getValue("type"); + currentField.description = attributes.getValue("description"); + currentField.experimental = getBoolean(attributes, "experimental", false); + currentField.contentType = attributes.getValue("contentType"); + currentField.relation = attributes.getValue("relation"); + currentField.transition = attributes.getValue("transition"); + break; + case "XmlContentType": + String name = attributes.getValue("name"); + String type = attributes.getValue("annotationType"); + String value = attributes.getValue("annotationValue"); + Class annotationType = createAnnotationClass(type); + AnnotationElement ae = value == null ? new AnnotationElement(annotationType) : new AnnotationElement(annotationType, value); + xmlContentTypes.put(name, ae); + break; + case "Relation": + String n = attributes.getValue("name"); + relations.add(n); + break; + } + } + + @SuppressWarnings("unchecked") + private Class createAnnotationClass(String type) { + try { + if (!type.startsWith("jdk.jfr.")) { + throw new IllegalStateException("Incorrect type " + type + ". Annotation class must be located in jdk.jfr package."); + } + Class c = Class.forName(type, true, null); + return (Class) c; + } catch (ClassNotFoundException cne) { + throw new IllegalStateException(cne); + } + } + + private boolean getBoolean(Attributes attributes, String name, boolean defaultValue) { + String value = attributes.getValue(name); + return value == null ? defaultValue : Boolean.valueOf(value); + } + + @Override + public void endElement(String uri, String localName, String qName) { + switch (qName) { + case "Type": + case "Event": + types.put(currentType.name, currentType); + currentType = null; + break; + case "Field": + currentType.fields.add(currentField); + currentField = null; + break; + } + } + + public static List createTypes() throws IOException { + SAXParser parser = new SAXParserImpl(); + MetadataHandler t = new MetadataHandler(); + try (InputStream is = new BufferedInputStream(SecuritySupport.getResourceAsStream("/jdk/jfr/internal/types/metadata.xml"))) { + Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, () -> "Parsing metadata.xml"); + try { + parser.parse(is, t); + return t.buildTypes(); + } catch (Exception e) { + e.printStackTrace(); + throw new IOException(e); + } + } + } + + private List buildTypes() { + removeXMLConvenience(); + Map typeMap = buildTypeMap(); + Map relationMap = buildRelationMap(typeMap); + addFields(typeMap, relationMap); + return trimTypes(typeMap); + } + + private Map buildRelationMap(Map typeMap) { + Map relationMap = new HashMap<>(); + for (String relation : relations) { + Type relationType = new Type(Type.TYPES_PREFIX + relation, Type.SUPER_TYPE_ANNOTATION, eventTypeId++); + relationType.setAnnotations(Collections.singletonList(new AnnotationElement(Relational.class))); + AnnotationElement ae = PrivateAccess.getInstance().newAnnotation(relationType, Collections.emptyList(), true); + relationMap.put(relation, ae); + typeMap.put(relationType.getName(), relationType); + } + return relationMap; + } + + private List trimTypes(Map lookup) { + List trimmedTypes = new ArrayList<>(lookup.size()); + for (Type t : lookup.values()) { + t.trimFields(); + trimmedTypes.add(t); + } + return trimmedTypes; + } + + private void addFields(Map lookup, Map relationMap) { + for (TypeElement te : types.values()) { + Type type = lookup.get(te.name); + if (te.isEvent) { + boolean periodic = te.period!= null; + TypeLibrary.addImplicitFields(type, periodic, te.startTime && !periodic, te.thread, te.stackTrace && !periodic, te.cutoff); + } + for (FieldElement f : te.fields) { + Type fieldType = Type.getKnownType(f.typeName); + if (fieldType == null) { + fieldType = Objects.requireNonNull(lookup.get(f.referenceType.name)); + } + List aes = new ArrayList<>(); + if (f.unsigned) { + aes.add(new AnnotationElement(Unsigned.class)); + } + if (f.contentType != null) { + aes.add(Objects.requireNonNull(xmlContentTypes.get(f.contentType))); + } + if (f.relation != null) { + aes.add(Objects.requireNonNull(relationMap.get(f.relation))); + } + if (f.label != null) { + aes.add(new AnnotationElement(Label.class, f.label)); + } + if (f.experimental) { + aes.add(new AnnotationElement(Experimental.class)); + } + if (f.description != null) { + aes.add(new AnnotationElement(Description.class, f.description)); + } + if ("from".equals(f.transition)) { + aes.add(new AnnotationElement(TransitionFrom.class)); + } + if ("to".equals(f.transition)) { + aes.add(new AnnotationElement(TransitionTo.class)); + } + boolean constantPool = !f.struct && f.referenceType != null; + type.add(PrivateAccess.getInstance().newValueDescriptor(f.name, fieldType, aes, f.array ? 1 : 0, constantPool, null)); + } + } + } + + private Map buildTypeMap() { + Map typeMap = new HashMap<>(); + for (Type type : Type.getKnownTypes()) { + typeMap.put(type.getName(), type); + } + + for (TypeElement t : types.values()) { + List aes = new ArrayList<>(); + if (t.category != null) { + aes.add(new AnnotationElement(Category.class, buildCategoryArray(t.category))); + } + if (t.label != null) { + aes.add(new AnnotationElement(Label.class, t.label)); + } + if (t.description != null) { + aes.add(new AnnotationElement(Description.class, t.description)); + } + if (t.isEvent) { + if (t.period != null) { + aes.add(new AnnotationElement(Period.class, t.period)); + } else { + if (t.startTime) { + aes.add(new AnnotationElement(Threshold.class, "0 ns")); + } + if (t.stackTrace) { + aes.add(new AnnotationElement(StackTrace.class, true)); + } + } + if (t.cutoff) { + aes.add(new AnnotationElement(Cutoff.class, Cutoff.INIFITY)); + } + } + if (t.experimental) { + aes.add(new AnnotationElement(Experimental.class)); + } + Type type; + if (t.isEvent) { + aes.add(new AnnotationElement(Enabled.class, false)); + type = new PlatformEventType(t.name, eventTypeId++, false, true); + } else { + // Struct types had their own XML-element in the past. To have id assigned in the + // same order as generated .hpp file do some tweaks here. + boolean valueType = t.name.endsWith("StackFrame") || t.valueType; + type = new Type(t.name, null, valueType ? eventTypeId++ : nextTypeId(t.name), false); + } + type.setAnnotations(aes); + typeMap.put(t.name, type); + } + return typeMap; + } + + private long nextTypeId(String name) { + if (Type.THREAD.getName().equals(name)) { + return Type.THREAD.getId(); + } + if (Type.STRING.getName().equals(name)) { + return Type.STRING.getId(); + } + if (Type.CLASS.getName().equals(name)) { + return Type.CLASS.getId(); + } + for (Type type : Type.getKnownTypes()) { + if (type.getName().equals(name)) { + return type.getId(); + } + } + return structTypeId++; + } + + private String[] buildCategoryArray(String category) { + List categories = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + for (char c : category.toCharArray()) { + if (c == ',') { + categories.add(sb.toString().trim()); + sb.setLength(0); + } else { + sb.append(c); + } + } + categories.add(sb.toString().trim()); + return categories.toArray(new String[0]); + } + + private void removeXMLConvenience() { + for (TypeElement t : types.values()) { + XmlType xmlType = xmlTypes.get(t.name); + if (xmlType != null && xmlType.javaType != null) { + t.name = xmlType.javaType; // known type, i.e primitive + } else { + if (t.isEvent) { + t.name = Type.EVENT_NAME_PREFIX + t.name; + } else { + t.name = Type.TYPES_PREFIX + t.name; + } + } + } + + for (TypeElement t : types.values()) { + for (FieldElement f : t.fields) { + f.referenceType = types.get(f.typeName); + XmlType xmlType = xmlTypes.get(f.typeName); + if (xmlType != null) { + if (xmlType.javaType != null) { + f.typeName = xmlType.javaType; + } + if (xmlType.contentType != null) { + f.contentType = xmlType.contentType; + } + if (xmlType.unsigned) { + f.unsigned = true; + } + } + if (f.struct && f.referenceType != null) { + f.referenceType.valueType = true; + } + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/MetadataReader.java 2019-02-08 18:32:31.974307045 +0300 @@ -0,0 +1,271 @@ +/* + * 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 static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_CONSTANT_POOL; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_DIMENSION; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_ID; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_NAME; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_SIMPLE_TYPE; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_SUPER_TYPE; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_TYPE_ID; +import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_ANNOTATION; +import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_FIELD; +import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_SETTING; +import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_TYPE; + +import java.io.DataInput; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.internal.MetadataDescriptor.Element; + +/** + * Parses metadata. + * + */ +final class MetadataReader { + + private final DataInput input; + private final List pool; + private final MetadataDescriptor descriptor; + private final Map types = new HashMap<>(); + + public MetadataReader(DataInput input) throws IOException { + this.input = input; + int size = input.readInt(); + this.pool = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + this.pool.add(input.readUTF()); + } + descriptor = new MetadataDescriptor(); + Element root = createElement(); + Element metadata = root.elements("metadata").get(0); + declareTypes(metadata); + defineTypes(metadata); + annotateTypes(metadata); + buildEvenTypes(); + Element time = root.elements("region").get(0); + descriptor.gmtOffset = time.attribute(MetadataDescriptor.ATTRIBUTE_GMT_OFFSET, 1); + descriptor.locale = time.attribute(MetadataDescriptor.ATTRIBUTE_LOCALE, ""); + descriptor.root = root; + if (LogTag.JFR_SYSTEM_PARSER.shouldLog(LogLevel.TRACE.level)) { + List ts = new ArrayList<>(types.values()); + Collections.sort(ts, (x,y) -> x.getName().compareTo(y.getName())); + for (Type t : ts) { + t.log("Found", LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE); + } + } + } + + private String readString() throws IOException { + return pool.get(readInt()); + } + + private int readInt() throws IOException { + return input.readInt(); + } + + private Element createElement() throws IOException { + String name = readString(); + Element e = new Element(name); + int attributeCount = readInt(); + for (int i = 0; i < attributeCount; i++) { + e.addAttribute(readString(), readString()); + } + int childrenCount = readInt(); + for (int i = 0; i < childrenCount; i++) { + e.add(createElement()); + } + return e; + } + + private void annotateTypes(Element metadata) throws IOException { + for (Element typeElement : metadata.elements(ELEMENT_TYPE)) { + Type type = getType(ATTRIBUTE_ID, typeElement); + ArrayList aes = new ArrayList<>(); + for (Element annotationElement : typeElement.elements(ELEMENT_ANNOTATION)) { + aes.add(makeAnnotation(annotationElement)); + } + aes.trimToSize(); + type.setAnnotations(aes); + + int index = 0; + if (type instanceof PlatformEventType) { + List settings = ((PlatformEventType) type).getAllSettings(); + for (Element settingElement : typeElement.elements(ELEMENT_SETTING)) { + ArrayList annotations = new ArrayList<>(); + for (Element annotationElement : settingElement.elements(ELEMENT_ANNOTATION)) { + annotations.add(makeAnnotation(annotationElement)); + } + annotations.trimToSize(); + PrivateAccess.getInstance().setAnnotations(settings.get(index), annotations); + index++; + } + } + index = 0; + List fields = type.getFields(); + for (Element fieldElement : typeElement.elements(ELEMENT_FIELD)) { + ArrayList annotations = new ArrayList<>(); + for (Element annotationElement : fieldElement.elements(ELEMENT_ANNOTATION)) { + annotations.add(makeAnnotation(annotationElement)); + } + annotations.trimToSize(); + PrivateAccess.getInstance().setAnnotations(fields.get(index), annotations); + index++; + } + } + } + + private AnnotationElement makeAnnotation(Element annotationElement) throws IOException { + Type annotationType = getType(ATTRIBUTE_TYPE_ID, annotationElement); + List values = new ArrayList<>(); + for (ValueDescriptor v : annotationType.getFields()) { + if (v.isArray()) { + List list = new ArrayList<>(); + int index = 0; + while (true) { + String text = annotationElement.attribute(v.getName() + "-" + index); + if (text == null) { + break; + } + list.add(objectify(v.getTypeName(), text)); + index++; + } + Object object = Utils.makePrimitiveArray(v.getTypeName(), list); + if (object == null) { + throw new IOException("Unsupported type " + list + " in array"); + } + values.add(object); + } else { + String text = annotationElement.attribute(v.getName()); + values.add(objectify(v.getTypeName(), text)); + } + } + return PrivateAccess.getInstance().newAnnotation(annotationType, values, false); + } + + private Object objectify(String typeName, String text) throws IOException { + try { + switch (typeName) { + case "int": + return Integer.valueOf(text); + case "long": + return Long.valueOf(text); + case "double": + return Double.valueOf(text); + case "float": + return Float.valueOf(text); + case "short": + return Short.valueOf(text); + case "char": + if (text.length() != 1) { + throw new IOException("Unexpected size of char"); + } + return text.charAt(0); + case "byte": + return Byte.valueOf(text); + case "boolean": + return Boolean.valueOf(text); + case "java.lang.String": + return text; + } + } catch (IllegalArgumentException iae) { + throw new IOException("Could not parse text representation of " + typeName); + } + throw new IOException("Unsupported type for annotation " + typeName); + } + + private Type getType(String attribute, Element element) { + long id = element.longValue(attribute); + Type type = types.get(id); + if (type == null) { + String name = element.attribute("type"); + throw new IllegalStateException("Type '" + id + "' is not defined for " + name); + } + return type; + } + + private void buildEvenTypes() { + for (Type type : descriptor.types) { + if (type instanceof PlatformEventType) { + descriptor.eventTypes.add(PrivateAccess.getInstance().newEventType((PlatformEventType) type)); + } + } + } + + private void defineTypes(Element metadata) { + for (Element typeElement : metadata.elements(ELEMENT_TYPE)) { + long id = typeElement.attribute(ATTRIBUTE_ID, -1); + Type t = types.get(id); + for (Element fieldElement : typeElement.elements(ELEMENT_SETTING)) { + String name = fieldElement.attribute(ATTRIBUTE_NAME); + String defaultValue = fieldElement.attribute(ATTRIBUTE_NAME); + Type settingType = getType(ATTRIBUTE_TYPE_ID, fieldElement); + PlatformEventType eventType = (PlatformEventType) t; + eventType.add(PrivateAccess.getInstance().newSettingDescriptor(settingType, name, defaultValue, new ArrayList<>(2))); + } + for (Element fieldElement : typeElement.elements(ELEMENT_FIELD)) { + String name = fieldElement.attribute(ATTRIBUTE_NAME); + Type fieldType = getType(ATTRIBUTE_TYPE_ID, fieldElement); + long dimension = fieldElement.attribute(ATTRIBUTE_DIMENSION, 0); + boolean constantPool = fieldElement.attribute(ATTRIBUTE_CONSTANT_POOL) != null; + // Add annotation later, because they may refer to undefined + // types at this stage + t.add(PrivateAccess.getInstance().newValueDescriptor(name, fieldType, new ArrayList<>(), (int) dimension, constantPool, null)); + } + t.trimFields(); + } + } + + private void declareTypes(Element metadata) { + for (Element typeElement : metadata.elements(ELEMENT_TYPE)) { + String typeName = typeElement.attribute(ATTRIBUTE_NAME); + String superType = typeElement.attribute(ATTRIBUTE_SUPER_TYPE); + boolean simpleType = typeElement.attribute(ATTRIBUTE_SIMPLE_TYPE) != null; + long id = typeElement.attribute(ATTRIBUTE_ID, -1); + Type t; + if (Type.SUPER_TYPE_EVENT.equals(superType)) { + t = new PlatformEventType(typeName, id, false, false); + } else { + t = new Type(typeName, superType, id, false, simpleType); + } + types.put(id, t); + descriptor.types.add(t); + } + } + + public MetadataDescriptor getDescriptor() { + return descriptor; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/MetadataRepository.java 2019-02-08 18:32:32.126301727 +0300 @@ -0,0 +1,277 @@ +/* + * 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 static jdk.jfr.internal.LogLevel.DEBUG; +import static jdk.jfr.internal.LogTag.JFR_SYSTEM; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Period; +import jdk.jfr.StackTrace; +import jdk.jfr.Threshold; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.internal.RequestEngine.RequestHook; +import jdk.jfr.internal.handlers.EventHandler; + +public final class MetadataRepository { + + private static final JVM jvm = JVM.getJVM(); + private static final MetadataRepository instace = new MetadataRepository(); + + private final List nativeEventTypes = new ArrayList<>(100); + private final List nativeControls = new ArrayList(100); + private final TypeLibrary typeLibrary = TypeLibrary.getInstance(); + private final SettingsManager settingsManager = new SettingsManager(); + private boolean staleMetadata = true; + private boolean unregistered; + private long lastUnloaded = -1; + + public MetadataRepository() { + initializeJVMEventTypes(); + } + + private void initializeJVMEventTypes() { + List requestHooks = new ArrayList<>(); + for (Type type : typeLibrary.getTypes()) { + if (type instanceof PlatformEventType) { + PlatformEventType pEventType = (PlatformEventType) type; + EventType eventType = PrivateAccess.getInstance().newEventType(pEventType); + pEventType.setHasDuration(eventType.getAnnotation(Threshold.class) != null); + pEventType.setHasStackTrace(eventType.getAnnotation(StackTrace.class) != null); + pEventType.setHasCutoff(eventType.getAnnotation(Cutoff.class) != null); + pEventType.setHasPeriod(eventType.getAnnotation(Period.class) != null); + // Must add hook before EventControl is created as it removes + // annotations, such as Period and Threshold. + if (pEventType.hasPeriod()) { + pEventType.setEventHook(true); + if (!(Type.EVENT_NAME_PREFIX + "ExecutionSample").equals(type.getName())) { + requestHooks.add(new RequestHook(pEventType)); + } + } + nativeControls.add(new EventControl(pEventType)); + nativeEventTypes.add(eventType); + } + } + RequestEngine.addHooks(requestHooks); + } + + public static MetadataRepository getInstance() { + return instace; + } + + public synchronized List getRegisteredEventTypes() { + List handlers = getEventHandlers(); + List eventTypes = new ArrayList<>(handlers.size() + nativeEventTypes.size()); + for (EventHandler h : handlers) { + if (h.isRegistered()) { + eventTypes.add(h.getEventType()); + } + } + eventTypes.addAll(nativeEventTypes); + return eventTypes; + } + + public synchronized EventType getEventType(Class eventClass) { + EventHandler h = getHandler(eventClass); + if (h != null && h.isRegistered()) { + return h.getEventType(); + } + throw new IllegalStateException("Event class " + eventClass.getName() + " is not registered"); + } + + public synchronized void unregister(Class eventClass) { + Utils.checkRegisterPermission(); + EventHandler handler = getHandler(eventClass); + if (handler != null) { + handler.setRegistered(false); + } + // never registered, ignore call + } + public synchronized EventType register(Class eventClass) { + return register(eventClass, Collections.emptyList(), Collections.emptyList()); + } + + public synchronized EventType register(Class eventClass, List dynamicAnnotations, List dynamicFields) { + Utils.checkRegisterPermission(); + EventHandler handler = getHandler(eventClass); + if (handler == null) { + handler = makeHandler(eventClass, dynamicAnnotations, dynamicFields); + } + handler.setRegistered(true); + typeLibrary.addType(handler.getPlatformEventType()); + if (jvm.isRecording()) { + storeDescriptorInJVM(); // needed for emergency dump + settingsManager.setEventControl(handler.getEventControl()); + settingsManager.updateRetransform(Collections.singletonList((eventClass))); + } else { + setStaleMetadata(); + } + return handler.getEventType(); + } + + private EventHandler getHandler(Class eventClass) { + Utils.ensureValidEventSubclass(eventClass); + SecuritySupport.makeVisibleToJFR(eventClass); + Utils.ensureInitialized(eventClass); + return Utils.getHandler(eventClass); + } + + private EventHandler makeHandler(Class eventClass, List dynamicAnnotations, List dynamicFields) throws InternalError { + SecuritySupport.addHandlerExport(eventClass); + PlatformEventType pEventType = (PlatformEventType) TypeLibrary.createType(eventClass, dynamicAnnotations, dynamicFields); + EventType eventType = PrivateAccess.getInstance().newEventType(pEventType); + EventControl ec = new EventControl(pEventType, eventClass); + Class handlerClass = null; + try { + String eventHandlerName = EventHandlerCreator.makeEventHandlerName(eventType.getId()); + handlerClass = Class.forName(eventHandlerName, false, Event.class.getClassLoader()).asSubclass(EventHandler.class); + // Created eagerly on class load, tag as instrumented + pEventType.setInstrumented(); + Logger.log(JFR_SYSTEM, DEBUG, "Found existing event handler for " + eventType.getName()); + } catch (ClassNotFoundException cne) { + EventHandlerCreator ehc = new EventHandlerCreator(eventType.getId(), ec.getSettingInfos(), eventType, eventClass); + handlerClass = ehc.makeEventHandlerClass(); + Logger.log(LogTag.JFR_SYSTEM, DEBUG, "Created event handler for " + eventType.getName()); + } + EventHandler handler = EventHandlerCreator.instantiateEventHandler(handlerClass, true, eventType, ec); + Utils.setHandler(eventClass, handler); + return handler; + } + + + public synchronized void setSettings(List> list) { + settingsManager.setSettings(list); + } + + synchronized void disableEvents() { + for (EventControl c : getEventControls()) { + c.disable(); + } + } + + public synchronized List getEventControls() { + List controls = new ArrayList<>(); + controls.addAll(nativeControls); + for (EventHandler eh : getEventHandlers()) { + controls.add(eh.getEventControl()); + } + return controls; + } + + private void storeDescriptorInJVM() throws InternalError { + jvm.storeMetadataDescriptor(getBinaryRepresentation()); + staleMetadata = false; + } + + private static List getEventHandlers() { + List> allEventClasses = jvm.getAllEventClasses(); + List eventHandlers = new ArrayList<>(allEventClasses.size()); + for (Class clazz : allEventClasses) { + EventHandler eh = Utils.getHandler(clazz); + if (eh != null) { + eventHandlers.add(eh); + } + } + return eventHandlers; + } + + private byte[] getBinaryRepresentation() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(40000); + DataOutputStream daos = new DataOutputStream(baos); + try { + List types = typeLibrary.getTypes(); + Collections.sort(types); + MetadataDescriptor.write(types, daos); + daos.flush(); + return baos.toByteArray(); + } catch (IOException e) { + // should not happen + throw new InternalError(e); + } + } + + synchronized boolean isEnabled(String eventName) { + return settingsManager.isEnabled(eventName); + } + + synchronized void setStaleMetadata() { + staleMetadata = true; + } + + // Lock around setOutput ensures that other threads dosn't + // emit event after setOutput and unregister the event class, before a call + // to storeDescriptorInJVM + synchronized void setOutput(String filename) { + jvm.setOutput(filename); + + unregisterUnloaded(); + if (unregistered) { + staleMetadata = typeLibrary.clearUnregistered(); + unregistered = false; + } + if (staleMetadata) { + storeDescriptorInJVM(); + } + } + + private void unregisterUnloaded() { + long unloaded = jvm.getUnloadedEventClassCount(); + if (this.lastUnloaded != unloaded) { + this.lastUnloaded = unloaded; + List> eventClasses = jvm.getAllEventClasses(); + HashSet knownIds = new HashSet<>(eventClasses.size()); + for (Class ec: eventClasses) { + knownIds.add(Type.getTypeId(ec)); + } + for (Type type : typeLibrary.getTypes()) { + if (type instanceof PlatformEventType) { + if (!knownIds.contains(type.getId())) { + PlatformEventType pe = (PlatformEventType) type; + if (!pe.isJVM()) { + pe.setRegistered(false); + } + } + } + } + } + } + + synchronized public void setUnregistered() { + unregistered = true; + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/MetadataWriter.java 2019-02-08 18:32:32.270296690 +0300 @@ -0,0 +1,225 @@ +/* + * 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 static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_CONSTANT_POOL; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_DEFAULT_VALUE; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_DIMENSION; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_GMT_OFFSET; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_ID; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_LOCALE; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_NAME; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_SIMPLE_TYPE; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_SUPER_TYPE; +import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_TYPE_ID; +import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_ANNOTATION; +import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_FIELD; +import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_SETTING; +import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_TYPE; + +import java.io.DataOutput; +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Set; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.internal.MetadataDescriptor.Attribute; +import jdk.jfr.internal.MetadataDescriptor.Element; +import jdk.jfr.internal.consumer.RecordingInput; + +/** + * Class responsible for converting a list of types into a format that can be + * parsed by a client. + * + */ +final class MetadataWriter { + + private final Element metadata = new Element("metadata"); + private final Element root = new Element("root"); + + public MetadataWriter(MetadataDescriptor descriptor) { + descriptor.getTypes().forEach(type -> makeTypeElement(metadata, type)); + + root.add(metadata); + Element region = new Element("region"); + region.addAttribute(ATTRIBUTE_LOCALE, descriptor.locale); + region.addAttribute(ATTRIBUTE_GMT_OFFSET, descriptor.gmtOffset); + root.add(region); + } + + public void writeBinary(DataOutput output) throws IOException { + Set stringPool = new HashSet<>(1000); + // Possible improvement, sort string by how often they occur. + // and assign low number to the most frequently used. + buildStringPool(root, stringPool); + HashMap lookup = new LinkedHashMap<>(stringPool.size()); + int index = 0; + int poolSize = stringPool.size(); + writeInt(output, poolSize); + for (String s : stringPool) { + lookup.put(s, index); + writeString(output, s); + index++; + } + write(output, root, lookup); + } + + private void writeString(DataOutput out, String s) throws IOException { + if (s == null ) { + out.writeByte(RecordingInput.STRING_ENCODING_NULL); + return; + } + out.writeByte(RecordingInput.STRING_ENCODING_CHAR_ARRAY); // encoding UTF-16 + int length = s.length(); + writeInt(out, length); + for (int i = 0; i < length; i++) { + writeInt(out, s.charAt(i)); + } + } + + private void writeInt(DataOutput out, int v) throws IOException { + + long s = v & 0xffffffffL; + if (s < 1 << 7) { + out.write((byte) (s)); + return; + } + out.write((byte) (s | 0x80)); // first byte written + s >>= 7; + if (s < 1 << 7) { + out.write((byte) (s)); + return; + } + out.write((byte) (s | 0x80)); // second byte written + s >>= 7; + if (s < 1 << 7) { + out.write((byte) (s)); + return; + } + out.write((byte) (s | 0x80)); // third byte written + s >>= 7; + if (s < 1 << 7) { + out.write((byte) (s)); + return; + } + s >>= 7; + out.write((byte) (s));// fourth byte written + } + + private void buildStringPool(Element element, Set pool) { + pool.add(element.name); + for (Attribute a : element.attributes) { + pool.add(a.name); + pool.add(a.value); + } + for (Element child : element.elements) { + buildStringPool(child, pool); + } + } + + private void write(DataOutput output,Element element, HashMap lookup) throws IOException { + writeInt(output, lookup.get(element.name)); + writeInt(output, element.attributes.size()); + for (Attribute a : element.attributes) { + writeInt(output, lookup.get(a.name)); + writeInt(output, lookup.get(a.value)); + } + writeInt(output, element.elements.size()); + for (Element child : element.elements) { + write(output, child, lookup); + } + } + + private void makeTypeElement(Element root, Type type) { + Element element = root.newChild(ELEMENT_TYPE); + element.addAttribute(ATTRIBUTE_NAME, type.getName()); + String superType = type.getSuperType(); + if (superType != null) { + element.addAttribute(ATTRIBUTE_SUPER_TYPE, superType); + } + if (type.isSimpleType()) { + element.addAttribute(ATTRIBUTE_SIMPLE_TYPE, true); + } + element.addAttribute(ATTRIBUTE_ID, type.getId()); + if (type instanceof PlatformEventType) { + for (SettingDescriptor v : ((PlatformEventType)type).getSettings()) { + makeSettingElement(element, v); + } + } + for (ValueDescriptor v : type.getFields()) { + makeFieldElement(element, v); + } + for (AnnotationElement a : type.getAnnotationElements()) { + makeAnnotation(element, a); + } + } + + private void makeSettingElement(Element typeElement, SettingDescriptor s) { + Element element = typeElement.newChild(ELEMENT_SETTING); + element.addAttribute(ATTRIBUTE_NAME, s.getName()); + element.addAttribute(ATTRIBUTE_TYPE_ID, s.getTypeId()); + element.addAttribute(ATTRIBUTE_DEFAULT_VALUE, s.getDefaultValue()); + for (AnnotationElement a : s.getAnnotationElements()) { + makeAnnotation(element, a); + } + } + + private void makeFieldElement(Element typeElement, ValueDescriptor v) { + Element element = typeElement.newChild(ELEMENT_FIELD); + element.addAttribute(ATTRIBUTE_NAME, v.getName()); + element.addAttribute(ATTRIBUTE_TYPE_ID, v.getTypeId()); + if (v.isArray()) { + element.addAttribute(ATTRIBUTE_DIMENSION, 1); + } + if (PrivateAccess.getInstance().isConstantPool(v)) { + element.addAttribute(ATTRIBUTE_CONSTANT_POOL, true); + } + for (AnnotationElement a : v.getAnnotationElements()) { + makeAnnotation(element, a); + } + } + + private void makeAnnotation(Element entity, AnnotationElement annotation) { + Element element = entity.newChild(ELEMENT_ANNOTATION); + element.addAttribute(ATTRIBUTE_TYPE_ID, annotation.getTypeId()); + List values = annotation.getValues(); + int index = 0; + for (ValueDescriptor v : annotation.getValueDescriptors()) { + Object value = values.get(index++); + if (v.isArray()) { + element.addArrayAttribute(element, v.getName(), value); + } else { + element.addAttribute(v.getName(), value); + } + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/OldObjectSample.java 2019-02-08 18:32:32.418291512 +0300 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 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.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.Enabled; +import jdk.jfr.RecordingState; +import jdk.jfr.internal.settings.CutoffSetting; +import jdk.jfr.internal.test.WhiteBox; + +// The Old Object event could have been implemented as a periodic event, but +// due to chunk rotations and how settings are calculated when multiple recordings +// are running at the same time, it would lead to unacceptable overhead. +// +// Instead, the event is only emitted before a recording stops and +// if that recording has the event enabled. +// +// This requires special handling and the purpose of this class is to provide that +// +public final class OldObjectSample { + + private static final String EVENT_NAME = Type.EVENT_NAME_PREFIX + "OldObjectSample"; + private static final String OLD_OBJECT_CUTOFF = EVENT_NAME + "#" + Cutoff.NAME; + private static final String OLD_OBJECT_ENABLED = EVENT_NAME + "#" + Enabled.NAME; + + // Emit if old object is enabled in recoding with cutoff for that recording + public static void emit(PlatformRecording recording) { + if (isEnabled(recording)) { + long nanos = CutoffSetting.parseValueSafe(recording.getSettings().get(OLD_OBJECT_CUTOFF)); + long ticks = Utils.nanosToTicks(nanos); + JVM.getJVM().emitOldObjectSamples(ticks, WhiteBox.getWriteAllObjectSamples()); + } + } + + // Emit if old object is enabled for at least one recording, and use the largest + // cutoff for an enabled recoding + public static void emit(List recordings, Boolean pathToGcRoots) { + boolean enabled = false; + long cutoffNanos = Boolean.TRUE.equals(pathToGcRoots) ? Long.MAX_VALUE : 0L; + for (PlatformRecording r : recordings) { + if (r.getState() == RecordingState.RUNNING) { + if (isEnabled(r)) { + enabled = true; + long c = CutoffSetting.parseValueSafe(r.getSettings().get(OLD_OBJECT_CUTOFF)); + cutoffNanos = Math.max(c, cutoffNanos); + } + } + } + if (enabled) { + long ticks = Utils.nanosToTicks(cutoffNanos); + JVM.getJVM().emitOldObjectSamples(ticks, WhiteBox.getWriteAllObjectSamples()); + } + } + + public static void updateSettingPathToGcRoots(Map s, Boolean pathToGcRoots) { + if (pathToGcRoots != null) { + s.put(OLD_OBJECT_CUTOFF, pathToGcRoots ? "infinity" : "0 ns"); + } + } + + public static Map createSettingsForSnapshot(PlatformRecording recording, Boolean pathToGcRoots) { + Map settings = new HashMap<>(recording.getSettings()); + updateSettingPathToGcRoots(settings, pathToGcRoots); + return settings; + } + + private static boolean isEnabled(PlatformRecording r) { + Map settings = r.getSettings(); + String s = settings.get(OLD_OBJECT_ENABLED); + return "true".equals(s); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/Options.java 2019-02-08 18:32:32.562286474 +0300 @@ -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 jdk.jfr.internal.SecuritySupport.SafePath; +import sun.misc.Unsafe; + +/** + * Options that control Flight Recorder. + * + * Can be set using JFR.configure + * + */ +public final class Options { + + private final static JVM jvm = JVM.getJVM(); + private final static long WAIT_INTERVAL = 1000; // ms; + + private final static long MIN_MAX_CHUNKSIZE = 1024 * 1024; + + private static final long DEFAULT_GLOBAL_BUFFER_COUNT = 20; + private static final long DEFAULT_GLOBAL_BUFFER_SIZE = 524288; + private static final long DEFAULT_MEMORY_SIZE = DEFAULT_GLOBAL_BUFFER_COUNT * DEFAULT_GLOBAL_BUFFER_SIZE; + private static long DEFAULT_THREAD_BUFFER_SIZE; + private static final int DEFAULT_STACK_DEPTH = 64; + private static final boolean DEFAULT_SAMPLE_THREADS = true; + private static final long DEFAULT_MAX_CHUNK_SIZE = 12 * 1024 * 1024; + private static final SafePath DEFAULT_DUMP_PATH = SecuritySupport.USER_HOME; + + private static long memorySize; + private static long globalBufferSize; + private static long globalBufferCount; + private static long threadBufferSize; + private static int stackDepth; + private static boolean sampleThreads; + private static long maxChunkSize; + private static SafePath dumpPath; + + static { + final long pageSize = Unsafe.getUnsafe().pageSize(); + DEFAULT_THREAD_BUFFER_SIZE = pageSize > 8 * 1024 ? pageSize : 8 * 1024; + reset(); + } + + public static synchronized void setMaxChunkSize(long max) { + if (max < MIN_MAX_CHUNKSIZE) { + throw new IllegalArgumentException("Max chunk size must be at least " + MIN_MAX_CHUNKSIZE); + } + jvm.setFileNotification(max); + maxChunkSize = max; + } + + public static synchronized long getMaxChunkSize() { + return maxChunkSize; + } + + public static synchronized void setMemorySize(long memSize) { + jvm.setMemorySize(memSize); + memorySize = memSize; + } + + public static synchronized long getMemorySize() { + return memorySize; + } + + public static synchronized void setThreadBufferSize(long threadBufSize) { + jvm.setThreadBufferSize(threadBufSize); + threadBufferSize = threadBufSize; + } + + public static synchronized long getThreadBufferSize() { + return threadBufferSize; + } + + public static synchronized long getGlobalBufferSize() { + return globalBufferSize; + } + + public static synchronized void setGlobalBufferCount(long globalBufCount) { + jvm.setGlobalBufferCount(globalBufCount); + globalBufferCount = globalBufCount; + } + + public static synchronized long getGlobalBufferCount() { + return globalBufferCount; + } + + public static synchronized void setGlobalBufferSize(long globalBufsize) { + jvm.setGlobalBufferSize(globalBufsize); + globalBufferSize = globalBufsize; + } + + public static synchronized void setDumpPath(SafePath path) { + dumpPath = path; + } + + public static synchronized SafePath getDumpPath() { + return dumpPath; + } + + public static synchronized void setStackDepth(Integer stackTraceDepth) { + jvm.setStackDepth(stackTraceDepth); + stackDepth = stackTraceDepth; + } + + public static synchronized int getStackDepth() { + return stackDepth; + } + + public static synchronized void setSampleThreads(Boolean sample) { + jvm.setSampleThreads(sample); + sampleThreads = sample; + } + + public static synchronized boolean getSampleThreads() { + return sampleThreads; + } + + private static synchronized void reset() { + setMaxChunkSize(DEFAULT_MAX_CHUNK_SIZE); + setMemorySize(DEFAULT_MEMORY_SIZE); + setGlobalBufferSize(DEFAULT_GLOBAL_BUFFER_SIZE); + setGlobalBufferCount(DEFAULT_GLOBAL_BUFFER_COUNT); + setDumpPath(DEFAULT_DUMP_PATH); + setSampleThreads(DEFAULT_SAMPLE_THREADS); + setStackDepth(DEFAULT_STACK_DEPTH); + setThreadBufferSize(DEFAULT_THREAD_BUFFER_SIZE); + } + + static synchronized long getWaitInterval() { + return WAIT_INTERVAL; + } + + static void ensureInitialized() { + // trigger clinit which will setup JVM defaults. + } + + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/PlatformEventType.java 2019-02-08 18:32:32.710281297 +0300 @@ -0,0 +1,281 @@ +/* + * 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.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import jdk.jfr.SettingDescriptor; + +/** + * Implementation of event type. + * + * To avoid memory leaks, this class must not hold strong reference to an event + * class or a setting class + */ +public final class PlatformEventType extends Type { + private final boolean isJVM; + private final boolean isJDK; + private final boolean isMethodSampling; + private final List settings = new ArrayList<>(5); + private final boolean dynamicSettings; + private final int stackTraceOffset; + + // default values + private boolean enabled = false; + private boolean stackTraceEnabled = true; + private long thresholdTicks = 0; + private long period = 0; + private boolean hasHook; + + private boolean beginChunk; + private boolean endChunk; + private boolean hasStackTrace = true; + private boolean hasDuration = true; + private boolean hasPeriod = true; + private boolean hasCutoff = false; + private boolean isInstrumented; + private boolean markForInstrumentation; + private boolean registered = true; + private boolean commitable = enabled && registered; + + + // package private + PlatformEventType(String name, long id, boolean isJDK, boolean dynamicSettings) { + super(name, Type.SUPER_TYPE_EVENT, id); + this.dynamicSettings = dynamicSettings; + this.isJVM = Type.isDefinedByJVM(id); + this.isMethodSampling = name.equals(Type.EVENT_NAME_PREFIX + "ExecutionSample") || name.equals(Type.EVENT_NAME_PREFIX + "NativeMethodSample"); + this.isJDK = isJDK; + this.stackTraceOffset = stackTraceOffset(name, isJDK); + } + + private static int stackTraceOffset(String name, boolean isJDK) { + if (isJDK) { + if (name.equals(Type.EVENT_NAME_PREFIX + "JavaExceptionThrow")) { + return 5; + } + if (name.equals(Type.EVENT_NAME_PREFIX + "JavaErrorThrow")) { + return 5; + } + } + return 4; + } + + public void add(SettingDescriptor settingDescriptor) { + Objects.requireNonNull(settingDescriptor); + settings.add(settingDescriptor); + } + + public List getSettings() { + if (dynamicSettings) { + List list = new ArrayList<>(settings.size()); + for (SettingDescriptor s : settings) { + if (Utils.isSettingVisible(s.getTypeId(), hasHook)) { + list.add(s); + } + } + return list; + } + return settings; + } + + public List getAllSettings() { + return settings; + } + + public void setHasStackTrace(boolean hasStackTrace) { + this.hasStackTrace = hasStackTrace; + } + + public void setHasDuration(boolean hasDuration) { + this.hasDuration = hasDuration; + } + + public void setHasCutoff(boolean hasCutoff) { + this.hasCutoff = hasCutoff; + } + + public void setCutoff(long cutoffNanos) { + if (isJVM) { + long cutoffTicks = Utils.nanosToTicks(cutoffNanos); + JVM.getJVM().setCutoff(getId(), cutoffTicks); + } + } + + public void setHasPeriod(boolean hasPeriod) { + this.hasPeriod = hasPeriod; + } + + public boolean hasStackTrace() { + return this.hasStackTrace; + } + + public boolean hasDuration() { + return this.hasDuration; + } + + public boolean hasPeriod() { + return this.hasPeriod; + } + + public boolean hasCutoff() { + return this.hasCutoff; + } + + public boolean isEnabled() { + return enabled; + } + + public boolean isJVM() { + return isJVM; + } + + public boolean isJDK() { + return isJDK; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + updateCommitable(); + if (isJVM) { + if (isMethodSampling) { + long p = enabled ? period : 0; + JVM.getJVM().setMethodSamplingInterval(getId(), p); + } else { + JVM.getJVM().setEnabled(getId(), enabled); + } + } + } + + public void setPeriod(long periodMillis, boolean beginChunk, boolean endChunk) { + if (isMethodSampling) { + long p = enabled ? periodMillis : 0; + JVM.getJVM().setMethodSamplingInterval(getId(), p); + } + this.beginChunk = beginChunk; + this.endChunk = endChunk; + this.period = periodMillis; + } + + public void setStackTraceEnabled(boolean stackTraceEnabled) { + this.stackTraceEnabled = stackTraceEnabled; + if (isJVM) { + JVM.getJVM().setStackTraceEnabled(getId(), stackTraceEnabled); + } + } + + public void setThreshold(long thresholdNanos) { + this.thresholdTicks = Utils.nanosToTicks(thresholdNanos); + if (isJVM) { + JVM.getJVM().setThreshold(getId(), thresholdTicks); + } + } + + public boolean isEveryChunk() { + return period == 0; + } + + public boolean getStackTraceEnabled() { + return stackTraceEnabled; + } + + public long getThresholdTicks() { + return thresholdTicks; + } + + public long getPeriod() { + return period; + } + + public boolean hasEventHook() { + return hasHook; + } + + public void setEventHook(boolean hasHook) { + this.hasHook = hasHook; + } + + public boolean isBeginChunk() { + return beginChunk; + } + + public boolean isEndChunk() { + return endChunk; + } + + public boolean isInstrumented() { + return isInstrumented; + } + + public void setInstrumented() { + isInstrumented = true; + } + + public void markForInstrumentation(boolean markForInstrumentation) { + this.markForInstrumentation = markForInstrumentation; + } + + public boolean isMarkedForInstrumentation() { + return markForInstrumentation; + } + + public boolean setRegistered(boolean registered) { + if (this.registered != registered) { + this.registered = registered; + updateCommitable(); + LogTag logTag = isJVM() || isJDK() ? LogTag.JFR_SYSTEM_EVENT : LogTag.JFR_EVENT; + if (registered) { + Logger.log(logTag, LogLevel.INFO, "Registered " + getLogName()); + } else { + Logger.log(logTag, LogLevel.INFO, "Unregistered " + getLogName()); + } + if (!registered) { + MetadataRepository.getInstance().setUnregistered(); + } + return true; + } + return false; + } + + private void updateCommitable() { + this.commitable = enabled && registered; + } + + public final boolean isRegistered() { + return registered; + } + + // Efficient check of enabled && registered + public boolean isCommitable() { + return commitable; + } + + public int getStackTraceOffset() { + return stackTraceOffset; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/PlatformRecorder.java 2019-02-08 18:32:32.858276119 +0300 @@ -0,0 +1,560 @@ +/* + * 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 static jdk.jfr.internal.LogLevel.INFO; +import static jdk.jfr.internal.LogLevel.TRACE; +import static jdk.jfr.internal.LogLevel.WARN; +import static jdk.jfr.internal.LogTag.JFR; +import static jdk.jfr.internal.LogTag.JFR_SYSTEM; + +import java.io.IOException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.CopyOnWriteArrayList; + +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.FlightRecorderListener; +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.jfr.events.ActiveRecordingEvent; +import jdk.jfr.events.ActiveSettingEvent; +import jdk.jfr.internal.SecuritySupport.SecureRecorderListener; +import jdk.jfr.internal.instrument.JDKEvents; + +public final class PlatformRecorder { + + private final List recordings = new ArrayList<>(); + private final static List changeListeners = new ArrayList<>(); + private final Repository repository; + private final Timer timer; + private final static JVM jvm = JVM.getJVM(); + private final EventType activeRecordingEvent; + private final EventType activeSettingEvent; + private final Thread shutdownHook; + + private long recordingCounter = 0; + private RepositoryChunk currentChunk; + + public PlatformRecorder() throws Exception { + repository = Repository.getRepository(); + Logger.log(JFR_SYSTEM, INFO, "Initialized disk repository"); + repository.ensureRepository(); + jvm.createNativeJFR(); + Logger.log(JFR_SYSTEM, INFO, "Created native"); + JDKEvents.initialize(); + Logger.log(JFR_SYSTEM, INFO, "Registered JDK events"); + JDKEvents.addInstrumentation(); + startDiskMonitor(); + SecuritySupport.registerEvent(ActiveRecordingEvent.class); + activeRecordingEvent = EventType.getEventType(ActiveRecordingEvent.class); + SecuritySupport.registerEvent(ActiveSettingEvent.class); + activeSettingEvent = EventType.getEventType(ActiveSettingEvent.class); + shutdownHook = SecuritySupport.createThreadWitNoPermissions("JFR: Shutdown Hook", new ShutdownHook(this)); + SecuritySupport.setUncaughtExceptionHandler(shutdownHook, new ShutdownHook.ExceptionHandler()); + SecuritySupport.registerShutdownHook(shutdownHook); + timer = createTimer(); + } + + private static Timer createTimer() { + try { + List result = new CopyOnWriteArrayList<>(); + Thread t = SecuritySupport.createThreadWitNoPermissions("Permissionless thread", ()-> { + result.add(new Timer("JFR Recording Scheduler", true)); + }); + t.start(); + t.join(); + return result.get(0); + } catch (InterruptedException e) { + throw new IllegalStateException("Not able to create timer task. " + e.getMessage(), e); + } + } + + public synchronized PlatformRecording newRecording(Map settings) { + return newRecording(settings, ++recordingCounter); + } + + // To be used internally when doing dumps. + // Caller must have recorder lock and close recording before releasing lock + public PlatformRecording newTemporaryRecording() { + if(!Thread.holdsLock(this)) { + throw new InternalError("Caller must have recorder lock"); + } + return newRecording(new HashMap<>(), 0); + } + + private synchronized PlatformRecording newRecording(Map settings, long id) { + PlatformRecording recording = new PlatformRecording(this, id); + if (!settings.isEmpty()) { + recording.setSettings(settings); + } + recordings.add(recording); + return recording; + } + + synchronized void finish(PlatformRecording recording) { + if (recording.getState() == RecordingState.RUNNING) { + recording.stop("Recording closed"); + } + recordings.remove(recording); + } + + public synchronized List getRecordings() { + return Collections.unmodifiableList(new ArrayList(recordings)); + } + + public synchronized static void addListener(FlightRecorderListener changeListener) { + AccessControlContext context = AccessController.getContext(); + SecureRecorderListener sl = new SecureRecorderListener(context, changeListener); + boolean runInitialized; + synchronized (PlatformRecorder.class) { + runInitialized = FlightRecorder.isInitialized(); + changeListeners.add(sl); + } + if (runInitialized) { + sl.recorderInitialized(FlightRecorder.getFlightRecorder()); + } + } + + public synchronized static boolean removeListener(FlightRecorderListener changeListener) { + for (SecureRecorderListener s : new ArrayList<>(changeListeners)) { + if (s.getChangeListener() == changeListener) { + changeListeners.remove(s); + return true; + } + } + return false; + } + + static synchronized List getListeners() { + return new ArrayList<>(changeListeners); + } + + Timer getTimer() { + return timer; + } + + public static void notifyRecorderInitialized(FlightRecorder recorder) { + Logger.log(JFR_SYSTEM, TRACE, "Notifying listeners that Flight Recorder is initialized"); + for (FlightRecorderListener r : getListeners()) { + r.recorderInitialized(recorder); + } + } + + // called by shutdown hook + synchronized void destroy() { + try { + timer.cancel(); + } catch (Exception ex) { + Logger.log(JFR_SYSTEM, WARN, "Shutdown hook could not cancel timer"); + } + + for (PlatformRecording p : getRecordings()) { + if (p.getState() == RecordingState.RUNNING) { + try { + p.stop("Shutdown"); + } catch (Exception ex) { + Logger.log(JFR, WARN, "Recording " + p.getName() + ":" + p.getId() + " could not be stopped"); + } + } + } + + JDKEvents.remove(); + + if (jvm.hasNativeJFR()) { + if (jvm.isRecording()) { + jvm.endRecording_(); + } + jvm.destroyNativeJFR(); + } + repository.clear(); + } + + synchronized void start(PlatformRecording recording) { + // State can only be NEW or DELAYED because of previous checks + Instant now = Instant.now(); + recording.setStartTime(now); + recording.updateTimer(); + Duration duration = recording.getDuration(); + if (duration != null) { + recording.setStopTime(now.plus(duration)); + } + boolean toDisk = recording.isToDisk(); + boolean beginPhysical = true; + for (PlatformRecording s : getRecordings()) { + if (s.getState() == RecordingState.RUNNING) { + beginPhysical = false; + if (s.isToDisk()) { + toDisk = true; + } + } + } + if (beginPhysical) { + RepositoryChunk newChunk = null; + if (toDisk) { + newChunk = repository.newChunk(now); + MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString()); + } else { + MetadataRepository.getInstance().setOutput(null); + } + currentChunk = newChunk; + jvm.beginRecording_(); + recording.setState(RecordingState.RUNNING); + updateSettings(); + writeMetaEvents(); + } else { + RepositoryChunk newChunk = null; + if (toDisk) { + newChunk = repository.newChunk(now); + RequestEngine.doChunkEnd(); + MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString()); + } + recording.setState(RecordingState.RUNNING); + updateSettings(); + writeMetaEvents(); + if (currentChunk != null) { + finishChunk(currentChunk, now, recording); + } + currentChunk = newChunk; + } + + RequestEngine.doChunkBegin(); + } + + synchronized void stop(PlatformRecording recording) { + RecordingState state = recording.getState(); + + if (Utils.isAfter(state, RecordingState.RUNNING)) { + throw new IllegalStateException("Can't stop an already stopped recording."); + } + if (Utils.isBefore(state, RecordingState.RUNNING)) { + throw new IllegalStateException("Recording must be started before it can be stopped."); + } + Instant now = Instant.now(); + boolean toDisk = false; + boolean endPhysical = true; + for (PlatformRecording s : getRecordings()) { + RecordingState rs = s.getState(); + if (s != recording && RecordingState.RUNNING == rs) { + endPhysical = false; + if (s.isToDisk()) { + toDisk = true; + } + } + } + OldObjectSample.emit(recording); + + if (endPhysical) { + RequestEngine.doChunkEnd(); + if (recording.isToDisk()) { + if (currentChunk != null) { + MetadataRepository.getInstance().setOutput(null); + finishChunk(currentChunk, now, null); + currentChunk = null; + } + } else { + // last memory + dumpMemoryToDestination(recording); + } + jvm.endRecording_(); + disableEvents(); + } else { + RepositoryChunk newChunk = null; + RequestEngine.doChunkEnd(); + updateSettingsButIgnoreRecording(recording); + if (toDisk) { + newChunk = repository.newChunk(now); + MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString()); + } else { + MetadataRepository.getInstance().setOutput(null); + } + writeMetaEvents(); + if (currentChunk != null) { + finishChunk(currentChunk, now, null); + } + currentChunk = newChunk; + RequestEngine.doChunkBegin(); + } + recording.setState(RecordingState.STOPPED); + } + + private void dumpMemoryToDestination(PlatformRecording recording) { + WriteableUserPath dest = recording.getDestination(); + if (dest != null) { + MetadataRepository.getInstance().setOutput(dest.getText()); + recording.clearDestination(); + } + } + private void disableEvents() { + MetadataRepository.getInstance().disableEvents(); + } + + void updateSettings() { + updateSettingsButIgnoreRecording(null); + } + + void updateSettingsButIgnoreRecording(PlatformRecording ignoreMe) { + List recordings = getRunningRecordings(); + List> list = new ArrayList<>(recordings.size()); + for (PlatformRecording r : recordings) { + if (r != ignoreMe) { + list.add(r.getSettings()); + } + } + MetadataRepository.getInstance().setSettings(list); + } + + synchronized void rotateDisk() { + Instant now = Instant.now(); + RepositoryChunk newChunk = repository.newChunk(now); + RequestEngine.doChunkEnd(); + MetadataRepository.getInstance().setOutput(newChunk.getUnfishedFile().toString()); + writeMetaEvents(); + if (currentChunk != null) { + finishChunk(currentChunk, now, null); + } + currentChunk = newChunk; + RequestEngine.doChunkBegin(); + } + + private List getRunningRecordings() { + List runningRecordings = new ArrayList<>(); + for (PlatformRecording recording : getRecordings()) { + if (recording.getState() == RecordingState.RUNNING) { + runningRecordings.add(recording); + } + } + return runningRecordings; + } + + private List makeChunkList(Instant startTime, Instant endTime) { + Set chunkSet = new HashSet<>(); + for (PlatformRecording r : getRecordings()) { + chunkSet.addAll(r.getChunks()); + } + if (chunkSet.size() > 0) { + List chunks = new ArrayList<>(chunkSet.size()); + for (RepositoryChunk rc : chunkSet) { + if (rc.inInterval(startTime, endTime)) { + chunks.add(rc); + } + } + // n*log(n), should be able to do n*log(k) with a priority queue, + // where k = number of recordings, n = number of chunks + Collections.sort(chunks, RepositoryChunk.END_TIME_COMPARATOR); + return chunks; + } + + return Collections.emptyList(); + } + + private void startDiskMonitor() { + Thread t = SecuritySupport.createThreadWitNoPermissions("JFR Periodic Tasks", () -> periodicTask()); + SecuritySupport.setDaemonThread(t, true); + t.start(); + } + + private void finishChunk(RepositoryChunk chunk, Instant time, PlatformRecording ignoreMe) { + chunk.finish(time); + for (PlatformRecording r : getRecordings()) { + if (r != ignoreMe && r.getState() == RecordingState.RUNNING) { + r.appendChunk(chunk); + } + } + } + + private void writeMetaEvents() { + + if (activeRecordingEvent.isEnabled()) { + for (PlatformRecording r : getRecordings()) { + if (r.getState() == RecordingState.RUNNING && r.shouldWriteMetadataEvent()) { + ActiveRecordingEvent event = new ActiveRecordingEvent(); + event.id = r.getId(); + event.name = r.getName(); + WriteableUserPath p = r.getDestination(); + event.destination = p == null ? null : p.getText(); + Duration d = r.getDuration(); + event.recordingDuration = d == null ? Long.MAX_VALUE : d.toMillis(); + Duration age = r.getMaxAge(); + event.maxAge = age == null ? Long.MAX_VALUE : age.toMillis(); + Long size = r.getMaxSize(); + event.maxSize = size == null ? Long.MAX_VALUE : size; + Instant start = r.getStartTime(); + event.recordingStart = start == null ? Long.MAX_VALUE : start.toEpochMilli(); + event.commit(); + } + } + } + if (activeSettingEvent.isEnabled()) { + for (EventControl ec : MetadataRepository.getInstance().getEventControls()) { + ec.writeActiveSettingEvent(); + } + } + } + + private void periodicTask() { + while (true) { + synchronized (this) { + if (!jvm.hasNativeJFR()) { + return; + } + if (currentChunk != null) { + try { + if (SecuritySupport.getFileSize(currentChunk.getUnfishedFile()) > Options.getMaxChunkSize()) { + rotateDisk(); + } + } catch (IOException e) { + Logger.log(JFR_SYSTEM, WARN, "Could not check file size to determine chunk rotation"); + } + } + } + long minDelta = RequestEngine.doPeriodic(); + long wait = Math.min(minDelta, Options.getWaitInterval()); + takeNap(wait); + } + } + + private void takeNap(long duration) { + try { + synchronized (JVM.FILE_DELTA_CHANGE) { + JVM.FILE_DELTA_CHANGE.wait(duration < 10 ? 10 : duration); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + synchronized Recording newCopy(PlatformRecording r, boolean stop) { + Recording newRec = new Recording(); + PlatformRecording copy = PrivateAccess.getInstance().getPlatformRecording(newRec); + copy.setSettings(r.getSettings()); + copy.setMaxAge(r.getMaxAge()); + copy.setMaxSize(r.getMaxSize()); + copy.setDumpOnExit(r.getDumpOnExit()); + copy.setName("Clone of " + r.getName()); + copy.setToDisk(r.isToDisk()); + copy.setInternalDuration(r.getDuration()); + copy.setStartTime(r.getStartTime()); + copy.setStopTime(r.getStopTime()); + + if (r.getState() == RecordingState.NEW) { + return newRec; + } + if (r.getState() == RecordingState.DELAYED) { + copy.scheduleStart(r.getStartTime()); + return newRec; + } + copy.setState(r.getState()); + // recording has started, copy chunks + for (RepositoryChunk c : r.getChunks()) { + copy.add(c); + } + if (r.getState() == RecordingState.RUNNING) { + if (stop) { + copy.stop("Stopped when cloning recording '" + r.getName() + "'"); + } else { + if (r.getStopTime() != null) { + TimerTask stopTask = copy.createStopTask(); + copy.setStopTask(copy.createStopTask()); + getTimer().schedule(stopTask, r.getStopTime().toEpochMilli()); + } + } + } + return newRec; + } + + public synchronized void fillWithRecordedData(PlatformRecording target, Boolean pathToGcRoots) { + boolean running = false; + boolean toDisk = false; + + for (PlatformRecording r : recordings) { + if (r.getState() == RecordingState.RUNNING) { + running = true; + if (r.isToDisk()) { + toDisk = true; + } + } + } + // If needed, flush data from memory + if (running) { + if (toDisk) { + OldObjectSample.emit(recordings, pathToGcRoots); + rotateDisk(); + } else { + try (PlatformRecording snapshot = newTemporaryRecording()) { + snapshot.setToDisk(true); + snapshot.setShouldWriteActiveRecordingEvent(false); + snapshot.start(); + OldObjectSample.emit(recordings, pathToGcRoots); + snapshot.stop("Snapshot dump"); + fillWithDiskChunks(target); + } + return; + } + } + fillWithDiskChunks(target); + } + + private void fillWithDiskChunks(PlatformRecording target) { + for (RepositoryChunk c : makeChunkList(null, null)) { + target.add(c); + } + target.setState(RecordingState.STOPPED); + Instant startTime = null; + Instant endTime = null; + + for (RepositoryChunk c : target.getChunks()) { + if (startTime == null || c.getStartTime().isBefore(startTime)) { + startTime = c.getStartTime(); + } + if (endTime == null || c.getEndTime().isAfter(endTime)) { + endTime = c.getEndTime(); + } + } + Instant now = Instant.now(); + if (startTime == null) { + startTime = now; + } + if (endTime == null) { + endTime = now; + } + target.setStartTime(startTime); + target.setStopTime(endTime); + target.setInternalDuration(Duration.between(startTime, endTime)); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/PlatformRecording.java 2019-02-08 18:32:33.006270941 +0300 @@ -0,0 +1,778 @@ +/* + * 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 static jdk.jfr.internal.LogLevel.DEBUG; +import static jdk.jfr.internal.LogLevel.WARN; +import static jdk.jfr.internal.LogTag.JFR; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.channels.FileChannel; +import java.nio.file.StandardOpenOption; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.StringJoiner; +import java.util.TimerTask; +import java.util.TreeMap; + +import jdk.jfr.Configuration; +import jdk.jfr.FlightRecorderListener; +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.jfr.internal.SecuritySupport.SafePath; + +public final class PlatformRecording implements AutoCloseable { + + private final PlatformRecorder recorder; + private final long id; + // Recording settings + private Map settings = new LinkedHashMap<>(); + private Duration duration; + private Duration maxAge; + private long maxSize; + + private WriteableUserPath destination; + + private boolean toDisk = true; + private String name; + private boolean dumpOnExit; + private SafePath dumpOnExitDirectory = new SafePath("."); + // Timestamp information + private Instant stopTime; + private Instant startTime; + + // Misc, information + private RecordingState state = RecordingState.NEW; + private long size; + private final LinkedList chunks = new LinkedList<>(); + private volatile Recording recording; + private TimerTask stopTask; + private TimerTask startTask; + private AccessControlContext noDestinationDumpOnExitAccessControlContext; + private boolean shuoldWriteActiveRecordingEvent = true; + + PlatformRecording(PlatformRecorder recorder, long id) { + // Typically the access control context is taken + // when you call dump(Path) or setDdestination(Path), + // but if no destination is set and dumponexit=true + // the control context of the recording is taken when the + // Recording object is constructed. This works well for + // -XX:StartFlightRecording and JFR.dump + this.noDestinationDumpOnExitAccessControlContext = AccessController.getContext(); + this.id = id; + this.recorder = recorder; + this.name = String.valueOf(id); + } + + public void start() { + RecordingState oldState; + RecordingState newState; + synchronized (recorder) { + oldState = getState(); + if (!Utils.isBefore(state, RecordingState.RUNNING)) { + throw new IllegalStateException("Recording can only be started once."); + } + if (startTask != null) { + startTask.cancel(); + startTask = null; + startTime = null; + } + recorder.start(this); + Logger.log(LogTag.JFR, LogLevel.INFO, () -> { + // Only print non-default values so it easy to see + // which options were added + StringJoiner options = new StringJoiner(", "); + if (!toDisk) { + options.add("disk=false"); + } + if (maxAge != null) { + options.add("maxage=" + Utils.formatTimespan(maxAge, "")); + } + if (maxSize != 0) { + options.add("maxsize=" + Utils.formatBytes(maxSize, "")); + } + if (dumpOnExit) { + options.add("dumponexit=true"); + } + if (duration != null) { + options.add("duration=" + Utils.formatTimespan(duration, "")); + } + if (destination != null) { + options.add("filename=" + destination.getText()); + } + String optionText = options.toString(); + if (optionText.length() != 0) { + optionText = "{" + optionText + "}"; + } + return "Started recording \"" + getName() + "\" (" + getId() + ") " + optionText; + }); + newState = getState(); + } + notifyIfStateChanged(oldState, newState); + } + + public boolean stop(String reason) { + RecordingState oldState; + RecordingState newState; + synchronized (recorder) { + oldState = getState(); + if (stopTask != null) { + stopTask.cancel(); + stopTask = null; + } + recorder.stop(this); + String endText = reason == null ? "" : ". Reason \"" + reason + "\"."; + Logger.log(LogTag.JFR, LogLevel.INFO, "Stopped recording \"" + getName() + "\" (" + getId() + ")" + endText); + this.stopTime = Instant.now(); + newState = getState(); + } + WriteableUserPath dest = getDestination(); + + if (dest != null) { + try { + dumpStopped(dest); + Logger.log(LogTag.JFR, LogLevel.INFO, "Wrote recording \"" + getName() + "\" (" + getId() + ") to " + dest.getText()); + notifyIfStateChanged(newState, oldState); + close(); // remove if copied out + } catch(IOException e) { + // throw e; // BUG8925030 + } + } else { + notifyIfStateChanged(newState, oldState); + } + return true; + } + + public void scheduleStart(Duration delay) { + synchronized (recorder) { + ensureOkForSchedule(); + + startTime = Instant.now().plus(delay); + LocalDateTime now = LocalDateTime.now().plus(delay); + setState(RecordingState.DELAYED); + startTask = createStartTask(); + recorder.getTimer().schedule(startTask, delay.toMillis()); + Logger.log(LogTag.JFR, LogLevel.INFO, "Scheduled recording \"" + getName() + "\" (" + getId() + ") to start at " + now); + } + } + + private void ensureOkForSchedule() { + if (getState() != RecordingState.NEW) { + throw new IllegalStateException("Only a new recoridng can be scheduled for start"); + } + } + + private TimerTask createStartTask() { + // Taking ref. to recording here. + // Opens up for memory leaks. + return new TimerTask() { + @Override + public void run() { + synchronized (recorder) { + if (getState() != RecordingState.DELAYED) { + return; + } + start(); + } + } + }; + } + + void scheduleStart(Instant startTime) { + synchronized (recorder) { + ensureOkForSchedule(); + this.startTime = startTime; + setState(RecordingState.DELAYED); + startTask = createStartTask(); + recorder.getTimer().schedule(startTask, startTime.toEpochMilli()); + } + } + + public Map getSettings() { + synchronized (recorder) { + return settings; + } + } + + public long getSize() { + return size; + } + + public Instant getStopTime() { + synchronized (recorder) { + return stopTime; + } + } + + public Instant getStartTime() { + synchronized (recorder) { + return startTime; + } + } + + public Long getMaxSize() { + synchronized (recorder) { + return maxSize; + } + } + + public Duration getMaxAge() { + synchronized (recorder) { + return maxAge; + } + } + + public String getName() { + synchronized (recorder) { + return name; + } + } + + public RecordingState getState() { + synchronized (recorder) { + return state; + } + } + + @Override + public void close() { + RecordingState oldState; + RecordingState newState; + + synchronized (recorder) { + oldState = getState(); + if (RecordingState.CLOSED != getState()) { + if (startTask != null) { + startTask.cancel(); + startTask = null; + } + recorder.finish(this); + for (RepositoryChunk c : chunks) { + removed(c); + } + chunks.clear(); + setState(RecordingState.CLOSED); + Logger.log(LogTag.JFR, LogLevel.INFO, "Closed recording \"" + getName() + "\" (" + getId() + ")"); + } + newState = getState(); + } + notifyIfStateChanged(newState, oldState); + } + + // To be used internally when doing dumps. + // Caller must have recorder lock and close recording before releasing lock + public PlatformRecording newSnapshotClone(String reason, Boolean pathToGcRoots) throws IOException { + if(!Thread.holdsLock(recorder)) { + throw new InternalError("Caller must have recorder lock"); + } + RecordingState state = getState(); + if (state == RecordingState.CLOSED) { + throw new IOException("Recording \"" + name + "\" (id=" + id + ") has been closed, no contents to write"); + } + if (state == RecordingState.DELAYED || state == RecordingState.NEW) { + throw new IOException("Recording \"" + name + "\" (id=" + id + ") has not started, no contents to write"); + } + if (state == RecordingState.STOPPED) { + PlatformRecording clone = recorder.newTemporaryRecording(); + for (RepositoryChunk r : chunks) { + clone.add(r); + } + return clone; + } + + // Recording is RUNNING, create a clone + PlatformRecording clone = recorder.newTemporaryRecording(); + clone.setShouldWriteActiveRecordingEvent(false); + clone.setName(getName()); + clone.setDestination(this.destination); + clone.setToDisk(true); + // We purposely don't clone settings here, since + // a union a == a + if (!isToDisk()) { + // force memory contents to disk + clone.start(); + } else { + // using existing chunks on disk + for (RepositoryChunk c : chunks) { + clone.add(c); + } + clone.setState(RecordingState.RUNNING); + clone.setStartTime(getStartTime()); + } + if (pathToGcRoots == null) { + clone.setSettings(getSettings()); // needed for old object sample + clone.stop(reason); // dumps to destination path here + } else { + // Risk of violating lock order here, since + // clone.stop() will take recorder lock inside + // metadata lock, but OK if we already + // have recorder lock when we entered metadata lock + synchronized (MetadataRepository.getInstance()) { + clone.setSettings(OldObjectSample.createSettingsForSnapshot(this, pathToGcRoots)); + clone.stop(reason); + } + } + return clone; + } + + public boolean isToDisk() { + synchronized (recorder) { + return toDisk; + } + } + + public void setMaxSize(long maxSize) { + synchronized (recorder) { + if (getState() == RecordingState.CLOSED) { + throw new IllegalStateException("Can't set max age when recording is closed"); + } + this.maxSize = maxSize; + trimToSize(); + } + } + + public void setDestination(WriteableUserPath userSuppliedPath) throws IOException { + synchronized (recorder) { + if (Utils.isState(getState(), RecordingState.STOPPED, RecordingState.CLOSED)) { + throw new IllegalStateException("Destination can't be set on a recording that has been stopped/closed"); + } + this.destination = userSuppliedPath; + } + } + + public WriteableUserPath getDestination() { + synchronized (recorder) { + return destination; + } + } + + void setState(RecordingState state) { + synchronized (recorder) { + this.state = state; + } + } + + void setStartTime(Instant startTime) { + synchronized (recorder) { + this.startTime = startTime; + } + } + + void setStopTime(Instant timeStamp) { + synchronized (recorder) { + stopTime = timeStamp; + } + } + + public long getId() { + synchronized (recorder) { + return id; + } + } + + public void setName(String name) { + synchronized (recorder) { + ensureNotClosed(); + this.name = name; + } + } + + private void ensureNotClosed() { + if (getState() == RecordingState.CLOSED) { + throw new IllegalStateException("Can't change name on a closed recording"); + } + } + + public void setDumpOnExit(boolean dumpOnExit) { + synchronized (recorder) { + this.dumpOnExit = dumpOnExit; + } + } + + public boolean getDumpOnExit() { + synchronized (recorder) { + return dumpOnExit; + } + } + + public void setToDisk(boolean toDisk) { + synchronized (recorder) { + if (Utils.isState(getState(), RecordingState.NEW, RecordingState.DELAYED)) { + this.toDisk = toDisk; + } else { + throw new IllegalStateException("Recording option disk can't be changed after recording has started"); + } + } + } + + public void setSetting(String id, String value) { + synchronized (recorder) { + this.settings.put(id, value); + if (getState() == RecordingState.RUNNING) { + recorder.updateSettings(); + } + } + } + + public void setSettings(Map settings) { + setSettings(settings, true); + } + + private void setSettings(Map settings, boolean update) { + if (LogTag.JFR_SETTING.shouldLog(LogLevel.INFO.level) && update) { + TreeMap ordered = new TreeMap<>(settings); + Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "New settings for recording \"" + getName() + "\" (" + getId() + ")"); + for (Map.Entry entry : ordered.entrySet()) { + String text = entry.getKey() + "=\"" + entry.getValue() + "\""; + Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, text); + } + } + synchronized (recorder) { + this.settings = new LinkedHashMap<>(settings); + if (getState() == RecordingState.RUNNING && update) { + recorder.updateSettings(); + } + } + } + + private void notifyIfStateChanged(RecordingState newState, RecordingState oldState) { + if (oldState == newState) { + return; + } + for (FlightRecorderListener cl : PlatformRecorder.getListeners()) { + try { + cl.recordingStateChanged(getRecording()); + } catch (RuntimeException re) { + Logger.log(JFR, WARN, "Error notifying recorder listener:" + re.getMessage()); + } + } + } + + public void setRecording(Recording recording) { + this.recording = recording; + } + + public Recording getRecording() { + return recording; + } + + @Override + public String toString() { + return getName() + " (id=" + getId() + ") " + getState(); + } + + public void setConfiguration(Configuration c) { + setSettings(c.getSettings()); + } + + public void setMaxAge(Duration maxAge) { + synchronized (recorder) { + if (getState() == RecordingState.CLOSED) { + throw new IllegalStateException("Can't set max age when recording is closed"); + } + this.maxAge = maxAge; + if (maxAge != null) { + trimToAge(Instant.now().minus(maxAge)); + } + } + } + + void appendChunk(RepositoryChunk chunk) { + if (!chunk.isFinished()) { + throw new Error("not finished chunk " + chunk.getStartTime()); + } + synchronized (recorder) { + if (!toDisk) { + return; + } + if (maxAge != null) { + trimToAge(chunk.getEndTime().minus(maxAge)); + } + chunks.addLast(chunk); + added(chunk); + trimToSize(); + } + } + + private void trimToSize() { + if (maxSize == 0) { + return; + } + while (size > maxSize && chunks.size() > 1) { + RepositoryChunk c = chunks.removeFirst(); + removed(c); + } + } + + private void trimToAge(Instant oldest) { + while (!chunks.isEmpty()) { + RepositoryChunk oldestChunk = chunks.peek(); + if (oldestChunk.getEndTime().isAfter(oldest)) { + return; + } + chunks.removeFirst(); + removed(oldestChunk); + } + } + + void add(RepositoryChunk c) { + chunks.add(c); + added(c); + } + + private void added(RepositoryChunk c) { + c.use(); + size += c.getSize(); + Logger.log(JFR, DEBUG, () -> "Recording \"" + name + "\" (" + id + ") added chunk " + c.toString() + ", current size=" + size); + } + + private void removed(RepositoryChunk c) { + size -= c.getSize(); + Logger.log(JFR, DEBUG, () -> "Recording \"" + name + "\" (" + id + ") removed chunk " + c.toString() + ", current size=" + size); + c.release(); + } + + public List getChunks() { + return chunks; + } + + public InputStream open(Instant start, Instant end) throws IOException { + synchronized (recorder) { + if (getState() != RecordingState.STOPPED) { + throw new IOException("Recording must be stopped before it can be read."); + } + List chunksToUse = new ArrayList(); + for (RepositoryChunk chunk : chunks) { + if (chunk.isFinished()) { + Instant chunkStart = chunk.getStartTime(); + Instant chunkEnd = chunk.getEndTime(); + if (start == null || !chunkEnd.isBefore(start)) { + if (end == null || !chunkStart.isAfter(end)) { + chunksToUse.add(chunk); + } + } + } + } + if (chunksToUse.isEmpty()) { + return null; + } + return new ChunkInputStream(chunksToUse); + } + } + + public Duration getDuration() { + synchronized (recorder) { + return duration; + } + } + + void setInternalDuration(Duration duration) { + this.duration = duration; + } + + public void setDuration(Duration duration) { + synchronized (recorder) { + if (Utils.isState(getState(), RecordingState.STOPPED, RecordingState.CLOSED)) { + throw new IllegalStateException("Duration can't be set after a recording has been stopped/closed"); + } + setInternalDuration(duration); + if (getState() != RecordingState.NEW) { + updateTimer(); + } + } + } + + void updateTimer() { + if (stopTask != null) { + stopTask.cancel(); + stopTask = null; + } + if (getState() == RecordingState.CLOSED) { + return; + } + if (duration != null) { + stopTask = createStopTask(); + recorder.getTimer().schedule(stopTask, new Date(startTime.plus(duration).toEpochMilli())); + } + } + + TimerTask createStopTask() { + return new TimerTask() { + @Override + public void run() { + try { + stop("End of duration reached"); + } catch (Throwable t) { + // Prevent malicious user to propagate exception callback in the wrong context + Logger.log(LogTag.JFR, LogLevel.ERROR, "Could not stop recording."); + } + } + }; + } + + public Recording newCopy(boolean stop) { + return recorder.newCopy(this, stop); + } + + void setStopTask(TimerTask stopTask) { + synchronized (recorder) { + this.stopTask = stopTask; + } + } + + void clearDestination() { + destination = null; + } + + public AccessControlContext getNoDestinationDumpOnExitAccessControlContext() { + return noDestinationDumpOnExitAccessControlContext; + } + + void setShouldWriteActiveRecordingEvent(boolean shouldWrite) { + this.shuoldWriteActiveRecordingEvent = shouldWrite; + } + + boolean shouldWriteMetadataEvent() { + return shuoldWriteActiveRecordingEvent; + } + + // Dump running and stopped recordings + public void dump(WriteableUserPath writeableUserPath) throws IOException { + synchronized (recorder) { + try(PlatformRecording p = newSnapshotClone("Dumped by user", null)) { + p.dumpStopped(writeableUserPath); + } + } + } + + public void dumpStopped(WriteableUserPath userPath) throws IOException { + synchronized (recorder) { + userPath.doPriviligedIO(() -> { + try (ChunksChannel cc = new ChunksChannel(chunks); FileChannel fc = FileChannel.open(userPath.getReal(), StandardOpenOption.WRITE, StandardOpenOption.APPEND)) { + cc.transferTo(fc); + fc.force(true); + } + return null; + }); + } + } + + public void filter(Instant begin, Instant end, Long maxSize) { + synchronized (recorder) { + List result = removeAfter(end, removeBefore(begin, new ArrayList<>(chunks))); + if (maxSize != null) { + if (begin != null && end == null) { + result = reduceFromBeginning(maxSize, result); + } else { + result = reduceFromEnd(maxSize, result); + } + } + int size = 0; + for (RepositoryChunk r : result) { + size += r.getSize(); + r.use(); + } + this.size = size; + for (RepositoryChunk r : chunks) { + r.release(); + } + chunks.clear(); + chunks.addAll(result); + } + } + + private static List removeBefore(Instant time, List input) { + if (time == null) { + return input; + } + List result = new ArrayList<>(input.size()); + for (RepositoryChunk r : input) { + if (!r.getEndTime().isBefore(time)) { + result.add(r); + } + } + return result; + } + + private static List removeAfter(Instant time, List input) { + if (time == null) { + return input; + } + List result = new ArrayList<>(input.size()); + for (RepositoryChunk r : input) { + if (!r.getStartTime().isAfter(time)) { + result.add(r); + } + } + return result; + } + + private static List reduceFromBeginning(Long maxSize, List input) { + if (maxSize == null || input.isEmpty()) { + return input; + } + List result = new ArrayList<>(input.size()); + long total = 0; + for (RepositoryChunk r : input) { + total += r.getSize(); + if (total > maxSize) { + break; + } + result.add(r); + } + // always keep at least one chunk + if (result.isEmpty()) { + result.add(input.get(0)); + } + return result; + } + + private static List reduceFromEnd(Long maxSize, List input) { + Collections.reverse(input); + List result = reduceFromBeginning(maxSize, input); + Collections.reverse(result); + return result; + } + + public void setDumpOnExitDirectory(SafePath directory) { + this.dumpOnExitDirectory = directory; + } + + public SafePath getDumpOnExitDirectory() { + return this.dumpOnExitDirectory; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/PrivateAccess.java 2019-02-08 18:32:33.150265905 +0300 @@ -0,0 +1,97 @@ +/* + * 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.Map; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Configuration; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorderPermission; +import jdk.jfr.Recording; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.ValueDescriptor; + +/** + * Provides access to package private function in jdk.jfr. + *

+ * The static initializer in this class loads the Settings class, which will + * call {@link #setPrivateAccess(PrivateAccess)} on this class, which can be + * used call to package protected methods. + * + * This is similar to how java.lang accesses package private methods in + * java.lang.reflect. + */ +public abstract class PrivateAccess { + private volatile static PrivateAccess instance; + + public static PrivateAccess getInstance() { + // Can't be initialized in because it may + // deadlock with FlightRecordeerPermission. + if (instance == null) { + // Will trigger + // FlightRecordeerPermission. + // which will call PrivateAccess.setPrivateAccess + new FlightRecorderPermission(Utils.REGISTER_EVENT); + } + return instance; + } + + public static void setPrivateAccess(PrivateAccess pa) { + instance = pa; + } + + public abstract Type getType(Object o); + + public abstract Configuration newConfiguration(String name, String label, String description, String provider, Map settings, String contents); + + public abstract EventType newEventType(PlatformEventType eventTypes); + + public abstract AnnotationElement newAnnotation(Type annotationType, List values, boolean boot); + + public abstract ValueDescriptor newValueDescriptor(String name, Type fieldType, List annotations, int dimension, boolean constantPool, String fieldName); + + public abstract PlatformRecording getPlatformRecording(Recording r); + + public abstract PlatformEventType getPlatformEventType(EventType eventType); + + public abstract boolean isConstantPool(ValueDescriptor v); + + public abstract String getFieldName(ValueDescriptor v); + + public abstract ValueDescriptor newValueDescriptor(Class type, String name); + + public abstract SettingDescriptor newSettingDescriptor(Type type, String name, String def, List aes); + + public abstract void setAnnotations(ValueDescriptor v, List a); + + public abstract void setAnnotations(SettingDescriptor s, List a); + + public abstract boolean isUnsigned(ValueDescriptor v); + + public abstract PlatformRecorder getPlatformRecorder(); +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/Repository.java 2019-02-08 18:32:33.290261007 +0300 @@ -0,0 +1,165 @@ +/* + * 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.IOException; +import java.nio.file.Path; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.HashSet; +import java.util.Set; + +import jdk.jfr.internal.SecuritySupport.SafePath; + +public final class Repository { + + private static final int MAX_REPO_CREATION_RETRIES = 1000; + private static final JVM jvm = JVM.getJVM(); + private static final Repository instance = new Repository(); + + public final static DateTimeFormatter REPO_DATE_FORMAT = DateTimeFormatter + .ofPattern("yyyy_MM_dd_HH_mm_ss"); + + private final Set cleanupDirectories = new HashSet<>(); + private SafePath baseLocation; + private SafePath repository; + + private Repository() { + } + + public static Repository getRepository() { + return instance; + } + + public synchronized void setBasePath(SafePath baseLocation) throws Exception { + // Probe to see if repository can be created, needed for fail fast + // during JVM startup or JFR.configure + this.repository = createRepository(baseLocation); + try { + // Remove so we don't "leak" repositories, if JFR is never started + // and shutdown hook not added. + SecuritySupport.delete(repository); + } catch (IOException ioe) { + Logger.log(LogTag.JFR, LogLevel.INFO, "Could not delete disk repository " + repository); + } + this.baseLocation = baseLocation; + } + + synchronized void ensureRepository() throws Exception { + if (baseLocation == null) { + setBasePath(SecuritySupport.JAVA_IO_TMPDIR); + } + } + + synchronized RepositoryChunk newChunk(Instant timestamp) { + try { + if (!SecuritySupport.existDirectory(repository)) { + this.repository = createRepository(baseLocation); + jvm.setRepositoryLocation(repository.toString()); + cleanupDirectories.add(repository); + } + return new RepositoryChunk(repository, timestamp); + } catch (Exception e) { + String errorMsg = String.format("Could not create chunk in repository %s, %s", repository, e.getMessage()); + Logger.log(LogTag.JFR, LogLevel.ERROR, errorMsg); + jvm.abort(errorMsg); + throw new InternalError("Could not abort after JFR disk creation error"); + } + } + + private static SafePath createRepository(SafePath basePath) throws Exception { + SafePath canonicalBaseRepositoryPath = createRealBasePath(basePath); + SafePath f = null; + + String basename = REPO_DATE_FORMAT.format(LocalDateTime.now()) + "_" + JVM.getJVM().getPid(); + String name = basename; + + int i = 0; + for (; i < MAX_REPO_CREATION_RETRIES; i++) { + f = new SafePath(canonicalBaseRepositoryPath.toPath().resolve(name)); + if (tryToUseAsRepository(f)) { + break; + } + name = basename + "_" + i; + } + + if (i == MAX_REPO_CREATION_RETRIES) { + throw new Exception("Unable to create JFR repository directory using base location (" + basePath + ")"); + } + SafePath canonicalRepositoryPath = SecuritySupport.toRealPath(f); + return canonicalRepositoryPath; + } + + private static SafePath createRealBasePath(SafePath safePath) throws Exception { + if (SecuritySupport.exists(safePath)) { + if (!SecuritySupport.isWritable(safePath)) { + throw new IOException("JFR repository directory (" + safePath.toString() + ") exists, but isn't writable"); + } + return SecuritySupport.toRealPath(safePath); + } + SafePath p = SecuritySupport.createDirectories(safePath); + return SecuritySupport.toRealPath(p); + } + + private static boolean tryToUseAsRepository(final SafePath path) { + Path parent = path.toPath().getParent(); + if (parent == null) { + return false; + } + try { + try { + SecuritySupport.createDirectories(path); + } catch (Exception e) { + // file already existed or some other problem occurred + } + if (!SecuritySupport.exists(path)) { + return false; + } + if (!SecuritySupport.isDirectory(path)) { + return false; + } + return true; + } catch (IOException io) { + return false; + } + } + + synchronized void clear() { + for (SafePath p : cleanupDirectories) { + try { + SecuritySupport.clearDirectory(p); + Logger.log(LogTag.JFR, LogLevel.INFO, "Removed repository " + p); + } catch (IOException e) { + Logger.log(LogTag.JFR, LogLevel.ERROR, "Repository " + p + " could not be removed at shutdown: " + e.getMessage()); + } + } + } + + public synchronized SafePath getRepositoryPath() { + return repository; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/RepositoryChunk.java 2019-02-08 18:32:33.434255970 +0300 @@ -0,0 +1,210 @@ +/* + * 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.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.ReadableByteChannel; +import java.nio.file.Path; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.util.Comparator; +import java.util.Objects; + +import jdk.jfr.internal.SecuritySupport.SafePath; + +final class RepositoryChunk { + private static final int MAX_CHUNK_NAMES = 100; + + static final Comparator END_TIME_COMPARATOR = new Comparator() { + @Override + public int compare(RepositoryChunk c1, RepositoryChunk c2) { + return c1.endTime.compareTo(c2.endTime); + } + }; + + private final SafePath repositoryPath; + private final SafePath unFinishedFile; + private final SafePath file; + private final Instant startTime; + private final RandomAccessFile unFinishedRAF; + + private Instant endTime = null; // unfinished + private int refCount = 0; + private long size; + + RepositoryChunk(SafePath path, Instant startTime) throws Exception { + ZonedDateTime z = ZonedDateTime.now(); + String fileName = Repository.REPO_DATE_FORMAT.format( + LocalDateTime.ofInstant(startTime, z.getZone())); + this.startTime = startTime; + this.repositoryPath = path; + this.unFinishedFile = findFileName(repositoryPath, fileName, ".part"); + this.file = findFileName(repositoryPath, fileName, ".jfr"); + this.unFinishedRAF = SecuritySupport.createRandomAccessFile(unFinishedFile); + SecuritySupport.touch(file); + } + + private static SafePath findFileName(SafePath directory, String name, String extension) throws Exception { + Path p = directory.toPath().resolve(name + extension); + for (int i = 1; i < MAX_CHUNK_NAMES; i++) { + SafePath s = new SafePath(p); + if (!SecuritySupport.exists(s)) { + return s; + } + String extendedName = String.format("%s_%02d%s", name, i, extension); + p = directory.toPath().resolve(extendedName); + } + p = directory.toPath().resolve(name + "_" + System.currentTimeMillis() + extension); + return SecuritySupport.toRealPath(new SafePath(p)); + } + + public SafePath getUnfishedFile() { + return unFinishedFile; + } + + void finish(Instant endTime) { + try { + finishWithException(endTime); + } catch (IOException e) { + Logger.log(LogTag.JFR, LogLevel.ERROR, "Could not finish chunk. " + e.getMessage()); + } + } + + private void finishWithException(Instant endTime) throws IOException { + unFinishedRAF.close(); + this.size = finish(unFinishedFile, file); + this.endTime = endTime; + Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, () -> "Chunk finished: " + file); + } + + private static long finish(SafePath unFinishedFile, SafePath file) throws IOException { + Objects.requireNonNull(unFinishedFile); + Objects.requireNonNull(file); + SecuritySupport.delete(file); + SecuritySupport.moveReplace(unFinishedFile, file); + return SecuritySupport.getFileSize(file); + } + + public Instant getStartTime() { + return startTime; + } + + public Instant getEndTime() { + return endTime; + } + + private void delete(SafePath f) { + try { + SecuritySupport.delete(f); + Logger.log(LogTag.JFR, LogLevel.DEBUG, () -> "Repository chunk " + f + " deleted"); + } catch (IOException e) { + Logger.log(LogTag.JFR, LogLevel.ERROR, () -> "Repository chunk " + f + " could not be deleted: " + e.getMessage()); + if (f != null) { + SecuritySupport.deleteOnExit(f); + } + } + } + + private void destroy() { + if (!isFinished()) { + finish(Instant.MIN); + } + if (file != null) { + delete(file); + } + try { + unFinishedRAF.close(); + } catch (IOException e) { + Logger.log(LogTag.JFR, LogLevel.ERROR, () -> "Could not close random access file: " + unFinishedFile.toString() + ". File will not be deleted due to: " + e.getMessage()); + } + } + + public synchronized void use() { + ++refCount; + Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, () -> "Use chunk " + toString() + " ref count now " + refCount); + } + + public synchronized void release() { + --refCount; + Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, () -> "Release chunk " + toString() + " ref count now " + refCount); + if (refCount == 0) { + destroy(); + } + } + + @Override + @SuppressWarnings("deprecation") + protected void finalize() { + boolean destroy = false; + synchronized (this) { + if (refCount > 0) { + destroy = true; + } + } + if (destroy) { + destroy(); + } + } + + public long getSize() { + return size; + } + + public boolean isFinished() { + return endTime != null; + } + + @Override + public String toString() { + if (isFinished()) { + return file.toString(); + } + return unFinishedFile.toString(); + } + + ReadableByteChannel newChannel() throws IOException { + if (!isFinished()) { + throw new IOException("Chunk not finished"); + } + return ((SecuritySupport.newFileChannelToRead(file))); + } + + public boolean inInterval(Instant startTime, Instant endTime) { + if (startTime != null && getEndTime().isBefore(startTime)) { + return false; + } + if (endTime != null && getStartTime().isAfter(endTime)) { + return false; + } + return true; + } + + public SafePath getFile() { + return file; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/RequestEngine.java 2019-02-08 18:32:33.578250933 +0300 @@ -0,0 +1,235 @@ +/* + * 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.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Predicate; + +public final class RequestEngine { + + private final static JVM jvm = JVM.getJVM(); + + final static class RequestHook { + private final Runnable hook; + private final PlatformEventType type; + private final AccessControlContext accessControllerContext; + private long delta; + + // Java events + private RequestHook(AccessControlContext acc, PlatformEventType eventType, Runnable hook) { + this.hook = hook; + this.type = eventType; + this.accessControllerContext = acc; + } + + // native events + RequestHook(PlatformEventType eventType) { + this(null, eventType, null); + } + + private void execute() { + try { + if (accessControllerContext == null) { // native + jvm.emitEvent(type.getId(), JVM.counterTime(), 0); + Logger.log(LogTag.JFR_SYSTEM_EVENT, LogLevel.DEBUG, ()-> "Executed periodic hook for " + type.getLogName()); + } else { + executeSecure(); + } + } catch (Throwable e) { + // Prevent malicious user to propagate exception callback in the wrong context + Logger.log(LogTag.JFR_SYSTEM_EVENT, LogLevel.WARN, "Exception occured during execution of period hook for " + type.getLogName()); + } + } + + private void executeSecure() { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + try { + hook.run(); + Logger.log(LogTag.JFR_EVENT, LogLevel.DEBUG, ()-> "Executed periodic hook for " + type.getLogName()); + } catch (Throwable t) { + // Prevent malicious user to propagate exception callback in the wrong context + Logger.log(LogTag.JFR_EVENT, LogLevel.WARN, "Exception occured during execution of period hook for " + type.getLogName()); + } + return null; + } + }, accessControllerContext); + } + } + + private final static List entries = new CopyOnWriteArrayList<>(); + private static long lastTimeMillis; + + // Insertion takes O(2*n), could be O(1) with HashMap, but + // thinking is that CopyOnWriteArrayList is faster + // to iterate over, which will happen more over time. + public static void addHook(AccessControlContext acc, PlatformEventType type, Runnable hook) { + Objects.requireNonNull(acc); + RequestHook he = new RequestHook(acc, type, hook); + for (RequestHook e : entries) { + if (e.hook == hook) { + throw new IllegalArgumentException("Hook has already been added"); + } + } + he.type.setEventHook(true); + entries.add(he); + logHook("Added", type); + } + + + private static void logHook(String action, PlatformEventType type) { + if (type.isJDK() || type.isJVM()) { + Logger.log(LogTag.JFR_SYSTEM_EVENT, LogLevel.INFO, action + " periodic hook for " + type.getLogName()); + } else { + Logger.log(LogTag.JFR_EVENT, LogLevel.INFO, action + " periodic hook for " + type.getLogName()); + } + } + + // Takes O(2*n), see addHook. + public static boolean removeHook(Runnable hook) { + for (RequestHook rh : entries) { + if (rh.hook == hook) { + entries.remove(rh); + rh.type.setEventHook(false); + logHook("Removed", rh.type); + return true; + } + } + return false; + } + + // Only to be used for JVM events. No access control contest + // or check if hook already exists + static void addHooks(List newEntries) { + List addEntries = new ArrayList<>(); + for (RequestHook rh : newEntries) { + rh.type.setEventHook(true); + addEntries.add(rh); + logHook("Added", rh.type); + } + entries.addAll(newEntries); + } + + static void doChunkEnd() { + doChunk(x -> x.isEndChunk()); + } + + static void doChunkBegin() { + doChunk(x -> x.isBeginChunk()); + } + + private static void doChunk(Predicate predicate) { + for (RequestHook requestHook : entries) { + PlatformEventType s = requestHook.type; + if (s.isEnabled() && predicate.test(s)) { + requestHook.execute(); + } + } + } + + static long doPeriodic() { + return run_requests(entries); + } + + // code copied from native impl. + private static long run_requests(Collection entries) { + long last = lastTimeMillis; + // Bug 9000556 - current time millis has rather lame resolution + // The use of os::elapsed_counter() is deliberate here, we don't + // want it exchanged for os::ft_elapsed_counter(). + // Keeping direct call os::elapsed_counter() here for reliable + // real time values in order to decide when registered requestable + // events are due. + long now = System.currentTimeMillis(); + long min = 0; + long delta = 0; + + if (last == 0) { + last = now; + } + + // time from then to now + delta = now - last; + + if (delta < 0) { + // to handle time adjustments + // for example Daylight Savings + lastTimeMillis = now; + return 0; + } + for (RequestHook he : entries) { + long left = 0; + PlatformEventType es = he.type; + // Not enabled, skip. + if (!es.isEnabled() || es.isEveryChunk()) { + continue; + } + long r_period = es.getPeriod(); + long r_delta = he.delta; + + // add time elapsed. + r_delta += delta; + + // above threshold? + if (r_delta >= r_period) { + // Bug 9000556 - don't try to compensate + // for wait > period + r_delta = 0; + he.execute(); + ; + } + + // calculate time left + left = (r_period - r_delta); + + /** + * nothing outside checks that a period is >= 0, so left can end up + * negative here. ex. (r_period =(-1)) - (r_delta = 0) if it is, + * handle it. + */ + if (left < 0) { + left = 0; + } + + // assign delta back + he.delta = r_delta; + + if (min == 0 || left < min) { + min = left; + } + } + lastTimeMillis = now; + return min; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/SecuritySupport.java 2019-02-08 18:32:33.722245896 +0300 @@ -0,0 +1,391 @@ +/* + * 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.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.io.Reader; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ReflectPermission; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.Permission; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.PropertyPermission; +import java.util.concurrent.Callable; + +import sun.misc.Unsafe; +import jdk.jfr.Event; +import jdk.jfr.FlightRecorder; +import jdk.jfr.FlightRecorderListener; +import jdk.jfr.FlightRecorderPermission; +import jdk.jfr.Recording; + +/** + * Contains JFR code that does + * {@link AccessController#doPrivileged(PrivilegedAction)} + */ +public final class SecuritySupport { + private final static Unsafe unsafe = Unsafe.getUnsafe(); + public final static SafePath JFC_DIRECTORY = getPathInProperty("java.home", "lib/jfr"); + + static final SafePath USER_HOME = getPathInProperty("user.home", null); + static final SafePath JAVA_IO_TMPDIR = getPathInProperty("java.io.tmpdir", null); + + final static class SecureRecorderListener implements FlightRecorderListener { + + private final AccessControlContext context; + private final FlightRecorderListener changeListener; + + SecureRecorderListener(AccessControlContext context, FlightRecorderListener changeListener) { + this.context = Objects.requireNonNull(context); + this.changeListener = Objects.requireNonNull(changeListener); + } + + @Override + public void recordingStateChanged(Recording recording) { + AccessController.doPrivileged((PrivilegedAction) () -> { + try { + changeListener.recordingStateChanged(recording); + } catch (Throwable t) { + // Prevent malicious user to propagate exception callback in the wrong context + Logger.log(LogTag.JFR, LogLevel.WARN, "Unexpected exception in listener " + changeListener.getClass()+ " at recording state change"); + } + return null; + }, context); + } + + @Override + public void recorderInitialized(FlightRecorder recorder) { + AccessController.doPrivileged((PrivilegedAction) () -> { + try { + changeListener.recorderInitialized(recorder); + } catch (Throwable t) { + // Prevent malicious user to propagate exception callback in the wrong context + Logger.log(LogTag.JFR, LogLevel.WARN, "Unexpected exception in listener " + changeListener.getClass()+ " when initializing FlightRecorder"); + } + return null; + }, context); + } + + public FlightRecorderListener getChangeListener() { + return changeListener; + } + } + + private static final class DirectoryCleaner extends SimpleFileVisitor { + @Override + public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { + Files.delete(path); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + if (exc != null) { + throw exc; + } + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + } + + /** + * Path created by the default file provider,and not + * a malicious provider. + * + */ + public static final class SafePath { + private final Path path; + private final String text; + + public SafePath(Path p) { + // sanitize + text = p.toString(); + path = Paths.get(text); + } + + public SafePath(String path) { + this(Paths.get(path)); + } + + public Path toPath() { + return path; + } + + public String toString() { + return text; + } + } + + private interface RunnableWithCheckedException { + public void run() throws Exception; + } + + private interface CallableWithoutCheckException { + public T call(); + } + + private static U doPrivilegedIOWithReturn(Callable function) throws IOException { + try { + return AccessController.doPrivileged(new PrivilegedExceptionAction() { + @Override + public U run() throws Exception { + return function.call(); + } + }, null); + } catch (PrivilegedActionException e) { + Throwable t = e.getCause(); + if (t instanceof IOException) { + throw (IOException) t; + } + throw new IOException("Unexpected error during I/O operation. " + t.getMessage(), t); + } + } + + private static void doPriviligedIO(RunnableWithCheckedException function) throws IOException { + doPrivilegedIOWithReturn(() -> { + function.run(); + return null; + }); + } + + private static void doPrivileged(Runnable function, Permission... perms) { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + function.run(); + return null; + } + }, null, perms); + } + + private static void doPrivileged(Runnable function) { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + function.run(); + return null; + } + }); + } + + private static T doPrivilegedWithReturn(CallableWithoutCheckException function, Permission... perms) { + return AccessController.doPrivileged(new PrivilegedAction() { + @Override + public T run() { + return function.call(); + } + }, null, perms); + } + + public static List getPredefinedJFCFiles() { + List list = new ArrayList<>(); + try { + Iterator pathIterator = doPrivilegedIOWithReturn(() -> { + return Files.newDirectoryStream(JFC_DIRECTORY.toPath(), "*").iterator(); + }); + while (pathIterator.hasNext()) { + Path path = pathIterator.next(); + if (path.toString().endsWith(".jfc")) { + list.add(new SafePath(path)); + } + } + } catch (IOException ioe) { + Logger.log(LogTag.JFR, LogLevel.WARN, "Could not access .jfc-files in " + JFC_DIRECTORY + ", " + ioe.getMessage()); + } + return list; + } + + static void makeVisibleToJFR(Class clazz) { + // nothing to do for JDK8 + } + + /** + * Adds a qualified export of the internal.jdk.jfr.internal.handlers package + * (for EventHandler) + */ + static void addHandlerExport(Class clazz) { + // nothing to do for JDK8 + } + + public static void registerEvent(Class eventClass) { + doPrivileged(() -> FlightRecorder.register(eventClass), new FlightRecorderPermission(Utils.REGISTER_EVENT)); + } + + static boolean getBooleanProperty(String propertyName) { + return doPrivilegedWithReturn(() -> Boolean.getBoolean(propertyName), new PropertyPermission(propertyName, "read")); + } + + private static SafePath getPathInProperty(String prop, String subPath) { + return doPrivilegedWithReturn(() -> { + String path = System.getProperty(prop); + if (path == null) { + return null; + } + File file = subPath == null ? new File(path) : new File(path, subPath); + return new SafePath(file.getAbsolutePath()); + }, new PropertyPermission("*", "read")); + } + + // Called by JVM during initialization of JFR + static Thread createRecorderThread(ThreadGroup systemThreadGroup, ClassLoader contextClassLoader) { + // The thread should have permission = new Permission[0], and not "modifyThreadGroup" and "modifyThread" on the stack, + // but it's hard circumvent if we are going to pass in system thread group in the constructor + Thread thread = doPrivilegedWithReturn(() -> new Thread(systemThreadGroup, "JFR Recorder Thread"), new RuntimePermission("modifyThreadGroup"), new RuntimePermission("modifyThread")); + doPrivileged(() -> thread.setContextClassLoader(contextClassLoader), new RuntimePermission("setContextClassLoader"), new RuntimePermission("modifyThread")); + return thread; + } + + static void registerShutdownHook(Thread shutdownHook) { + doPrivileged(() -> Runtime.getRuntime().addShutdownHook(shutdownHook), new RuntimePermission("shutdownHooks")); + } + + static void setUncaughtExceptionHandler(Thread thread, Thread.UncaughtExceptionHandler eh) { + doPrivileged(() -> thread.setUncaughtExceptionHandler(eh), new RuntimePermission("modifyThread")); + } + + static void moveReplace(SafePath from, SafePath to) throws IOException { + doPrivilegedIOWithReturn(() -> Files.move(from.toPath(), to.toPath())); + } + + static void clearDirectory(SafePath safePath) throws IOException { + doPriviligedIO(() -> Files.walkFileTree(safePath.toPath(), new DirectoryCleaner())); + } + + static SafePath toRealPath(SafePath safePath) throws Exception { + return new SafePath(doPrivilegedIOWithReturn(() -> safePath.toPath().toRealPath())); + } + + static boolean existDirectory(SafePath directory) throws IOException { + return doPrivilegedIOWithReturn(() -> Files.exists(directory.toPath())); + } + + static RandomAccessFile createRandomAccessFile(SafePath path) throws Exception { + return doPrivilegedIOWithReturn(() -> new RandomAccessFile(path.toPath().toFile(), "rw")); + } + + public static InputStream newFileInputStream(SafePath safePath) throws IOException { + return doPrivilegedIOWithReturn(() -> Files.newInputStream(safePath.toPath())); + } + + public static long getFileSize(SafePath safePath) throws IOException { + return doPrivilegedIOWithReturn(() -> Files.size(safePath.toPath())); + } + + static SafePath createDirectories(SafePath safePath) throws IOException { + Path p = doPrivilegedIOWithReturn(() -> Files.createDirectories(safePath.toPath())); + return new SafePath(p); + } + + public static boolean exists(SafePath safePath) throws IOException { + return doPrivilegedIOWithReturn(() -> Files.exists(safePath.toPath())); + } + + public static boolean isDirectory(SafePath safePath) throws IOException { + return doPrivilegedIOWithReturn(() -> Files.isDirectory(safePath.toPath())); + } + + static void delete(SafePath localPath) throws IOException { + doPriviligedIO(() -> Files.delete(localPath.toPath())); + } + + static boolean isWritable(SafePath safePath) throws IOException { + return doPrivilegedIOWithReturn(() -> Files.isWritable(safePath.toPath())); + } + + static void deleteOnExit(SafePath safePath) { + doPrivileged(() -> safePath.toPath().toFile().deleteOnExit()); + } + + static ReadableByteChannel newFileChannelToRead(SafePath safePath) throws IOException { + return doPrivilegedIOWithReturn(() -> FileChannel.open(safePath.toPath(), StandardOpenOption.READ)); + } + + public static InputStream getResourceAsStream(String name) throws IOException { + return doPrivilegedIOWithReturn(() -> SecuritySupport.class.getResourceAsStream(name)); + } + + public static Reader newFileReader(SafePath safePath) throws FileNotFoundException, IOException { + return doPrivilegedIOWithReturn(() -> Files.newBufferedReader(safePath.toPath())); + } + + static void touch(SafePath path) throws IOException { + doPriviligedIO(() -> new RandomAccessFile(path.toPath().toFile(), "rw").close()); + } + + static void setAccessible(Method method) { + doPrivileged(() -> method.setAccessible(true), new ReflectPermission("suppressAccessChecks")); + } + + static void setAccessible(Field field) { + doPrivileged(() -> field.setAccessible(true), new ReflectPermission("suppressAccessChecks")); + } + + static void setAccessible(Constructor constructor) { + doPrivileged(() -> constructor.setAccessible(true), new ReflectPermission("suppressAccessChecks")); + } + + static void ensureClassIsInitialized(Class clazz) { + unsafe.ensureClassInitialized(clazz); + } + + static Class defineClass(String name, byte[] bytes, ClassLoader classLoader) { + return unsafe.defineClass(name, bytes, 0, bytes.length, classLoader, null); + } + + static Thread createThreadWitNoPermissions(String threadName, Runnable runnable) { + return doPrivilegedWithReturn(() -> new Thread(runnable, threadName), new Permission[0]); + } + + static void setDaemonThread(Thread t, boolean daeomn) { + doPrivileged(()-> t.setDaemon(daeomn), new RuntimePermission("modifyThread")); + } + + public static SafePath getAbsolutePath(SafePath path) throws IOException { + return new SafePath(doPrivilegedIOWithReturn((()-> path.toPath().toAbsolutePath()))); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/SettingsManager.java 2019-02-08 18:32:33.866240859 +0300 @@ -0,0 +1,292 @@ +/* + * 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.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.StringJoiner; + +import jdk.jfr.Event; +import jdk.jfr.internal.handlers.EventHandler; + +final class SettingsManager { + + private static class InternalSetting { + + private final String identifier; + private Map> enabledMap = new LinkedHashMap<>(5); + private Map> allMap = new LinkedHashMap<>(5); + private boolean enabled; + + /** + * Settings identifier, for example "com.example.HelloWorld" or "56" + * (id of event) + * + * @param settingsId + */ + public InternalSetting(String settingsId) { + this.identifier = settingsId; + } + + public Set getValues(String key) { + if (enabled) { + return enabledMap.get(key); + } else { + return allMap.get(key); + } + } + + public void add(String attribute, String value) { + if ("enabled".equals(attribute) && "true".equals(value)) { + enabled = true; + allMap = null; // no need to keep these around + } + addToMap(enabledMap, attribute, value); + if (allMap != null) { + addToMap(allMap, attribute, value); + } + } + + private void addToMap(Map> map, String attribute, String value) { + Set values = map.get(attribute); + if (values == null) { + values = new HashSet(5); + map.put(attribute, values); + } + values.add(value); + + } + + public String getSettingsId() { + return identifier; + } + + public void add(InternalSetting enabled) { + for (Map.Entry> entry : enabled.enabledMap.entrySet()) { + for (String value : entry.getValue()) { + add(entry.getKey(), value); + } + } + } + + public boolean isEnabled() { + return enabled; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(identifier); + sb.append(": "); + sb.append(enabledMap.toString()); + return sb.toString(); + } + + public void finish() { + if (!enabled) { + // settings from disabled + // events should not impact results, but + // we can't clear enabledMap since enabled=false + // needs be there, so events that are enabled + // by default are turned off + Map> disabledMap = new HashMap<>(2); + Set values = new HashSet<>(2); + values.add("false"); + disabledMap.put("enabled", values); + enabledMap = disabledMap; + } + } + } + + private Map availableSettings = new LinkedHashMap<>(); + + void setSettings(List> activeSettings) { + // store settings so they are available if a new event class is loaded + availableSettings = createSettingsMap(activeSettings); + List eventControls = MetadataRepository.getInstance().getEventControls(); + if (!JVM.getJVM().isRecording()) { + for (EventControl ec : eventControls) { + ec.disable(); + } + } else { + if (LogTag.JFR_SETTING.shouldLog(LogLevel.INFO.level)) { + Collections.sort(eventControls, (x,y) -> x.getEventType().getName().compareTo(y.getEventType().getName())); + } + for (EventControl ec : eventControls) { + setEventControl(ec); + } + } + if (JVM.getJVM().getAllowedToDoEventRetransforms()) { + updateRetransform(JVM.getJVM().getAllEventClasses()); + } + } + + public void updateRetransform(List> eventClasses) { + List> classes = new ArrayList<>(); + for(Class eventClass: eventClasses) { + EventHandler eh = Utils.getHandler(eventClass); + if (eh != null ) { + PlatformEventType eventType = eh.getPlatformEventType(); + if (eventType.isMarkedForInstrumentation()) { + classes.add(eventClass); + eventType.markForInstrumentation(false); + // A bit premature to set it here, but hard to check + // after call to retransformClasses. + eventType.setInstrumented(); + } + } + } + if (!classes.isEmpty()) { + JVM.getJVM().retransformClasses(classes.toArray(new Class[0])); + } + } + + private Map createSettingsMap(List> activeSettings) { + Map map = new LinkedHashMap<>(activeSettings.size()); + for (Map rec : activeSettings) { + for (InternalSetting internal : makeInternalSettings(rec)) { + InternalSetting is = map.get(internal.getSettingsId()); + if (is == null) { + map.put(internal.getSettingsId(), internal); + } else { + is.add(internal); + } + } + } + return map; + } + + private Collection makeInternalSettings(Map rec) { + Map internals = new LinkedHashMap<>(); + for (Map.Entry entry : rec.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + int index = key.indexOf("#"); + if (index > 1 && index < key.length() - 2) { + String eventName = key.substring(0, index); + eventName = Utils.upgradeLegacyJDKEvent(eventName); + InternalSetting s = internals.get(eventName); + String settingName = key.substring(index + 1).trim(); + if (s == null) { + s = new InternalSetting(eventName); + internals.put(eventName, s); + } + s.add(settingName, value); + } + } + for (InternalSetting s : internals.values()) { + s.finish(); + } + + return internals.values(); + } + + void setEventControl(EventControl ec) { + InternalSetting is = getInternalSetting(ec); + Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "Applied settings for " + ec.getEventType().getLogName() + " {"); + for (Entry entry : ec.getEntries()) { + Set values = null; + String settingName = entry.getKey(); + if (is != null) { + values = is.getValues(settingName); + } + Control control = entry.getValue(); + if (values != null) { + control.apply(values); + String after = control.getLastValue(); + if (LogTag.JFR_SETTING.shouldLog(LogLevel.INFO.level)) { + if (Utils.isSettingVisible(control, ec.getEventType().hasEventHook())) { + if (values.size() > 1) { + StringJoiner sj = new StringJoiner(", ", "{", "}"); + for (String s : values) { + sj.add("\"" + s + "\""); + } + String message = " " + settingName + "= " + sj.toString() + " => \"" + after + "\""; + Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message); + } else { + String message = " " + settingName + "=\"" + control.getLastValue() + "\""; + Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message); + } + } + } + } else { + control.setDefault(); + if (LogTag.JFR_SETTING.shouldLog(LogLevel.INFO.level)) { + String message = " " + settingName + "=\"" + control.getLastValue() + "\""; + Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, message); + } + } + } + ec.writeActiveSettingEvent(); + Logger.log(LogTag.JFR_SETTING, LogLevel.INFO, "}"); + } + + private InternalSetting getInternalSetting(EventControl ec) { + String name = ec.getEventType().getName(); + InternalSetting nameBased = availableSettings.get(name); + InternalSetting idBased = availableSettings.get(ec.getSettingsId()); + + if (nameBased == null && idBased == null) { + return null; + } + if (idBased == null) { + return nameBased; + } + if (nameBased == null) { + return idBased; + } + InternalSetting mixed = new InternalSetting(nameBased.getSettingsId()); + mixed.add(nameBased); + mixed.add(idBased); + return mixed; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (InternalSetting enabled : availableSettings.values()) { + sb.append(enabled.toString()); + sb.append("\n"); + } + return sb.toString(); + } + + boolean isEnabled(String eventName) { + InternalSetting is = availableSettings.get(eventName); + if (is == null) { + return false; + } + return is.isEnabled(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/ShutdownHook.java 2019-02-08 18:32:34.010235821 +0300 @@ -0,0 +1,105 @@ +/* + * 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.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +import jdk.jfr.RecordingState; + +/** + * Class responsible for dumping recordings on exit + * + */ +final class ShutdownHook implements Runnable { + private final PlatformRecorder recorder; + Object tlabDummyObject; + + ShutdownHook(PlatformRecorder recorder) { + this.recorder = recorder; + } + + @Override + public void run() { + // this allocation is done in order to fetch a new TLAB before + // starting any "real" operations. In low memory situations, + // we would like to take an OOM as early as possible. + tlabDummyObject = new Object(); + + for (PlatformRecording recording : recorder.getRecordings()) { + if (recording.getDumpOnExit() && recording.getState() == RecordingState.RUNNING) { + dump(recording); + } + } + recorder.destroy(); + } + + private void dump(PlatformRecording recording) { + try { + WriteableUserPath dest = recording.getDestination(); + if (dest == null) { + dest = makeDumpOnExitPath(recording); + recording.setDestination(dest); + } + if (dest != null) { + recording.stop("Dump on exit"); + } + } catch (Exception e) { + Logger.log(LogTag.JFR, LogLevel.DEBUG, () -> "Could not dump recording " + recording.getName() + " on exit."); + } + } + + private WriteableUserPath makeDumpOnExitPath(PlatformRecording recording) { + try { + String name = Utils.makeFilename(recording.getRecording()); + AccessControlContext acc = recording.getNoDestinationDumpOnExitAccessControlContext(); + return AccessController.doPrivileged(new PrivilegedExceptionAction() { + @Override + public WriteableUserPath run() throws Exception { + return new WriteableUserPath(recording.getDumpOnExitDirectory().toPath().resolve(name)); + } + }, acc); + } catch (PrivilegedActionException e) { + Throwable t = e.getCause(); + if (t instanceof SecurityException) { + Logger.log(LogTag.JFR, LogLevel.WARN, "Not allowed to create dump path for recording " + recording.getId() + " on exit."); + } + if (t instanceof IOException) { + Logger.log(LogTag.JFR, LogLevel.WARN, "Could not dump " + recording.getId() + " on exit."); + } + return null; + } + } + + static final class ExceptionHandler implements Thread.UncaughtExceptionHandler { + public void uncaughtException(Thread t, Throwable e) { + JVM.getJVM().uncaughtException(t, e); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/StringPool.java 2019-02-08 18:32:34.154230784 +0300 @@ -0,0 +1,135 @@ +/* + * 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.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; + +import sun.misc.Unsafe; + +public final class StringPool { + + private static final Unsafe unsafe = Unsafe.getUnsafe(); + + static final int MIN_LIMIT = 16; + static final int MAX_LIMIT = 128; /* 0 MAX means disabled */ + private static final long epochAddress; + private static final SimpleStringIdPool sp = new SimpleStringIdPool(); + static { + epochAddress = JVM.getJVM().getEpochAddress(); + sp.reset(); + } + public static long addString(String s) { + return sp.addString(s); + } + private static boolean getCurrentEpoch() { + return unsafe.getByte(epochAddress) == 1; + } + private static class SimpleStringIdPool { + /* string id index */ + private final AtomicLong sidIdx = new AtomicLong(); + /* epoch of cached strings */ + private boolean poolEpoch; + /* the cache */ + private final ConcurrentHashMap cache; + /* max size */ + private final int MAX_SIZE = 32*1024; + /* max size bytes*/ + private final long MAX_SIZE_UTF16 = 16*1024*1024; + /* max size bytes*/ + private long currentSizeUTF16; + + /* looking at a biased data set 4 is a good value */ + private final String[] preCache = new String[]{"", "" , "" ,""}; + /* index of oldest */ + private int preCacheOld = 0; + /* loop mask */ + private static final int preCacheMask = 0x03; + + SimpleStringIdPool() { + cache = new ConcurrentHashMap<>(MAX_SIZE, 0.75f); + } + void reset() { + reset(getCurrentEpoch()); + } + private void reset(boolean epoch) { + this.cache.clear(); + this.poolEpoch = epoch; + this.currentSizeUTF16 = 0; + } + private long addString(String s) { + boolean currentEpoch = getCurrentEpoch(); + if (poolEpoch == currentEpoch) { + /* pool is for current chunk */ + Long lsid = this.cache.get(s); + if (lsid != null) { + return lsid.longValue(); + } + } else { + /* pool is for an old chunk */ + reset(currentEpoch); + } + if (!preCache(s)) { + /* we should not pool this string */ + return -1; + } + if (cache.size() > MAX_SIZE || currentSizeUTF16 > MAX_SIZE_UTF16) { + /* pool was full */ + reset(currentEpoch); + } + return storeString(s); + } + + private long storeString(String s) { + long sid = this.sidIdx.getAndIncrement(); + /* we can race but it is ok */ + this.cache.put(s, sid); + boolean currentEpoch; + synchronized(SimpleStringIdPool.class) { + currentEpoch = JVM.addStringConstant(poolEpoch, sid, s); + currentSizeUTF16 += s.length(); + } + /* did we write in chunk that this pool represent */ + return currentEpoch == poolEpoch ? sid : -1; + } + private boolean preCache(String s) { + if (preCache[0].equals(s)) { + return true; + } + if (preCache[1].equals(s)) { + return true; + } + if (preCache[2].equals(s)) { + return true; + } + if (preCache[3].equals(s)) { + return true; + } + preCacheOld = (preCacheOld - 1) & preCacheMask; + preCache[preCacheOld] = s; + return false; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/Type.java 2019-02-08 18:32:34.302225608 +0300 @@ -0,0 +1,321 @@ +/* + * 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.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Event; +import jdk.jfr.SettingControl; +import jdk.jfr.ValueDescriptor; + +/** + * Internal data structure that describes a type, + * + * Used to create event types, value descriptor and annotations. + * + */ +public class Type implements Comparable { + public static final String SUPER_TYPE_ANNOTATION = java.lang.annotation.Annotation.class.getName(); + public static final String SUPER_TYPE_SETTING = SettingControl.class.getName(); + public static final String SUPER_TYPE_EVENT = Event.class.getName(); + public static final String EVENT_NAME_PREFIX = "jdk."; + public static final String TYPES_PREFIX = "jdk.types."; + public static final String SETTINGS_PREFIX = "jdk.settings."; + + + // Initialization of known types + private final static Map> knownTypes = new HashMap<>(); + static final Type BOOLEAN = register(boolean.class, new Type("boolean", null, 4)); + static final Type CHAR = register(char.class, new Type("char", null, 5)); + static final Type FLOAT = register(float.class, new Type("float", null, 6)); + static final Type DOUBLE = register(double.class, new Type("double", null, 7)); + static final Type BYTE = register(byte.class, new Type("byte", null, 8)); + static final Type SHORT = register(short.class, new Type("short", null, 9)); + static final Type INT = register(int.class, new Type("int", null, 10)); + static final Type LONG = register(long.class, new Type("long", null, 11)); + static final Type CLASS = register(Class.class, new Type("java.lang.Class", null, 20)); + static final Type STRING = register(String.class, new Type("java.lang.String", null, 21)); + static final Type THREAD = register(Thread.class, new Type("java.lang.Thread", null, 22)); + static final Type STACK_TRACE = register(null, new Type(TYPES_PREFIX + "StackTrace", null, 23)); + + private final AnnotationConstruct annos = new AnnotationConstruct(); + private final String name; + private final String superType; + private final boolean constantPool; + private final long id; + private final ArrayList fields = new ArrayList<>(); + private Boolean simpleType; // calculated lazy + private boolean remove = true; + /** + * Creates a type + * + * @param javaTypeName i.e "java.lang.String" + * @param superType i.e "java.lang.Annotation" + * @param id the class id that represents the class in the JVM + * + */ + public Type(String javaTypeName, String superType, long typeId) { + this(javaTypeName, superType, typeId, false); + } + + Type(String javaTypeName, String superType, long typeId, boolean contantPool) { + this(javaTypeName, superType, typeId, contantPool, null); + } + + Type(String javaTypeName, String superType, long typeId, boolean contantPool, Boolean simpleType) { + Objects.requireNonNull(javaTypeName); + + if (!isValidJavaIdentifier(javaTypeName)) { + throw new IllegalArgumentException(javaTypeName + " is not a valid Java identifier"); + } + this.constantPool = contantPool; + this.superType = superType; + this.name = javaTypeName; + this.id = typeId; + this.simpleType = simpleType; + } + + static boolean isDefinedByJVM(long id) { + return id < JVM.RESERVED_CLASS_ID_LIMIT; + } + + public static long getTypeId(Class clazz) { + Type type = Type.getKnownType(clazz); + return type == null ? JVM.getJVM().getTypeId(clazz) : type.getId(); + } + + static Collection getKnownTypes() { + return knownTypes.keySet(); + } + + public static boolean isValidJavaIdentifier(String identifier) { + if (identifier.isEmpty()) { + return false; + } + if (!Character.isJavaIdentifierStart(identifier.charAt(0))) { + return false; + } + for (int i = 1; i < identifier.length(); i++) { + char c = identifier.charAt(i); + if (c != '.') { + if (!Character.isJavaIdentifierPart(c)) { + return false; + } + } + } + return true; + } + + public static boolean isValidJavaFieldType(String name) { + for (Map.Entry> entry : knownTypes.entrySet()) { + Class clazz = entry.getValue(); + if (clazz != null && name.equals(clazz.getName())) { + return true; + } + } + return false; + } + + public static Type getKnownType(String typeName) { + for (Type type : knownTypes.keySet()) { + if (type.getName().equals(typeName)) { + return type; + } + } + return null; + } + + static boolean isKnownType(Class type) { + if (type.isPrimitive()) { + return true; + } + if (type.equals(Class.class) || type.equals(Thread.class) || type.equals(String.class)) { + return true; + } + return false; + } + + public static Type getKnownType(Class clazz) { + for (Map.Entry> entry : knownTypes.entrySet()) { + if (clazz != null && clazz.equals(entry.getValue())) { + return entry.getKey(); + } + } + return null; + } + + public String getName() { + return name; + } + + public String getLogName() { + return getName() + "(" + getId() + ")"; + } + + public List getFields() { + return fields; + } + + public boolean isSimpleType() { + if (simpleType == null) { + simpleType = calculateSimpleType(); + } + return simpleType.booleanValue(); + } + + private boolean calculateSimpleType() { + if (fields.size() != 1) { + return false; + } + // annotation, settings and event can never be simple types + return superType == null; + } + + public boolean isDefinedByJVM() { + return id < JVM.RESERVED_CLASS_ID_LIMIT; + } + + private static Type register(Class clazz, Type type) { + knownTypes.put(type, clazz); + return type; + } + + public void add(ValueDescriptor valueDescriptor) { + Objects.requireNonNull(valueDescriptor); + fields.add(valueDescriptor); + } + + void trimFields() { + fields.trimToSize(); + } + + void setAnnotations(List annotations) { + annos.setAnnotationElements(annotations); + } + + public String getSuperType() { + return superType; + } + + public long getId() { + return id; + } + + public boolean isConstantPool() { + return constantPool; + } + + public String getLabel() { + return annos.getLabel(); + } + + public List getAnnotationElements() { + return annos.getUnmodifiableAnnotationElements(); + } + + public T getAnnotation(Class clazz) { + return annos.getAnnotation(clazz); + } + + public String getDescription() { + return annos.getDescription(); + } + + @Override + public int hashCode() { + return Long.hashCode(id); + } + + @Override + public boolean equals(Object object) { + if (object instanceof Type) { + Type that = (Type) object; + return that.id == this.id; + } + return false; + } + + @Override + public int compareTo(Type that) { + return Long.compare(this.id, that.id); + } + + void log(String action, LogTag logTag, LogLevel level) { + if (logTag.shouldLog(level.level) && !isSimpleType()) { + Logger.log(logTag, LogLevel.TRACE, action + " " + typeText() + " " + getLogName() + " {"); + for (ValueDescriptor v : getFields()) { + String array = v.isArray() ? "[]" : ""; + Logger.log(logTag, LogLevel.TRACE, " " + v.getTypeName() + array + " " + v.getName() + ";"); + } + Logger.log(logTag, LogLevel.TRACE, "}"); + } else { + if (logTag.shouldLog(LogLevel.INFO.level) && !isSimpleType()) { + Logger.log(logTag, LogLevel.INFO, action + " " + typeText() + " " + getLogName()); + } + } + } + + private String typeText() { + if (this instanceof PlatformEventType) { + return "event type"; + } + if (Type.SUPER_TYPE_SETTING.equals(superType)) { + return "setting type"; + } + if (Type.SUPER_TYPE_ANNOTATION.equals(superType)) { + return "annotation type"; + } + return "type"; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getLogName()); + if (!getFields().isEmpty()) { + sb.append(" {\n"); + for (ValueDescriptor td : getFields()) { + sb.append(" type=" + td.getTypeName() + "(" + td.getTypeId() + ") name=" + td.getName() + "\n"); + } + sb.append("}\n"); + } + return sb.toString(); + } + + public void setRemove(boolean remove) { + this.remove = remove; + } + + public boolean getRemove() { + return remove; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/TypeLibrary.java 2019-02-08 18:32:34.450220431 +0300 @@ -0,0 +1,492 @@ +/* + * 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.lang.annotation.Annotation; +import java.lang.annotation.Repeatable; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.Name; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.Timespan; +import jdk.jfr.Timestamp; +import jdk.jfr.ValueDescriptor; + +public final class TypeLibrary { + + private static TypeLibrary instance; + private static final Map types = new LinkedHashMap<>(100); + static final ValueDescriptor DURATION_FIELD = createDurationField(); + static final ValueDescriptor THREAD_FIELD = createThreadField(); + static final ValueDescriptor STACK_TRACE_FIELD = createStackTraceField(); + static final ValueDescriptor START_TIME_FIELD = createStartTimeField(); + + private TypeLibrary(List jvmTypes) { + visitReachable(jvmTypes, t -> !types.containsKey(t.getId()), t -> types.put(t.getId(), t)); + if (LogTag.JFR_SYSTEM_METADATA.shouldLog(LogLevel.INFO.level)) { + Stream s = types.values().stream().sorted((x, y) -> Long.compare(x.getId(), y.getId())); + s.forEach(t -> t.log("Added", LogTag.JFR_SYSTEM_METADATA, LogLevel.INFO)); + } + } + + private static ValueDescriptor createStartTimeField() { + List annos = createStandardAnnotations("Start Time", null); + annos.add(new jdk.jfr.AnnotationElement(Timestamp.class, Timestamp.TICKS)); + return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_START_TIME, Type.LONG, annos, 0, false, + EventInstrumentation.FIELD_START_TIME); + + } + + private static ValueDescriptor createStackTraceField() { + List annos = new ArrayList<>(); + annos = createStandardAnnotations("Stack Trace", "Stack Trace starting from the method the event was committed in"); + return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_STACK_TRACE, Type.STACK_TRACE, annos, 0, true, + EventInstrumentation.FIELD_STACK_TRACE); + } + + private static ValueDescriptor createThreadField() { + List annos = new ArrayList<>(); + annos = createStandardAnnotations("Event Thread", "Thread in which event was committed in"); + return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_EVENT_THREAD, Type.THREAD, annos, 0, true, + EventInstrumentation.FIELD_EVENT_THREAD); + } + + private static ValueDescriptor createDurationField() { + List annos = new ArrayList<>(); + annos = createStandardAnnotations("Duration", null); + annos.add(new jdk.jfr.AnnotationElement(Timespan.class, Timespan.TICKS)); + return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_DURATION, Type.LONG, annos, 0, false, EventInstrumentation.FIELD_DURATION); + } + + public static TypeLibrary getInstance() { + synchronized (TypeLibrary.class) { + if (instance == null) { + List jvmTypes; + try { + jvmTypes = MetadataHandler.createTypes(); + Collections.sort(jvmTypes, (a,b) -> Long.compare(a.getId(), b.getId())); + } catch (IOException e) { + throw new Error("JFR: Could not read metadata"); + } + instance = new TypeLibrary(jvmTypes); + } + return instance; + } + } + + public List getTypes() { + return new ArrayList<>(types.values()); + } + + public static Type createAnnotationType(Class a) { + if (shouldPersist(a)) { + Type type = defineType(a, Type.SUPER_TYPE_ANNOTATION, false); + if (type != null) { + SecuritySupport.makeVisibleToJFR(a); + for (Method method : a.getDeclaredMethods()) { + type.add(PrivateAccess.getInstance().newValueDescriptor(method.getReturnType(), method.getName())); + } + ArrayList aes = new ArrayList<>(); + for (Annotation annotation : resolveRepeatedAnnotations(a.getAnnotations())) { + AnnotationElement ae = createAnnotation(annotation); + if (ae != null) { + aes.add(ae); + } + } + aes.trimToSize(); + type.setAnnotations(aes); + } + return getType(a); + } + return null; + } + + static AnnotationElement createAnnotation(Annotation annotation) { + Class annotationType = annotation.annotationType(); + Type type = createAnnotationType(annotationType); + if (type != null) { + List values = new ArrayList<>(); + for (ValueDescriptor v : type.getFields()) { + values.add(invokeAnnotation(annotation, v.getName())); + } + + return PrivateAccess.getInstance().newAnnotation(type, values, annotation.annotationType().getClassLoader() == null); + } + return null; + } + + private static Object invokeAnnotation(Annotation annotation, String methodName) { + final Method m; + try { + m = annotation.getClass().getMethod(methodName, new Class[0]); + } catch (NoSuchMethodException e1) { + throw (Error) new InternalError("Could not loacate method " + methodName + " in annotation " + annotation.getClass().getName()); + } + SecuritySupport.setAccessible(m); + try { + return m.invoke(annotation, new Object[0]); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw (Error) new InternalError("Could not get value for method " + methodName + " in annotation " + annotation.getClass().getName()); + } + } + + private static boolean shouldPersist(Class a) { + if (a == MetadataDefinition.class || a.getAnnotation(MetadataDefinition.class) == null) { + return false; + } + return true; + } + + private static boolean isDefined(Class clazz) { + return types.containsKey(Type.getTypeId(clazz)); + } + + private static Type getType(Class clazz) { + return types.get(Type.getTypeId(clazz)); + } + + private static Type defineType(Class clazz, String superType, boolean eventType) { + if (!isDefined(clazz)) { + Name name = clazz.getAnnotation(Name.class); + String typeName = name != null ? name.value() : clazz.getName(); + long id = Type.getTypeId(clazz); + Type t; + if (eventType) { + t = new PlatformEventType(typeName, id, clazz.getClassLoader() == null, true); + } else { + t = new Type(typeName, superType, id); + } + types.put(t.getId(), t); + return t; + } + return null; + } + public static Type createType(Class clazz) { + return createType(clazz, Collections.emptyList(), Collections.emptyList()); + } + + public static Type createType(Class clazz, List dynamicAnnotations, List dynamicFields) { + + if (Thread.class == clazz) { + return Type.THREAD; + } + + if (Class.class.isAssignableFrom(clazz)) { + return Type.CLASS; + } + + if (String.class.equals(clazz)) { + return Type.STRING; + } + + if (isDefined(clazz)) { + return getType(clazz); + } + + if (clazz.isPrimitive()) { + return defineType(clazz, null,false); + } + + if (clazz.isArray()) { + throw new InternalError("Arrays not supported"); + } + + // STRUCT + String superType = null; + boolean eventType = false; + if (Event.class.isAssignableFrom(clazz)) { + superType = Type.SUPER_TYPE_EVENT; + eventType= true; + } + if (Control.class.isAssignableFrom(clazz)) { + superType = Type.SUPER_TYPE_SETTING; + } + + // forward declare to avoid infinite recursion + defineType(clazz, superType, eventType); + Type type = getType(clazz); + + if (eventType) { + addImplicitFields(type, true, true, true, true ,false); + addUserFields(clazz, type, dynamicFields); + type.trimFields(); + } + addAnnotations(clazz, type, dynamicAnnotations); + + if (clazz.getClassLoader() == null) { + type.log("Added", LogTag.JFR_SYSTEM_METADATA, LogLevel.INFO); + } else { + type.log("Added", LogTag.JFR_METADATA, LogLevel.INFO); + } + return type; + } + + private static void addAnnotations(Class clazz, Type type, List dynamicAnnotations) { + ArrayList aes = new ArrayList<>(); + if (dynamicAnnotations.isEmpty()) { + for (Annotation a : Utils.getAnnotations(clazz)) { + AnnotationElement ae = createAnnotation(a); + if (ae != null) { + aes.add(ae); + } + } + } else { + List newTypes = new ArrayList<>(); + aes.addAll(dynamicAnnotations); + for (AnnotationElement ae : dynamicAnnotations) { + newTypes.add(PrivateAccess.getInstance().getType(ae)); + } + addTypes(newTypes); + } + type.setAnnotations(aes); + aes.trimToSize(); + } + + private static void addUserFields(Class clazz, Type type, List dynamicFields) { + Map dynamicFieldSet = new HashMap<>(); + for (ValueDescriptor dynamicField : dynamicFields) { + dynamicFieldSet.put(dynamicField.getName(), dynamicField); + } + List newTypes = new ArrayList<>(); + for (Field field : Utils.getVisibleEventFields(clazz)) { + ValueDescriptor vd = dynamicFieldSet.get(field.getName()); + if (vd != null) { + if (!vd.getTypeName().equals(field.getType().getName())) { + throw new InternalError("Type expected to match for field " + vd.getName() + " expected " + field.getName() + " but got " + vd.getName()); + } + for (AnnotationElement ae : vd.getAnnotationElements()) { + newTypes.add(PrivateAccess.getInstance().getType(ae)); + } + newTypes.add(PrivateAccess.getInstance().getType(vd)); + } else { + vd = createField(field); + } + if (vd != null) { + type.add(vd); + } + } + addTypes(newTypes); + } + + // By convention all events have these fields. + static void addImplicitFields(Type type, boolean requestable, boolean hasDuration, boolean hasThread, boolean hasStackTrace, boolean hasCutoff) { + createAnnotationType(Timespan.class); + createAnnotationType(Timestamp.class); + createAnnotationType(Label.class); + defineType(long.class, null,false); + addFields(type, requestable, hasDuration, hasThread, hasStackTrace, hasCutoff); + } + + private static void addFields(Type type, boolean requestable, boolean hasDuration, boolean hasThread, boolean hasStackTrace, boolean hasCutoff) { + type.add(START_TIME_FIELD); + if (hasDuration || hasCutoff) { + type.add(DURATION_FIELD); + } + if (hasThread) { + type.add(THREAD_FIELD); + } + if (hasStackTrace) { + type.add(STACK_TRACE_FIELD); + } + } + + private static List createStandardAnnotations(String name, String description) { + List annotationElements = new ArrayList<>(2); + annotationElements.add(new jdk.jfr.AnnotationElement(Label.class, name)); + if (description != null) { + annotationElements.add(new jdk.jfr.AnnotationElement(Description.class, description)); + } + return annotationElements; + } + + private static ValueDescriptor createField(Field field) { + int mod = field.getModifiers(); + if (Modifier.isTransient(mod)) { + return null; + } + if (Modifier.isStatic(mod)) { + return null; + } + Class fieldType = field.getType(); + if (!Type.isKnownType(fieldType)) { + return null; + } + boolean constantPool = Thread.class == fieldType || fieldType == Class.class; + Type type = createType(fieldType); + String fieldName = field.getName(); + Name name = field.getAnnotation(Name.class); + String useName = fieldName; + if (name != null) { + useName = name.value(); + } + List ans = new ArrayList<>(); + for (Annotation a : resolveRepeatedAnnotations(field.getAnnotations())) { + AnnotationElement ae = createAnnotation(a); + if (ae != null) { + ans.add(ae); + } + } + return PrivateAccess.getInstance().newValueDescriptor(useName, type, ans, 0, constantPool, fieldName); + } + + private static List resolveRepeatedAnnotations(Annotation[] annotations) { + List annos = new ArrayList<>(annotations.length); + for (Annotation a : annotations) { + boolean repeated = false; + Method m; + try { + m = a.annotationType().getMethod("value"); + Class returnType = m.getReturnType(); + if (returnType.isArray()) { + Class ct = returnType.getComponentType(); + if (Annotation.class.isAssignableFrom(ct) && ct.getAnnotation(Repeatable.class) != null) { + Object res = m.invoke(a, new Object[0]); + if (res != null && Annotation[].class.isAssignableFrom(res.getClass())) { + for (Annotation rep : (Annotation[]) m.invoke(a, new Object[0])) { + annos.add(rep); + } + repeated = true; + } + } + } + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + // Ignore, can't access repeatable information + } + if (!repeated) { + annos.add(a); + } + } + return annos; + } + + // Purpose of this method is to mark types that are reachable + // from registered event types. Those types that are not reachable can + // safely be removed + public boolean clearUnregistered() { + Logger.log(LogTag.JFR_METADATA, LogLevel.TRACE, "Cleaning out obsolete metadata"); + List registered = new ArrayList<>(); + for (Type type : types.values()) { + if (type instanceof PlatformEventType) { + if (((PlatformEventType) type).isRegistered()) { + registered.add(type); + } + } + } + visitReachable(registered, t -> t.getRemove(), t -> t.setRemove(false)); + List removeIds = new ArrayList<>(); + for (Type type : types.values()) { + if (type.getRemove() && !Type.isDefinedByJVM(type.getId())) { + removeIds.add(type.getId()); + if (LogTag.JFR_METADATA.shouldLog(LogLevel.TRACE.level)) { + Logger.log(LogTag.JFR_METADATA, LogLevel.TRACE, "Removed obsolete metadata " + type.getName()); + } + } + // Optimization, set to true now to avoid iterating + // types first thing at next call to clearUnregistered + type.setRemove(true); + } + for (Long id : removeIds) { + types.remove(id); + } + return !removeIds.isEmpty(); + } + + public void addType(Type type) { + addTypes(Collections.singletonList(type)); + } + + public static void addTypes(List ts) { + if (!ts.isEmpty()) { + visitReachable(ts, t -> !types.containsKey(t.getId()), t -> types.put(t.getId(), t)); + } + } + + /** + * Iterates all reachable types from a start collection + * + * @param rootSet the types to start from + * @param p if a type should be accepted + * @param c action to take on an accepted type + */ + private static void visitReachable(Collection rootSet, Predicate p, Consumer c) { + Queue typeQ = new ArrayDeque<>(rootSet); + while (!typeQ.isEmpty()) { + Type type = typeQ.poll(); + if (p.test(type)) { + c.accept(type); + visitAnnotations(typeQ, type.getAnnotationElements()); + for (ValueDescriptor v : type.getFields()) { + typeQ.add(PrivateAccess.getInstance().getType(v)); + visitAnnotations(typeQ, v.getAnnotationElements()); + } + if (type instanceof PlatformEventType) { + PlatformEventType pe = (PlatformEventType) type; + for (SettingDescriptor s : pe.getAllSettings()) { + typeQ.add(PrivateAccess.getInstance().getType(s)); + visitAnnotations(typeQ, s.getAnnotationElements()); + } + } + } + } + } + + private static void visitAnnotations(Queue typeQ, List aes) { + Queue aQ = new ArrayDeque<>(aes); + Set visited = new HashSet<>(); + while (!aQ.isEmpty()) { + AnnotationElement ae = aQ.poll(); + if (!visited.contains(ae)) { + Type ty = PrivateAccess.getInstance().getType(ae); + typeQ.add(ty); + visited.add(ae); + } + aQ.addAll(ae.getAnnotationElements()); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/Utils.java 2019-02-08 18:32:34.598215255 +0300 @@ -0,0 +1,508 @@ +/* + * 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 static java.util.concurrent.TimeUnit.MICROSECONDS; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.RandomAccessFile; +import java.lang.annotation.Annotation; +import java.lang.annotation.Repeatable; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.file.Path; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import jdk.internal.org.objectweb.asm.ClassReader; +import jdk.internal.org.objectweb.asm.util.CheckClassAdapter; +import jdk.jfr.Event; +import jdk.jfr.FlightRecorderPermission; +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.jfr.internal.handlers.EventHandler; +import jdk.jfr.internal.settings.PeriodSetting; +import jdk.jfr.internal.settings.StackTraceSetting; +import jdk.jfr.internal.settings.ThresholdSetting; + +public final class Utils { + + private static Boolean SAVE_GENERATED; + + public static final String EVENTS_PACKAGE_NAME = "jdk.jfr.events"; + public static final String INSTRUMENT_PACKAGE_NAME = "jdk.jfr.internal.instrument"; + public static final String HANDLERS_PACKAGE_NAME = "jdk.jfr.internal.handlers"; + public static final String REGISTER_EVENT = "registerEvent"; + public static final String ACCESS_FLIGHT_RECORDER = "accessFlightRecorder"; + + private final static String LEGACY_EVENT_NAME_PREFIX = "com.oracle.jdk."; + + public static void checkAccessFlightRecorder() throws SecurityException { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new FlightRecorderPermission(ACCESS_FLIGHT_RECORDER)); + } + } + + public static void checkRegisterPermission() throws SecurityException { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new FlightRecorderPermission(REGISTER_EVENT)); + } + } + + private static enum TimespanUnit { + NANOSECONDS("ns", 1000), MICROSECONDS("us", 1000), MILLISECONDS("ms", 1000), SECONDS("s", 60), MINUTES("m", 60), HOURS("h", 24), DAYS("d", 7); + + final String text; + final long amount; + + TimespanUnit(String unit, long amount) { + this.text = unit; + this.amount = amount; + } + } + + public static String formatBytes(long bytes, String separation) { + if (bytes < 1024) { + return bytes + " bytes"; + } + int exp = (int) (Math.log(bytes) / Math.log(1024)); + char bytePrefix = "kMGTPE".charAt(exp - 1); + return String.format("%.1f%s%cB", bytes / Math.pow(1024, exp), separation, bytePrefix); + } + + public static String formatTimespan(Duration dValue, String separation) { + if (dValue == null) { + return "0"; + } + + long value = dValue.toNanos(); + TimespanUnit result = TimespanUnit.NANOSECONDS; + for (TimespanUnit unit : TimespanUnit.values()) { + result = unit; + long amount = unit.amount; + if (result == TimespanUnit.DAYS || value < amount || value % amount != 0) { + break; + } + value /= amount; + } + return String.format("%d%s%s", value, separation, result.text); + } + + public static long parseTimespan(String s) { + if (s.endsWith("ns")) { + return Long.parseLong(s.substring(0, s.length() - 2).trim()); + } + if (s.endsWith("us")) { + return NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 2).trim()), MICROSECONDS); + } + if (s.endsWith("ms")) { + return NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 2).trim()), MILLISECONDS); + } + if (s.endsWith("s")) { + return NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS); + } + if (s.endsWith("m")) { + return 60 * NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS); + } + if (s.endsWith("h")) { + return 60 * 60 * NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS); + } + if (s.endsWith("d")) { + return 24 * 60 * 60 * NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS); + } + + try { + Long.parseLong(s); + } catch (NumberFormatException nfe) { + throw new NumberFormatException("'" + s + "' is not a valid timespan. Shoule be numeric value followed by a unit, i.e. 20 ms. Valid units are ns, us, s, m, h and d."); + } + // Only accept values with units + throw new NumberFormatException("Timespan + '" + s + "' is missing unit. Valid units are ns, us, s, m, h and d."); + } + + /** + * Return all annotations as they are visible in the source code + * + * @param clazz class to return annotations from + * + * @return list of annotation + * + */ + static List getAnnotations(Class clazz) { + List annos = new ArrayList<>(); + for (Annotation a : clazz.getAnnotations()) { + annos.addAll(getAnnotation(a)); + } + return annos; + } + + private static List getAnnotation(Annotation a) { + Class annotated = a.annotationType(); + Method valueMethod = getValueMethod(annotated); + if (valueMethod != null) { + Class returnType = valueMethod.getReturnType(); + if (returnType.isArray()) { + Class candidate = returnType.getComponentType(); + Repeatable r = candidate.getAnnotation(Repeatable.class); + if (r != null) { + Class repeatClass = r.value(); + if (annotated == repeatClass) { + return getAnnotationValues(a, valueMethod); + } + } + } + } + List annos = new ArrayList<>(); + annos.add(a); + return annos; + } + + static boolean isAfter(RecordingState stateToTest, RecordingState b) { + return stateToTest.ordinal() > b.ordinal(); + } + + static boolean isBefore(RecordingState stateToTest, RecordingState b) { + return stateToTest.ordinal() < b.ordinal(); + } + + static boolean isState(RecordingState stateToTest, RecordingState... states) { + for (RecordingState s : states) { + if (s == stateToTest) { + return true; + } + } + return false; + } + + private static List getAnnotationValues(Annotation a, Method valueMethod) { + try { + return Arrays.asList((Annotation[]) valueMethod.invoke(a, new Object[0])); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + return new ArrayList<>(); + } + } + + private static Method getValueMethod(Class annotated) { + try { + return annotated.getMethod("value", new Class[0]); + } catch (NoSuchMethodException e) { + return null; + } + } + + public static void touch(Path dumpFile) throws IOException { + RandomAccessFile raf = new RandomAccessFile(dumpFile.toFile(), "rw"); + raf.close(); + } + + public static Class unboxType(Class t) { + if (t == Integer.class) { + return int.class; + } + if (t == Long.class) { + return long.class; + } + if (t == Float.class) { + return float.class; + } + if (t == Double.class) { + return double.class; + } + if (t == Byte.class) { + return byte.class; + } + if (t == Short.class) { + return short.class; + } + if (t == Boolean.class) { + return boolean.class; + } + if (t == Character.class) { + return char.class; + } + return t; + } + + static long nanosToTicks(long nanos) { + return (long) (nanos * JVM.getJVM().getTimeConversionFactor()); + } + + static synchronized EventHandler getHandler(Class eventClass) { + Utils.ensureValidEventSubclass(eventClass); + try { + Field f = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER); + SecuritySupport.setAccessible(f); + return (EventHandler) f.get(null); + } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { + throw new InternalError("Could not access event handler"); + } + } + + static synchronized void setHandler(Class eventClass, EventHandler handler) { + Utils.ensureValidEventSubclass(eventClass); + try { + Field field = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER); + SecuritySupport.setAccessible(field); + field.set(null, handler); + } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { + throw new InternalError("Could not access event handler"); + } + } + + public static Map sanitizeNullFreeStringMap(Map settings) { + HashMap map = new HashMap<>(settings.size()); + for (Map.Entry e : settings.entrySet()) { + String key = e.getKey(); + if (key == null) { + throw new NullPointerException("Null key is not allowed in map"); + } + String value = e.getValue(); + if (value == null) { + throw new NullPointerException("Null value is not allowed in map"); + } + map.put(key, value); + } + return map; + } + + public static List sanitizeNullFreeList(List elements, Class clazz) { + List sanitized = new ArrayList<>(elements.size()); + for (T element : elements) { + if (element == null) { + throw new NullPointerException("Null is not an allowed element in list"); + } + if (element.getClass() != clazz) { + throw new ClassCastException(); + } + sanitized.add(element); + } + return sanitized; + } + + static List getVisibleEventFields(Class clazz) { + Utils.ensureValidEventSubclass(clazz); + List fields = new ArrayList<>(); + for (Class c = clazz; c != Event.class; c = c.getSuperclass()) { + for (Field field : c.getDeclaredFields()) { + // skip private field in base classes + if (c == clazz || !Modifier.isPrivate(field.getModifiers())) { + fields.add(field); + } + } + } + return fields; + } + + public static void ensureValidEventSubclass(Class eventClass) { + if (Event.class.isAssignableFrom(eventClass) && Modifier.isAbstract(eventClass.getModifiers())) { + throw new IllegalArgumentException("Abstract event classes are not allowed"); + } + if (eventClass == Event.class || !Event.class.isAssignableFrom(eventClass)) { + throw new IllegalArgumentException("Must be a subclass to " + Event.class.getName()); + } + } + + public static void writeGeneratedASM(String className, byte[] bytes) { + if (SAVE_GENERATED == null) { + // We can't calculate value statically because it will force + // initialization of SecuritySupport, which cause + // UnsatisfiedLinkedError on JDK 8 or non-Oracle JDKs + SAVE_GENERATED = SecuritySupport.getBooleanProperty("jfr.save.generated.asm"); + } + if (SAVE_GENERATED) { + try { + try (FileOutputStream fos = new FileOutputStream(className + ".class")) { + fos.write(bytes); + } + + try (FileWriter fw = new FileWriter(className + ".asm"); PrintWriter pw = new PrintWriter(fw)) { + ClassReader cr = new ClassReader(bytes); + CheckClassAdapter.verify(cr, true, pw); + } + Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.INFO, "Instrumented code saved to " + className + ".class and .asm"); + } catch (IOException e) { + Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.INFO, "Could not save instrumented code, for " + className + ".class and .asm"); + } + } + } + + public static void ensureInitialized(Class eventClass) { + SecuritySupport.ensureClassIsInitialized(eventClass); + } + + public static Object makePrimitiveArray(String typeName, List values) { + int length = values.size(); + switch (typeName) { + case "int": + int[] ints = new int[length]; + for (int i = 0; i < length; i++) { + ints[i] = (int) values.get(i); + } + return ints; + case "long": + long[] longs = new long[length]; + for (int i = 0; i < length; i++) { + longs[i] = (long) values.get(i); + } + return longs; + + case "float": + float[] floats = new float[length]; + for (int i = 0; i < length; i++) { + floats[i] = (float) values.get(i); + } + return floats; + + case "double": + double[] doubles = new double[length]; + for (int i = 0; i < length; i++) { + doubles[i] = (double) values.get(i); + } + return doubles; + + case "short": + short[] shorts = new short[length]; + for (int i = 0; i < length; i++) { + shorts[i] = (short) values.get(i); + } + return shorts; + case "char": + char[] chars = new char[length]; + for (int i = 0; i < length; i++) { + chars[i] = (char) values.get(i); + } + return chars; + case "byte": + byte[] bytes = new byte[length]; + for (int i = 0; i < length; i++) { + bytes[i] = (byte) values.get(i); + } + return bytes; + case "boolean": + boolean[] booleans = new boolean[length]; + for (int i = 0; i < length; i++) { + booleans[i] = (boolean) values.get(i); + } + return booleans; + case "java.lang.String": + String[] strings = new String[length]; + for (int i = 0; i < length; i++) { + strings[i] = (String) values.get(i); + } + return strings; + } + return null; + } + + public static boolean isSettingVisible(Control c, boolean hasEventHook) { + if (c instanceof ThresholdSetting) { + return !hasEventHook; + } + if (c instanceof PeriodSetting) { + return hasEventHook; + } + if (c instanceof StackTraceSetting) { + return !hasEventHook; + } + return true; + } + + public static boolean isSettingVisible(long typeId, boolean hasEventHook) { + if (ThresholdSetting.isType(typeId)) { + return !hasEventHook; + } + if (PeriodSetting.isType(typeId)) { + return hasEventHook; + } + if (StackTraceSetting.isType(typeId)) { + return !hasEventHook; + } + return true; + } + + public static Type getValidType(Class type, String name) { + Objects.requireNonNull(type, "Null is not a valid type for value descriptor " + name); + if (type.isArray()) { + type = type.getComponentType(); + if (type != String.class && !type.isPrimitive()) { + throw new IllegalArgumentException("Only arrays of primitives and Strings are allowed"); + } + } + + Type knownType = Type.getKnownType(type); + if (knownType == null || knownType == Type.STACK_TRACE) { + throw new IllegalArgumentException("Only primitive types, java.lang.Thread, java.lang.String and java.lang.Class are allowed for value descriptors. " + type.getName()); + } + return knownType; + } + + public static List smallUnmodifiable(List list) { + if (list.isEmpty()) { + return Collections.emptyList(); + } + if (list.size() == 1) { + return Collections.singletonList(list.get(0)); + } + return Collections.unmodifiableList(list); + } + + public static String upgradeLegacyJDKEvent(String eventName) { + if (eventName.length() <= LEGACY_EVENT_NAME_PREFIX.length()) { + return eventName; + } + if (eventName.startsWith(LEGACY_EVENT_NAME_PREFIX)) { + int index = eventName.lastIndexOf("."); + if (index == LEGACY_EVENT_NAME_PREFIX.length() - 1) { + return Type.EVENT_NAME_PREFIX + eventName.substring(index + 1); + } + } + return eventName; + } + + public static String makeFilename(Recording recording) { + String pid = JVM.getJVM().getPid(); + String date = Repository.REPO_DATE_FORMAT.format(LocalDateTime.now()); + String idText = recording == null ? "" : "-id-" + Long.toString(recording.getId()); + return "hotspot-" + "pid-" + pid + idText + "-" + date + ".jfr"; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/WriteableUserPath.java 2019-02-08 18:32:34.742210218 +0300 @@ -0,0 +1,129 @@ +/* + * 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.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.Callable; + +/** + * Purpose of this class is to simplify analysis of security risks. + *

+ * Paths in the public API should be wrapped in this class so we + * at all time know what kind of paths we are dealing with. + *

+ * A user supplied path must never be used in an unsafe context, such as a + * shutdown hook or any other thread created by JFR. + *

+ * All operation using this path must happen in {@link #doPriviligedIO(Callable)} + */ +public final class WriteableUserPath { + private final AccessControlContext controlContext; + private final Path original; + private final Path real; + private final String text; + + // Not to ensure security, but to help + // against programming errors + private volatile boolean inPrivileged; + + public WriteableUserPath(Path path) throws IOException { + controlContext = AccessController.getContext(); + // verify that the path is writeable + if (Files.exists(path) && !Files.isWritable(path)) { + // throw same type of exception as FileOutputStream + // constructor, if file can't be opened. + throw new FileNotFoundException("Could not write to file: " + path.toAbsolutePath()); + } + // will throw if non-writeable + BufferedWriter fw = Files.newBufferedWriter(path); + fw.close(); + this.original = path; + this.real = path.toRealPath(); + this.text = real.toString(); + } + + /** + * Returns a potentially malicious path where the user may have implemented + * their own version of Path. This method should never be called in an + * unsafe context and the Path value should never be passed along to other + * methods. + * + * @return path from a potentially malicious user + */ + public Path getPotentiallyMaliciousOriginal() { + return original; + } + + /** + * Returns a string representation of the path. + * + * @return path as text + */ + public String getText() { + return text; + } + + /** + * Returns a potentially malicious path where the user may have implemented + * their own version of Path. This method should never be called in an + * unsafe context and the Path value should never be passed along to other + * methods. + * + * @return path from a potentially malicious user + */ + public Path getReal() { + if (!inPrivileged) { + throw new InternalError("A user path was accessed outside the context it was supplied in"); + } + return real; + } + + public void doPriviligedIO(Callable function) throws IOException { + try { + inPrivileged = true; + AccessController.doPrivileged(new PrivilegedExceptionAction() { + @Override + public Void run() throws Exception { + function.call(); + return null; + } + }, controlContext); + } catch (Throwable t) { + // prevent malicious user to propagate exception callback + // in the wrong context + throw new IOException("Unexpected error during I/O operation"); + } finally { + inPrivileged = false; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/cmd/Command.java 2019-02-08 18:32:34.890205040 +0300 @@ -0,0 +1,150 @@ +/* + * 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.cmd; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Deque; +import java.util.List; + +abstract class Command { + + private final static Command HELP = new HelpCommand(); + private final static List COMMANDS = createCommands(); + + static void displayHelp() { + System.out.println("Usage: java " + Execute.class.getName() + " []"); + System.out.println(); + displayAvailableCommands(); + } + + static void displayAvailableCommands() { + System.out.println("Available commands are:"); + System.out.println(); + boolean first = true; + for (Command c : Command.COMMANDS) { + if (!first) { + System.out.println(); + } + System.out.println(" " + c.getName() + " " + c.getOptionSyntax()); + System.out.println(" " + c.getDescription()); + first = false; + } + } + + public static List getCommands() { + return COMMANDS; + } + + public static Command valueOf(String commandName) { + for (Command command : COMMANDS) { + if (command.getName().equals(commandName)) { + return command; + } + } + return null; + } + + abstract public String getOptionSyntax(); + + abstract public String getName(); + + abstract public String getDescription(); + + abstract public void displayOptionUsage(); + + abstract public void execute(Deque options); + + final protected void userFailed(String message) { + println(); + println(message); + displayUsage(); + throw new IllegalArgumentException(message); + } + + final protected void ensureMaxArgumentCount(Deque options, int maxCount) { + if (options.size() > maxCount) { + userFailed("Too many arguments"); + } + } + + final protected void ensureMinArgumentCount(Deque options, int minCount) { + if (options.size() < minCount) { + userFailed("Too few arguments"); + } + } + + final protected void ensureFileExist(Path file) { + if (!Files.exists(file)) { + userFailed("Could not find file " + file); + } + } + + final protected Path ensureFileDoesNotExist(Path file) { + if (Files.exists(file)) { + userFailed("File " + file + " already exists"); + } + return file; + } + + final protected void ensureJFRFile(Path path) { + if (!path.toString().endsWith(".jfr")) { + userFailed("Filename must end with .jfr"); + } + } + + final protected void displayUsage() { + String javaText = "java " + Execute.class.getName(); + println(); + println("Usage: " + javaText + " " + getName() + " " + getOptionSyntax()); + println(); + displayOptionUsage(); + } + + final protected void println() { + System.out.println(); + } + + final protected void print(String text) { + System.out.print(text); + } + + final protected void println(String text) { + System.out.println(text); + } + + private static List createCommands() { + List commands = new ArrayList<>(); + commands.add(new PrintCommand()); + commands.add(new SummaryCommand()); + commands.add(new ReconstructCommand()); + commands.add(new SplitCommand()); + commands.add(HELP); + return Collections.unmodifiableList(commands); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/cmd/Execute.java 2019-02-08 18:32:35.034200004 +0300 @@ -0,0 +1,65 @@ +/* + * 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.cmd; + +import java.util.Arrays; +import java.util.Deque; +import java.util.LinkedList; + +/** + * Launcher class for JFR tools + * + */ +public final class Execute { + + public static void main(String... args) { + Deque argList = new LinkedList<>(Arrays.asList(args)); + if (argList.isEmpty()) { + System.out.println(); + Command.displayHelp(); + return; + } + String command = argList.remove(); + for (Command c : Command.getCommands()) { + if (c.getName().equals(command)) { + try { + c.execute(argList); + } catch (IllegalArgumentException iae) { + return; // already handled by command + } catch (Throwable e) { + System.out.println(); + System.out.println(e.getMessage()); + System.out.println(); + } + return; + } + } + System.out.println(); + System.out.println("Unknown command " + command + "."); + System.out.println(); + Command.displayHelp(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/cmd/HelpCommand.java 2019-02-08 18:32:35.178194967 +0300 @@ -0,0 +1,68 @@ +/* + * 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.cmd; + +import java.util.Deque; + +final class HelpCommand extends Command { + + @Override + public String getOptionSyntax() { + return "[]"; + } + + @Override + public void displayOptionUsage() { + println(" The name of the command to get help for"); + println(); + Command.displayAvailableCommands(); + } + + @Override + public String getName() { + return "help"; + } + + @Override + public String getDescription() { + return "Display help about a command"; + } + + @Override + public void execute(Deque options) { + if (options.isEmpty()) { + displayUsage(); + } else { + ensureMaxArgumentCount(options, 1); + String commandName = options.remove(); + Command c = Command.valueOf(commandName); + if (c == null) { + userFailed("Unknown command " + commandName); + } + c.displayUsage(); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/cmd/JSONWriter.java 2019-02-08 18:32:35.322189931 +0300 @@ -0,0 +1,258 @@ +/* + * 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.cmd; + +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Path; + +import jdk.jfr.EventType; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.consumer.RecordingFile; + +final class JSONWriter extends StructuredWriter { + + public JSONWriter(PrintWriter writer) { + super(writer); + } + + public void print(Path source) throws IOException { + try (RecordingFile es = new RecordingFile(source)) { + printObjectBegin(); + printRecording(es); + printObjectEnd(); + flush(); + } + } + + private void printRecording(RecordingFile es) throws IOException { + printDataStructureName("recording"); + printObjectBegin(); + printEvents(es); + printObjectEnd(); + } + + private void printEvents(RecordingFile es) throws IOException { + printDataStructureName("events"); + printArrayBegin(); + boolean first = true; + while (es.hasMoreEvents()) { + RecordedEvent e = es.readEvent(); + printNewDataStructure(first, true, null); + printEvent(e); + flush(); + first = false; + } + printArrayEnd(); + } + + private void printEvent(RecordedEvent e) { + printObjectBegin(); + EventType type = e.getEventType(); + printValue(true, false, "name", type.getName()); + printValue(false, false, "typeId", type.getId()); + printValue(false, false, "startTime", e.getStartTime()); + printValue(false, false, "duration", e.getDuration()); + printNewDataStructure(false, false, "values"); + printObject(e); + printObjectEnd(); + } + + void printValue(boolean first, boolean arrayElement, String name, Object value) { + printNewDataStructure(first, arrayElement, name); + if (!printIfNull(value)) { + if (value instanceof Boolean) { + printAsString(value); + return; + } + if (value instanceof Double) { + Double dValue = (Double) value; + if (Double.isNaN(dValue) || Double.isInfinite(dValue)) { + printNull(); + return; + } + printAsString(value); + return; + } + if (value instanceof Float) { + Float fValue = (Float) value; + if (Float.isNaN(fValue) || Float.isInfinite(fValue)) { + printNull(); + return; + } + printAsString(value); + return; + } + if (value instanceof Number) { + printAsString(value); + return; + } + print("\""); + printEscaped(String.valueOf(value)); + print("\""); + } + } + + public void printObject(RecordedObject object) { + printObjectBegin(); + boolean first = true; + for (ValueDescriptor v : object.getFields()) { + printValueDescriptor(first, false, v, object.getValue(v.getName())); + first = false; + } + printObjectEnd(); + } + + private void printArray(ValueDescriptor v, Object[] array) { + printArrayBegin(); + boolean first = true; + for (Object arrayElement : array) { + printValueDescriptor(first, true, v, arrayElement); + first = false; + } + printArrayEnd(); + } + + private void printValueDescriptor(boolean first, boolean arrayElement, ValueDescriptor vd, Object value) { + if (vd.isArray() && !arrayElement) { + printNewDataStructure(first, arrayElement, vd.getName()); + if (!printIfNull(value)) { + printArray(vd, (Object[]) value); + } + return; + } + if (!vd.getFields().isEmpty()) { + printNewDataStructure(first, arrayElement, vd.getName()); + if (!printIfNull(value)) { + printObject((RecordedObject) value); + } + return; + } + printValue(first, arrayElement, vd.getName(), value); + } + + private void printNewDataStructure(boolean first, boolean arrayElement, String name) { + if (!first) { + print(", "); + if (!arrayElement) { + println(); + } + } + if (!arrayElement) { + printDataStructureName(name); + } + } + + private boolean printIfNull(Object value) { + if (value == null) { + printNull(); + return true; + } + return false; + } + + private void printNull() { + print("null"); + } + + private void printDataStructureName(String text) { + printIndent(); + print("\""); + print(text); + print("\": "); + } + + private void printObjectEnd() { + retract(); + println(); + printIndent(); + print("}"); + } + + private void printObjectBegin() { + println("{"); + indent(); + } + + private void printArrayEnd() { + print("]"); + } + + private void printArrayBegin() { + print("["); + } + + private void printEscaped(String text) { + for (int i = 0; i < text.length(); i++) { + printEscaped(text.charAt(i)); + } + } + + private void printEscaped(char c) { + if (c == '\b') { + print("\\b"); + return; + } + if (c == '\n') { + print("\\n"); + return; + } + if (c == '\t') { + print("\\t"); + return; + } + if (c == '\f') { + print("\\f"); + return; + } + if (c == '\r') { + print("\\r"); + return; + } + if (c == '\"') { + print("\\\""); + return; + } + if (c == '\\') { + print("\\\\"); + return; + } + if (c == '/') { + print("\\/"); + return; + } + if (c > 0x7F || c < 32) { + print("\\u"); + // 0x10000 will pad with zeros. + print(Integer.toHexString(0x10000 + (int) c).substring(1)); + return; + } + print(c); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/cmd/PrettyWriter.java 2019-02-08 18:32:35.466184895 +0300 @@ -0,0 +1,236 @@ +/* + * 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.cmd; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.StringJoiner; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.consumer.RecordingFile; +import jdk.jfr.internal.PrivateAccess; +import jdk.jfr.internal.Type; +import jdk.jfr.internal.consumer.ChunkHeader; +import jdk.jfr.internal.consumer.RecordingInput; + +public final class PrettyWriter extends StructuredWriter { + + public PrettyWriter(PrintWriter destination) { + super(destination); + } + + void print(Path source) throws FileNotFoundException, IOException { + try (RecordingInput input = new RecordingInput(source.toFile())) { + HashSet typeSet = new HashSet<>(); + for (ChunkHeader ch = new ChunkHeader(input); !ch.isLastChunk(); ch = ch.nextHeader()) { + typeSet.addAll(ch.readMetadata().getTypes()); + } + List types = new ArrayList<>(typeSet); + Collections.sort(types, (c1, c2) -> Long.compare(c1.getId(), c2.getId())); + for (Type t : types) { + printType(t); + } + flush(); + } + + try (RecordingFile es = new RecordingFile(source)) { + while (es.hasMoreEvents()) { + print(es.readEvent()); + flush(); + } + } + flush(); + } + + public void printType(Type t) throws IOException { + print("// id: "); + println(String.valueOf(t.getId())); + int commentIndex = t.getName().length() + 10; + String typeName = t.getName(); + int index = typeName.lastIndexOf("."); + if (index != -1) { + println("package " + typeName.substring(0, index) + ";"); + } + printAnnotations(commentIndex, t.getAnnotationElements()); + print("class " + typeName.substring(index + 1)); + String superType = t.getSuperType(); + if (superType != null) { + print(" extends " + superType); + } + println(" {"); + indent(); + for (ValueDescriptor v : t.getFields()) { + printField(commentIndex, v); + } + retract(); + println("}"); + println(); + } + + private void printField(int commentIndex, ValueDescriptor v) throws IOException { + println(); + printAnnotations(commentIndex, v.getAnnotationElements()); + printIndent(); + Type vType = PrivateAccess.getInstance().getType(v); + if (Type.SUPER_TYPE_SETTING.equals(vType.getSuperType())) { + print("static "); + } + print(makeSimpleType(v.getTypeName())); + if (v.isArray()) { + print("[]"); + } + print(" "); + print(v.getName()); + print(";"); + printCommentRef(commentIndex, v.getTypeId()); + } + + private void printCommentRef(int commentIndex, long typeId) throws IOException { + int column = getColumn(); + if (column > commentIndex) { + print(" "); + } else { + while (column < commentIndex) { + print(" "); + column++; + } + } + println(" // id=" + typeId); + } + + private void printAnnotations(int commentIndex, List annotations) throws IOException { + for (AnnotationElement a : annotations) { + printIndent(); + print("@"); + print(makeSimpleType(a.getTypeName())); + List vs = a.getValueDescriptors(); + if (!vs.isEmpty()) { + printAnnotation(a); + printCommentRef(commentIndex, a.getTypeId()); + } else { + println(); + } + } + } + + private void printAnnotation(AnnotationElement a) throws IOException { + StringJoiner sj = new StringJoiner(", ", "(", ")"); + List vs = a.getValueDescriptors(); + for (ValueDescriptor v : vs) { + Object o = a.getValue(v.getName()); + if (vs.size() == 1 && v.getName().equals("value")) { + sj.add(textify(o)); + } else { + sj.add(v.getName() + "=" + textify(o)); + } + } + print(sj.toString()); + } + + private String textify(Object o) { + if (o.getClass().isArray()) { + Object[] array = (Object[]) o; + if (array.length == 1) { + return quoteIfNeeded(array[0]); + } + StringJoiner s = new StringJoiner(", ", "{", "}") ; + for (Object ob : array) { + s.add(quoteIfNeeded(ob)); + } + return s.toString(); + } else { + return quoteIfNeeded(o); + } + } + + private String quoteIfNeeded(Object o) { + if (o instanceof String) { + return "\"" + o + "\""; + } else { + return String.valueOf(o); + } + } + + private String makeSimpleType(String typeName) { + int index = typeName.lastIndexOf("."); + return typeName.substring(index + 1); + } + + public void print(RecordedEvent event) throws IOException { + print(makeSimpleType(event.getEventType().getName()), " "); + print((RecordedObject) event, ""); + } + + public void print(RecordedObject struct, String postFix) throws IOException { + println("{"); + indent(); + for (ValueDescriptor v : struct.getFields()) { + printIndent(); + print(v.getName(), " = "); + printValue(struct.getValue(v.getName()), ""); + } + retract(); + printIndent(); + println("}" + postFix); + } + + private void printArray(Object[] array) throws IOException { + println("["); + indent(); + for (int i = 0; i < array.length; i++) { + printIndent(); + printValue(array[i], i + 1 < array.length ? ", " : ""); + } + retract(); + printIndent(); + println("]"); + } + + private void printValue(Object value, String postFix) throws IOException { + if (value == null) { + println("null" + postFix); + } else if (value instanceof RecordedObject) { + print((RecordedObject) value, postFix); + } else if (value.getClass().isArray()) { + printArray((Object[]) value); + } else { + String text = String.valueOf(value); + if (value instanceof String) { + text = "\"" + text + "\""; + } + println(text); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/cmd/PrintCommand.java 2019-02-08 18:32:35.610179858 +0300 @@ -0,0 +1,100 @@ +/* + * 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.cmd; + +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Deque; + +final class PrintCommand extends Command { + @Override + public String getName() { + return "print"; + } + + @Override + public String getOptionSyntax() { + return "[--xml|--json] "; + } + + @Override + public String getDescription() { + return "Print contents of a recording file (.jfr)"; + } + + @Override + public void displayOptionUsage() { + println(" --xml Print a recording in XML format"); + println(); + println(" --json Print a recording in JSON format"); + println(); + println(" Location of the recording file (.jfr) to print"); + } + + @Override + public void execute(Deque options) { + if (options.isEmpty()) { + userFailed("Missing file"); + } + ensureMaxArgumentCount(options, 2); + + Path file = Paths.get(options.removeLast()); + + ensureFileExist(file); + ensureJFRFile(file); + ensureMaxArgumentCount(options, 1); + + String format = "--pretty"; + if (!options.isEmpty()) { + format = options.remove(); + } + try (PrintWriter pw = new PrintWriter(System.out)) { + try { + switch (format) { + case "--pretty": + PrettyWriter prettyWriter = new PrettyWriter(pw); + prettyWriter.print(file); + break; + case "--xml": + XMLWriter xmlPrinter = new XMLWriter(pw); + xmlPrinter.print(file); + break; + case "--json": + JSONWriter jsonWriter = new JSONWriter(pw); + jsonWriter.print(file); + break; + default: + userFailed("Unknown option " + format); + break; + } + } catch (IOException ioe) { + userFailed("Could not read recording at " + file.toAbsolutePath() + ". " + ioe.getMessage()); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/cmd/ReconstructCommand.java 2019-02-08 18:32:35.754174822 +0300 @@ -0,0 +1,131 @@ +/* + * 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.cmd; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; + +final class ReconstructCommand extends Command { + + @Override + public String getOptionSyntax() { + return " "; + } + + @Override + public String getName() { + return "reconstruct"; + } + + @Override + public String getDescription() { + return "Assemble leftover chunks, from a disk repository, into a recording file (.jfr)"; + } + + @Override + public void displayOptionUsage() { + println(" Directory where the repository is located"); + println(); + println(" Name of the recording file (.jfr) to create"); + } + + @Override + public void execute(Deque options) { + ensureMinArgumentCount(options, 2); + ensureMaxArgumentCount(options, 2); + + Path repository = Paths.get(options.pop()).toAbsolutePath(); + if (!Files.exists(repository)) { + userFailed("Could not find disk repository at " + repository); + } + if (!Files.isDirectory(repository)) { + userFailed("Must specify a directory as disk repository"); + } + Path output = Paths.get(options.pop()); + ensureFileDoesNotExist(output); + ensureJFRFile(output); + + try (FileOutputStream fos = new FileOutputStream(output.toFile())) { + List files = listJFRFiles(repository); + if (files.isEmpty()) { + throw new IllegalStateException("No *.jfr files found at " + repository); + } + println(); + println("Combining files... "); + println(); + transferTo(files, output, fos.getChannel()); + println(); + println("Reconstruction complete."); + } catch (IOException e) { + userFailed("Could not open destination file " + output + ". " + e.getMessage()); + } + } + + private List listJFRFiles(Path path) throws IOException { + try { + List files = new ArrayList<>(); + if (Files.isDirectory(path)) { + try (DirectoryStream stream = Files.newDirectoryStream(path, "*.jfr")) { + for (Path p : stream) { + if (!Files.isDirectory(p) && Files.isReadable(p)) { + files.add(p); + } + } + } + } + files.sort((u, v) -> u.getFileName().compareTo(v.getFileName())); + return files; + } catch (IOException ioe) { + throw new IllegalStateException("Could not list *.jfr for directory " + path + ". " + ioe.getMessage()); + } + } + + private void transferTo(List sourceFiles, Path output, FileChannel out) { + long pos = 0; + for (Path p : sourceFiles) { + println(" " + p.toString()); + try (FileChannel sourceChannel = FileChannel.open(p)) { + long rem = Files.size(p); + while (rem > 0) { + long n = Math.min(rem, 1024 * 1024); + long w = out.transferFrom(sourceChannel, pos, n); + pos += w; + rem -= w; + } + } catch (IOException ioe) { + throw new IllegalStateException("Could not copy recording chunk " + p + " to new file. " + ioe.getMessage()); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/cmd/SplitCommand.java 2019-02-08 18:32:35.898169786 +0300 @@ -0,0 +1,195 @@ +/* + * 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.cmd; + +import java.io.BufferedInputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; + +import jdk.jfr.internal.consumer.ChunkHeader; +import jdk.jfr.internal.consumer.RecordingInput; + +final class SplitCommand extends Command { + + @Override + public String getOptionSyntax() { + return "[--maxchunks ] "; + } + + @Override + public void displayOptionUsage() { + println(" --maxchunks Maximum number of chunks per splitted file (default 5)."); + println(" The chunk size varies, but is typically around 15 MB."); + println(); + println(" Location of recording file (.jfr) to split"); + } + + @Override + public String getName() { + return "split"; + } + + @Override + public String getDescription() { + return "Splits a recording file into smaller files"; + } + + @Override + public void execute(Deque options) { + if (options.isEmpty()) { + userFailed("Missing file"); + } + ensureMaxArgumentCount(options, 3); + Path file = Paths.get(options.removeLast()); + ensureFileExist(file); + ensureJFRFile(file); + int maxchunks = 5; + if (!options.isEmpty()) { + String option = options.pop(); + if (!"--maxchunks".equals(option)) { + userFailed("Unknown option " + option); + } + if (options.isEmpty()) { + userFailed("Missing value for --maxChunks"); + } + String value = options.pop(); + try { + maxchunks = Integer.parseInt(value); + if (maxchunks < 1) { + userFailed("Must be at least one chunk per file."); + } + } catch (NumberFormatException nfe) { + userFailed("Not a valid value for --maxchunks."); + } + } + ensureMaxArgumentCount(options, 0); + println(); + println("Examining recording " + file + " ..."); + List sizes; + + try { + sizes = findChunkSizes(file); + } catch (IOException e) { + throw new IllegalStateException("Unexpected error. " + e.getMessage()); + } + if (sizes.size() <= maxchunks) { + throw new IllegalStateException("Number of chunks in recording (" + sizes.size() + ") doesn't exceed max chunks (" + maxchunks + ")"); + } + println(); + + println(); + if (sizes.size() > 0) { + print("File consists of " + sizes.size() + " chunks. The recording will be split into "); + sizes = combineChunkSizes(sizes, maxchunks); + println(sizes.size() + " files with at most " + maxchunks + " chunks per file."); + println(); + + try { + splitFile(file, sizes); + } catch (IOException e) { + throw new IllegalStateException("Unexpected error. " + e.getMessage()); + } + } else { + println("No JFR chunks found in file. "); + } + } + + private List findChunkSizes(Path p) throws IOException { + try (RecordingInput input = new RecordingInput(p.toFile())) { + List sizes = new ArrayList<>(); + ChunkHeader ch = new ChunkHeader(input); + sizes.add(ch.getSize()); + while (!ch.isLastChunk()) { + ch = ch.nextHeader(); + sizes.add(ch.getSize()); + } + return sizes; + } + } + + private List combineChunkSizes(List sizes, int chunksPerFile) { + List reduced = new ArrayList(); + long size = sizes.get(0); + for (int n = 1; n < sizes.size(); n++) { + if (n % chunksPerFile == 0) { + reduced.add(size); + size = 0; + } + size += sizes.get(n); + } + reduced.add(size); + return reduced; + } + + private void splitFile(Path file, List splitPositions) throws IOException { + + int padAmountZeros = String.valueOf(splitPositions.size() - 1).length(); + String fileName = file.toString(); + String fileFormatter = fileName.subSequence(0, fileName.length() - 4) + "_%0" + padAmountZeros + "d.jfr"; + for (int i = 0; i < splitPositions.size(); i++) { + Path p = Paths.get(String.format(fileFormatter, i)); + if (Files.exists(p)) { + throw new IllegalStateException("Can't create split file " + p + ", a file with that name already exist"); + } + } + DataInputStream stream = new DataInputStream(new BufferedInputStream(new FileInputStream(file.toFile()))); + + for (int i = 0; i < splitPositions.size(); i++) { + Long l = splitPositions.get(i); + byte[] bytes = readBytes(stream, l.intValue()); + Path p = Paths.get(String.format(fileFormatter, i)); + File splittedFile = p.toFile(); + println("Writing " + splittedFile + " ..."); + FileOutputStream fos = new FileOutputStream(splittedFile); + fos.write(bytes); + fos.close(); + } + stream.close(); + } + + private byte[] readBytes(InputStream stream, int count) throws IOException { + byte[] data = new byte[count]; + int totalRead = 0; + while (totalRead < data.length) { + int read = stream.read(data, totalRead, data.length - totalRead); + if (read == -1) { + throw new IOException("Unexpected end of data."); + } + totalRead += read; + } + return data; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/cmd/StructuredWriter.java 2019-02-08 18:32:36.042164749 +0300 @@ -0,0 +1,111 @@ +/* + * 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.cmd; + +import java.io.PrintWriter; + +abstract class StructuredWriter { + private final static String LINE_SEPARATOR = String.format("%n"); + + private final PrintWriter out; + private final StringBuilder builder = new StringBuilder(4000); + + private char[] indentionArray = new char[0]; + private int indent = 0; + private int column; + + StructuredWriter(PrintWriter p) { + out = p; + } + + final protected int getColumn() { + return column; + } + + // Flush to print writer + public final void flush() { + out.print(builder.toString()); + builder.setLength(0); + } + + final public void printIndent() { + builder.append(indentionArray, 0, indent); + column += indent; + } + + final public void println() { + builder.append(LINE_SEPARATOR); + column = 0; + } + + final public void print(String... texts) { + for (String text : texts) { + print(text); + } + } + + final public void printAsString(Object o) { + print(String.valueOf(o)); + } + + final public void print(String text) { + builder.append(text); + column += text.length(); + } + + final public void print(char c) { + builder.append(c); + column++; + } + + final public void print(int value) { + print(String.valueOf(value)); + } + + final public void indent() { + indent += 2; + updateIndent(); + } + + final public void retract() { + indent -= 2; + updateIndent(); + } + + final public void println(String text) { + print(text); + println(); + } + + private void updateIndent() { + if (indent > indentionArray.length) { + indentionArray = new char[indent]; + for (int i = 0; i < indentionArray.length; i++) { + indentionArray[i] = ' '; + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/cmd/SummaryCommand.java 2019-02-08 18:32:36.186159713 +0300 @@ -0,0 +1,173 @@ +/* + * 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.cmd; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.List; + +import jdk.jfr.EventType; +import jdk.jfr.internal.MetadataDescriptor; +import jdk.jfr.internal.Type; +import jdk.jfr.internal.consumer.ChunkHeader; +import jdk.jfr.internal.consumer.RecordingInput; + +final class SummaryCommand extends Command { + + private static class Statistics { + Statistics(String name) { + this.name = name; + } + + String name; + long count; + long size; + } + + @Override + public String getOptionSyntax() { + return ""; + } + + @Override + public void displayOptionUsage() { + println(" Location of the recording file (.jfr) to display information about"); + } + + @Override + public String getName() { + return "summary"; + } + + @Override + public String getDescription() { + return "Display general information about a recording file (.jfr)"; + } + + @Override + public void execute(Deque options) { + if (options.isEmpty()) { + userFailed("Missing file"); + } + ensureMaxArgumentCount(options, 1); + Path p = Paths.get(options.remove()); + ensureFileExist(p); + ensureJFRFile(p); + try { + printInformation(p); + } catch (IOException e) { + throw new IllegalStateException("Unexpected error. " + e.getMessage()); + } + } + + private void printInformation(Path p) throws IOException { + long totalSize = 0; + long totalDuration = 0; + long chunks = 0; + + try (RecordingInput input = new RecordingInput(p.toFile())) { + ChunkHeader first = new ChunkHeader(input); + ChunkHeader ch = first; + String eventPrefix = Type.EVENT_NAME_PREFIX; + if (first.getMajor() == 1) { + eventPrefix = "com.oracle.jdk."; + } + HashMap stats = new HashMap<>(); + stats.put(0L, new Statistics(eventPrefix + "Metadata")); + stats.put(1L, new Statistics(eventPrefix + "CheckPoint")); + int minWidth = 0; + while (true) { + long chunkEnd = ch.getEnd(); + MetadataDescriptor md = ch.readMetadata(); + + for (EventType eventType : md.getEventTypes()) { + stats.computeIfAbsent(eventType.getId(), (e) -> new Statistics(eventType.getName())); + minWidth = Math.max(minWidth, eventType.getName().length()); + } + + totalSize += ch.getSize(); + totalDuration += ch.getDuration(); + chunks++; + input.position(ch.getEventStart()); + while (input.position() < chunkEnd) { + + long pos = input.position(); + int size = input.readInt(); + long eventTypeId = input.readLong(); + Statistics s = stats.get(eventTypeId); + + if (s != null) { + s.count++; + s.size += size; + } + input.position(pos + size); + } + if (ch.isLastChunk()) { + break; + } + ch = ch.nextHeader(); + } + println(); + long epochSeconds = first.getStartNanos() / 1_000_000_000L; + long adjustNanos = first.getStartNanos() - epochSeconds * 1_000_000_000L; + println(" Version: " + first.getMajor() + "." + first.getMinor()); + println(" Chunks: " + chunks); + println(" Size: " + totalSize + " bytes"); + println(" Start: " + Instant.ofEpochSecond(epochSeconds, adjustNanos)); + println(" Duration: " + Duration.ofNanos(totalDuration)); + println(); + println(" Start Ticks: " + first.getStartTicks()); + println(" Ticks / Second: " + first.getTicksPerSecond()); + + List statsList = new ArrayList<>(stats.values()); + Collections.sort(statsList, (u, v) -> Long.compare(v.count, u.count)); + println(); + String header = " Count Size (bytes) "; + String typeHeader = " Event Type"; + minWidth = Math.max(minWidth, typeHeader.length()); + println(typeHeader + pad(minWidth - typeHeader.length(), ' ') + header); + println(pad(minWidth + header.length(), '=')); + for (Statistics s : statsList) { + System.out.printf(" %-" + minWidth + "s%10d %12d\n", s.name, s.count, s.size); + } + } + } + + private String pad(int count, char c) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < count; i++) { + sb.append(c); + } + return sb.toString(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/cmd/XMLWriter.java 2019-02-08 18:32:36.338154397 +0300 @@ -0,0 +1,191 @@ +/* + * 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.cmd; + +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Path; + +import jdk.jfr.EventType; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.consumer.RecordingFile; + +final class XMLWriter extends StructuredWriter { + + public XMLWriter(PrintWriter destination) { + super(destination); + } + + public void print(Path source) throws IOException { + try (RecordingFile es = new RecordingFile(source)) { + println(""); + println(""); + indent(); + printIndent(); + println(""); + indent(); + while (es.hasMoreEvents()) { + printEvent(es.readEvent()); + flush(); + } + retract(); + printIndent(); + println(""); + retract(); + println(""); + flush(); + } + } + + private void printEvent(RecordedEvent e) throws IOException { + EventType type = e.getEventType(); + printIndent(); + print(""); + printObject(e); + printIndent(); + println(""); + println(); + } + + private void printAttribute(String name, String value) { + print(" ", name, "=\"", value, "\""); + } + + public void printObject(RecordedObject struct) { + println(); + indent(); + for (ValueDescriptor v : struct.getFields()) { + printValueDescriptor(v, struct.getValue(v.getName()), -1); + } + retract(); + } + + private void printArray(ValueDescriptor v, Object[] array) { + println(); + indent(); + for (int index = 0; index < array.length; index++) { + printValueDescriptor(v, array[index], index); + } + retract(); + } + + private void printValueDescriptor(ValueDescriptor vd, Object value, int index) { + boolean arrayElement = index != -1; + String name = arrayElement ? null : vd.getName(); + if (vd.isArray() && !arrayElement) { + if (printBeginElement("array", name, value, index)) { + printArray(vd, (Object[]) value); + printIndent(); + printEndElement("array"); + } + return; + } + if (!vd.getFields().isEmpty()) { + if (printBeginElement("struct", name, value, index)) { + printObject((RecordedObject) value); + printIndent(); + printEndElement("struct"); + } + return; + } + if (printBeginElement("value", name, value, index)) { + printEscaped(String.valueOf(value)); + printEndElement("value"); + } + } + + private boolean printBeginElement(String elementName, String name, Object value, int index) { + printIndent(); + print("<", elementName); + if (name != null) { + printAttribute("name", name); + } + if (index != -1) { + printAttribute("index", Integer.toString(index)); + } + if (value == null) { + print(">"); + return false; + } + if (value.getClass().isArray()) { + Object[] array = (Object[]) value; + printAttribute("size", Integer.toString(array.length)); + } + print(">"); + return true; + } + + private void printEndElement(String elementName) { + print(""); + } + + private void printEscaped(String text) { + for (int i = 0; i < text.length(); i++) { + printEscaped(text.charAt(i)); + } + } + + private void printEscaped(char c) { + if (c == 34) { + print("""); + return; + } + if (c == 38) { + print("&"); + return; + } + if (c == 39) { + print("'"); + return; + } + if (c == 60) { + print("<"); + return; + } + if (c == 62) { + print(">"); + return; + } + if (c > 0x7F) { + print("&#"); + print((int) c); + print(';'); + return; + } + print(c); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/consumer/ChunkHeader.java 2019-02-08 18:32:36.482149361 +0300 @@ -0,0 +1,184 @@ +/* + * 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.consumer; + +import java.io.DataInput; +import java.io.IOException; + +import jdk.jfr.internal.LogLevel; +import jdk.jfr.internal.LogTag; +import jdk.jfr.internal.Logger; +import jdk.jfr.internal.MetadataDescriptor; + +public final class ChunkHeader { + private static final long METADATA_TYPE_ID = 0; + private static final byte[] FILE_MAGIC = { 'F', 'L', 'R', '\0' }; + + private final short major; + private final short minor; + private final long chunkSize; + private final long chunkStartTicks; + private final long ticksPerSecond; + private final long chunkStartNanos; + private final long metadataPosition; + // private final long absoluteInitialConstantPoolPosition; + private final long absoluteChunkEnd; + private final long absoluteEventStart; + private final long absoluteChunkStart; + private final boolean lastChunk; + private final RecordingInput input; + private final long durationNanos; + private final long id; + private long constantPoolPosition; + + public ChunkHeader(RecordingInput input) throws IOException { + this(input, 0, 0); + } + + private ChunkHeader(RecordingInput input, long absoluteChunkStart, long id) throws IOException { + input.position(absoluteChunkStart); + if (input.position() >= input.size()) { + throw new IOException("Chunk contains no data"); + } + verifyMagic(input); + this.input = input; + this.id = id; + Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk " + id); + Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startPosition=" + absoluteChunkStart); + major = input.readRawShort(); + Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: major=" + major); + minor = input.readRawShort(); + Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: minor=" + minor); + if (major != 1 && major != 2) { + throw new IOException("File version " + major + "." + minor + ". Only Flight Recorder files of version 1.x and 2.x can be read by this JDK."); + } + chunkSize = input.readRawLong(); + Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize); + this.constantPoolPosition = input.readRawLong(); + Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition); + metadataPosition = input.readRawLong(); + Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition); + chunkStartNanos = input.readRawLong(); // nanos since epoch + Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startNanos=" + chunkStartNanos); + durationNanos = input.readRawLong(); // duration nanos, not used + Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: durationNanos=" + durationNanos); + chunkStartTicks = input.readRawLong(); + Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startTicks=" + chunkStartTicks); + ticksPerSecond = input.readRawLong(); + Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: ticksPerSecond=" + ticksPerSecond); + input.readRawInt(); // features, not used + + // set up boundaries + this.absoluteChunkStart = absoluteChunkStart; + absoluteChunkEnd = absoluteChunkStart + chunkSize; + lastChunk = input.size() == absoluteChunkEnd; + absoluteEventStart = input.position(); + + // read metadata + input.position(absoluteEventStart); + } + + public ChunkHeader nextHeader() throws IOException { + return new ChunkHeader(input, absoluteChunkEnd, id + 1); + } + + public MetadataDescriptor readMetadata() throws IOException { + input.position(absoluteChunkStart + metadataPosition); + input.readInt(); // size + long id = input.readLong(); // event type id + if (id != METADATA_TYPE_ID) { + throw new IOException("Expected metadata event. Type id=" + id + ", should have been " + METADATA_TYPE_ID); + } + input.readLong(); // start time + input.readLong(); // duration + long metadataId = input.readLong(); + Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Metadata id=" + metadataId); + // No need to read if metadataId == lastMetadataId, but we + // do it for verification purposes. + return MetadataDescriptor.read(input); + } + + public boolean isLastChunk() { + return lastChunk; + } + + public short getMajor() { + return major; + } + + public short getMinor() { + return minor; + } + + public long getAbsoluteChunkStart() { + return absoluteChunkStart; + } + + public long getConstantPoolPosition() { + return constantPoolPosition; + } + + public long getStartTicks() { + return chunkStartTicks; + } + + public double getTicksPerSecond() { + return ticksPerSecond; + } + + public long getStartNanos() { + return chunkStartNanos; + } + + public long getEnd() { + return absoluteChunkEnd; + } + + public long getSize() { + return chunkSize; + } + + public long getDuration() { + return durationNanos; + } + + public RecordingInput getInput() { + return input; + } + + private static void verifyMagic(DataInput input) throws IOException { + for (byte c : FILE_MAGIC) { + if (input.readByte() != c) { + throw new IOException("Not a Flight Recorder file"); + } + } + } + + public long getEventStart() { + return absoluteEventStart; + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/consumer/RecordingInput.java 2019-02-08 18:32:36.630144186 +0300 @@ -0,0 +1,339 @@ +/* + * 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.consumer; + +import java.io.DataInput; +import java.io.EOFException; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.charset.Charset; + +public final class RecordingInput implements DataInput, AutoCloseable { + + public static final byte STRING_ENCODING_NULL = 0; + public static final byte STRING_ENCODING_EMPTY_STRING = 1; + public static final byte STRING_ENCODING_CONSTANT_POOL = 2; + public static final byte STRING_ENCODING_UTF8_BYTE_ARRAY = 3; + public static final byte STRING_ENCODING_CHAR_ARRAY = 4; + public static final byte STRING_ENCODING_LATIN1_BYTE_ARRAY = 5; + + private final static int DEFAULT_BLOCK_SIZE = 16 * 1024 * 1024; + private final static Charset UTF8 = Charset.forName("UTF-8"); + private final static Charset LATIN1 = Charset.forName("ISO-8859-1"); + + private static final class Block { + private byte[] bytes = new byte[0]; + private long blockPosition; + + boolean contains(long position) { + return position >= blockPosition && position < blockPosition + bytes.length; + } + + public void read(RandomAccessFile file, int amount) throws IOException { + blockPosition = file.getFilePointer(); + // reuse byte array, if possible + if (amount != bytes.length) { + bytes = new byte[amount]; + } + file.readFully(bytes); + } + + public byte get(long position) { + return bytes[(int) (position - blockPosition)]; + } + } + + private final RandomAccessFile file; + private final long size; + private Block currentBlock = new Block(); + private Block previousBlock = new Block(); + private long position; + private final int blockSize; + + private RecordingInput(File f, int blockSize) throws IOException { + this.size = f.length(); + this.blockSize = blockSize; + this.file = new RandomAccessFile(f, "r"); + if (size < 8) { + throw new IOException("Not a valid Flight Recorder file. File length is only " + size + " bytes."); + } + } + + public RecordingInput(File f) throws IOException { + this(f, DEFAULT_BLOCK_SIZE); + } + + @Override + public final byte readByte() throws IOException { + if (!currentBlock.contains(position)) { + position(position); + } + return currentBlock.get(position++); + } + + @Override + public final void readFully(byte[] dest, int offset, int length) throws IOException { + // TODO: Optimize, use Arrays.copy if all bytes are in current block + // array + for (int i = 0; i < length; i++) { + dest[i + offset] = readByte(); + } + } + + @Override + public final void readFully(byte[] dst) throws IOException { + readFully(dst, 0, dst.length); + } + + public final short readRawShort() throws IOException { + // copied from java.io.Bits + byte b0 = readByte(); + byte b1 = readByte(); + return (short) ((b1 & 0xFF) + (b0 << 8)); + } + + @Override + public final double readDouble() throws IOException { + // copied from java.io.Bits + return Double.longBitsToDouble(readRawLong()); + } + + @Override + public final float readFloat() throws IOException { + // copied from java.io.Bits + return Float.intBitsToFloat(readRawInt()); + } + + public final int readRawInt() throws IOException { + // copied from java.io.Bits + byte b0 = readByte(); + byte b1 = readByte(); + byte b2 = readByte(); + byte b3 = readByte(); + return ((b3 & 0xFF)) + ((b2 & 0xFF) << 8) + ((b1 & 0xFF) << 16) + ((b0) << 24); + } + + public final long readRawLong() throws IOException { + // copied from java.io.Bits + byte b0 = readByte(); + byte b1 = readByte(); + byte b2 = readByte(); + byte b3 = readByte(); + byte b4 = readByte(); + byte b5 = readByte(); + byte b6 = readByte(); + byte b7 = readByte(); + return ((b7 & 0xFFL)) + ((b6 & 0xFFL) << 8) + ((b5 & 0xFFL) << 16) + ((b4 & 0xFFL) << 24) + ((b3 & 0xFFL) << 32) + ((b2 & 0xFFL) << 40) + ((b1 & 0xFFL) << 48) + (((long) b0) << 56); + } + + public final long position() throws IOException { + return position; + } + + public final void position(long newPosition) throws IOException { + if (!currentBlock.contains(newPosition)) { + if (!previousBlock.contains(newPosition)) { + if (newPosition > size()) { + throw new EOFException("Trying to read at " + newPosition + ", but file is only " + size() + " bytes."); + } + long blockStart = trimToFileSize(calculateBlockStart(newPosition)); + file.seek(blockStart); + // trim amount to file size + long amount = Math.min(size() - blockStart, blockSize); + previousBlock.read(file, (int) amount); + } + // swap previous and current + Block tmp = currentBlock; + currentBlock = previousBlock; + previousBlock = tmp; + } + position = newPosition; + } + + private final long trimToFileSize(long position) throws IOException { + return Math.min(size(), Math.max(0, position)); + } + + private final long calculateBlockStart(long newPosition) { + // align to end of current block + if (currentBlock.contains(newPosition - blockSize)) { + return currentBlock.blockPosition + currentBlock.bytes.length; + } + // align before current block + if (currentBlock.contains(newPosition + blockSize)) { + return currentBlock.blockPosition - blockSize; + } + // not near current block, pick middle + return newPosition - blockSize / 2; + } + + public final long size() throws IOException { + return size; + } + + public final void close() throws IOException { + file.close(); + } + + @Override + public final int skipBytes(int n) throws IOException { + long position = position(); + position(position + n); + return (int) (position() - position); + } + + @Override + public final boolean readBoolean() throws IOException { + return readByte() != 0; + } + + @Override + public int readUnsignedByte() throws IOException { + return readByte() & 0x00FF; + } + + @Override + public int readUnsignedShort() throws IOException { + return readShort() & 0xFFFF; + } + + @Override + public final String readLine() throws IOException { + throw new UnsupportedOperationException(); + } + + // NOTE, this method should really be called readString + // but can't be renamed without making RecordingInput a + // public class. + // + // This method DOES Not read as expected (s2 + utf8 encoded character) + // instead it read: + // byte encoding + // int size + // data (byte or char) + // + // where encoding + // + // 0, means null + // 1, means UTF8 encoded byte array + // 2, means char array + // 3, means latin-1 (ISO-8859-1) encoded byte array + // 4, means "" + @Override + public String readUTF() throws IOException { + return readEncodedString(readByte()); + } + + public String readEncodedString(byte encoding) throws IOException { + if (encoding == STRING_ENCODING_NULL) { + return null; + } + if (encoding == STRING_ENCODING_EMPTY_STRING) { + return ""; + } + int size = readInt(); + if (encoding == STRING_ENCODING_CHAR_ARRAY) { + char[] c = new char[size]; + for (int i = 0; i < size; i++) { + c[i] = readChar(); + } + return new String(c); + } + byte[] bytes = new byte[size]; + readFully(bytes); // TODO: optimize, check size, and copy only if needed + if (encoding == STRING_ENCODING_UTF8_BYTE_ARRAY) { + return new String(bytes, UTF8); + } + + if (encoding == STRING_ENCODING_LATIN1_BYTE_ARRAY) { + return new String(bytes, LATIN1); + } + throw new IOException("Unknown string encoding " + encoding); + } + + @Override + public char readChar() throws IOException { + return (char) readLong(); + } + + @Override + public short readShort() throws IOException { + return (short) readLong(); + } + + @Override + public int readInt() throws IOException { + return (int) readLong(); + } + + @Override + public long readLong() throws IOException { + // can be optimized by branching checks, but will do for now + byte b0 = readByte(); + long ret = (b0 & 0x7FL); + if (b0 >= 0) { + return ret; + } + int b1 = readByte(); + ret += (b1 & 0x7FL) << 7; + if (b1 >= 0) { + return ret; + } + int b2 = readByte(); + ret += (b2 & 0x7FL) << 14; + if (b2 >= 0) { + return ret; + } + int b3 = readByte(); + ret += (b3 & 0x7FL) << 21; + if (b3 >= 0) { + return ret; + } + int b4 = readByte(); + ret += (b4 & 0x7FL) << 28; + if (b4 >= 0) { + return ret; + } + int b5 = readByte(); + ret += (b5 & 0x7FL) << 35; + if (b5 >= 0) { + return ret; + } + int b6 = readByte(); + ret += (b6 & 0x7FL) << 42; + if (b6 >= 0) { + return ret; + } + int b7 = readByte(); + ret += (b7 & 0x7FL) << 49; + if (b7 >= 0) { + return ret; + } + int b8 = readByte(); // read last byte raw + return ret + (((long) (b8 & 0XFF)) << 56); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/dcmd/AbstractDCmd.java 2019-02-08 18:32:36.770139290 +0300 @@ -0,0 +1,201 @@ +/* + * 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.dcmd; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.internal.JVM; +import jdk.jfr.internal.SecuritySupport; +import jdk.jfr.internal.SecuritySupport.SafePath; +import jdk.jfr.internal.Utils; + +/** + * Base class for JFR diagnostic commands + * + */ +abstract class AbstractDCmd { + + private final StringWriter result; + private final PrintWriter log; + + protected AbstractDCmd() { + result = new StringWriter(); + log = new PrintWriter(result); + } + + protected final FlightRecorder getFlightRecorder() { + return FlightRecorder.getFlightRecorder(); + } + + public final String getResult() { + return result.toString(); + } + + public String getPid() { + // Invoking ProcessHandle.current().pid() would require loading more + // classes during startup so instead JVM.getJVM().getPid() is used. + // The pid will not be exposed to running Java application, only when starting + // JFR from command line (-XX:StartFlightRecordin) or jcmd (JFR.start and JFR.check) + return JVM.getJVM().getPid(); + } + + protected final SafePath resolvePath(Recording recording, String filename) throws InvalidPathException { + if (filename == null) { + return makeGenerated(recording, Paths.get(".")); + } + Path path = Paths.get(filename); + if (Files.isDirectory(path)) { + return makeGenerated(recording, path); + } + return new SafePath(path.toAbsolutePath().normalize()); + } + + private SafePath makeGenerated(Recording recording, Path directory) { + return new SafePath(directory.toAbsolutePath().resolve(Utils.makeFilename(recording)).normalize()); + } + + protected final Recording findRecording(String name) throws DCmdException { + try { + return findRecordingById(Integer.parseInt(name)); + } catch (NumberFormatException nfe) { + // User specified a name, not an id. + return findRecordingByName(name); + } + } + + protected final void reportOperationComplete(String actionPrefix, String name, SafePath file) { + print(actionPrefix); + print(" recording"); + if (name != null) { + print(" \"" + name + "\""); + } + if (file != null) { + print(","); + try { + print(" "); + long bytes = SecuritySupport.getFileSize(file); + printBytes(bytes, " "); + } catch (IOException e) { + // Ignore, not essential + } + println(" written to:"); + println(); + printPath(file); + } else { + println("."); + } + } + + protected final List getRecordings() { + List list = new ArrayList<>(getFlightRecorder().getRecordings()); + Collections.sort(list, Comparator.comparing(Recording::getId)); + return list; + } + + static String quoteIfNeeded(String text) { + if (text.contains(" ")) { + return "\\\"" + text + "\\\""; + } else { + return text; + } + } + + protected final void println() { + log.println(); + } + + protected final void print(String s) { + log.print(s); + } + + protected final void print(String s, Object... args) { + log.printf(s, args); + } + + protected final void println(String s, Object... args) { + print(s, args); + println(); + } + + protected final void printBytes(long bytes, String separation) { + print(Utils.formatBytes(bytes, separation)); + } + + protected final void printTimespan(Duration timespan, String separator) { + print(Utils.formatTimespan(timespan, separator)); + } + + protected final void printPath(SafePath path) { + if (path == null) { + print("N/A"); + return; + } + try { + printPath(SecuritySupport.getAbsolutePath(path).toPath()); + } catch (IOException ioe) { + printPath(path.toPath()); + } + } + + protected final void printPath(Path path) { + try { + println(path.toAbsolutePath().toString()); + } catch (SecurityException e) { + // fall back on filename + println(path.toString()); + } + } + + private Recording findRecordingById(int id) throws DCmdException { + for (Recording r : getFlightRecorder().getRecordings()) { + if (r.getId() == id) { + return r; + } + } + throw new DCmdException("Could not find %d.\n\nUse JFR.check without options to see list of all available recordings.", id); + } + + private Recording findRecordingByName(String name) throws DCmdException { + for (Recording recording : getFlightRecorder().getRecordings()) { + if (name.equals(recording.getName())) { + return recording; + } + } + throw new DCmdException("Could not find %s.\n\nUse JFR.check without options to see list of all available recordings.", name); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/dcmd/DCmdCheck.java 2019-02-08 18:32:36.918134114 +0300 @@ -0,0 +1,163 @@ +/* + * 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.dcmd; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.StringJoiner; + +import jdk.jfr.EventType; +import jdk.jfr.Recording; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.internal.LogLevel; +import jdk.jfr.internal.LogTag; +import jdk.jfr.internal.Logger; + +/** + * JFR.check - invoked from native + * + */ +final class DCmdCheck extends AbstractDCmd { + /** + * Execute JFR.check + * + * @param recordingText name or id of the recording to check, or + * null to show a list of all recordings. + * + * @param verbose if event settings should be included. + * + * @return result output + * + * @throws DCmdException if the check could not be completed. + */ + public String execute(String recordingText, Boolean verbose) throws DCmdException { + executeInternal(recordingText, verbose); + return getResult(); + } + + private void executeInternal(String name, Boolean verbose) throws DCmdException { + if (LogTag.JFR_DCMD.shouldLog(LogLevel.DEBUG)) { + Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdCheck: name=" + name + ", verbose=" + verbose); + } + + if (verbose == null) { + verbose = Boolean.FALSE; + } + + if (name != null) { + printRecording(findRecording(name), verbose); + return; + } + + List recordings = getRecordings(); + if (!verbose && recordings.isEmpty()) { + println("No available recordings."); + println(); + println("Use jcmd " + getPid() + " JFR.start to start a recording."); + return; + } + boolean first = true; + for (Recording recording : recordings) { + // Print separation between recordings, + if (!first) { + println(); + if (Boolean.TRUE.equals(verbose)) { + println(); + } + } + first = false; + printRecording(recording, verbose); + } + } + + private void printRecording(Recording recording, boolean verbose) { + printGeneral(recording); + if (verbose) { + println(); + printSetttings(recording); + } + } + + private void printGeneral(Recording recording) { + print("Recording " + recording.getId() + ": name=" + recording.getName()); + + Duration duration = recording.getDuration(); + if (duration != null) { + print(" duration="); + printTimespan(duration, ""); + } + + long maxSize = recording.getMaxSize(); + if (maxSize != 0) { + print(" maxsize="); + printBytes(maxSize, ""); + } + Duration maxAge = recording.getMaxAge(); + if (maxAge != null) { + print(" maxage="); + printTimespan(maxAge, ""); + } + + print(" (" + recording.getState().toString().toLowerCase() + ")"); + println(); + } + + private void printSetttings(Recording recording) { + Map settings = recording.getSettings(); + for (EventType eventType : sortByEventPath(getFlightRecorder().getEventTypes())) { + StringJoiner sj = new StringJoiner(",", "[", "]"); + sj.setEmptyValue(""); + for (SettingDescriptor s : eventType.getSettingDescriptors()) { + String settingsPath = eventType.getName() + "#" + s.getName(); + if (settings.containsKey(settingsPath)) { + sj.add(s.getName() + "=" + settings.get(settingsPath)); + } + } + String settingsText = sj.toString(); + if (!settingsText.isEmpty()) { + print(" %s (%s)", eventType.getLabel(), eventType.getName()); + println(); + println(" " + settingsText); + } + } + } + + private static List sortByEventPath(Collection events) { + List sorted = new ArrayList<>(); + sorted.addAll(events); + Collections.sort(sorted, new Comparator() { + @Override + public int compare(EventType e1, EventType e2) { + return e1.getName().compareTo(e2.getName()); + } + }); + return sorted; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/dcmd/DCmdConfigure.java 2019-02-08 18:32:37.062129078 +0300 @@ -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.internal.dcmd; + + + +import jdk.jfr.internal.LogLevel; +import jdk.jfr.internal.LogTag; +import jdk.jfr.internal.Logger; +import jdk.jfr.internal.Options; +import jdk.jfr.internal.Repository; +import jdk.jfr.internal.SecuritySupport.SafePath; + +/** + * JFR.configure - invoked from native + * + */ +//Instantiated by native +final class DCmdConfigure extends AbstractDCmd { + /** + * Execute JFR.configure. + * + * @param repositoryPath the path + * @param dumpPath path to dump to on fatal error (oom) + * @param stackDepth depth of stack traces + * @param globalBufferCount number of global buffers + * @param globalBufferSize size of global buffers + * @param threadBufferSize size of thread buffer for events + * @param maxChunkSize threshold at which a new chunk is created in the disk repository + * @param sampleThreads if thread sampling should be enabled + * + * @return result + + * @throws DCmdException + * if the dump could not be completed + */ + public String execute + ( + String repositoryPath, + String dumpPath, + Integer stackDepth, + Long globalBufferCount, + Long globalBufferSize, + Long threadBufferSize, + Long memorySize, + Long maxChunkSize, + Boolean sampleThreads + + ) throws DCmdException { + if (LogTag.JFR_DCMD.shouldLog(LogLevel.DEBUG)) { + Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdConfigure: repositorypath=" + repositoryPath + + ", dumppath=" + dumpPath + + ", stackdepth=" + stackDepth + + ", globalbuffercount=" + globalBufferCount + + ", globalbuffersize=" + globalBufferSize + + ", thread_buffer_size" + threadBufferSize + + ", memorysize" + memorySize + + ", maxchunksize=" + maxChunkSize + + ", samplethreads" + sampleThreads); + } + + + boolean updated = false; + if (repositoryPath != null) { + try { + SafePath s = new SafePath(repositoryPath); + Repository.getRepository().setBasePath(s); + Logger.log(LogTag.JFR, LogLevel.INFO, "Base repository path set to " + repositoryPath); + } catch (Exception e) { + throw new DCmdException("Could not use " + repositoryPath + " as repository. " + e.getMessage(), e); + } + printRepositoryPath(); + updated = true; + } + + if (dumpPath != null) { + Options.setDumpPath(new SafePath(dumpPath)); + Logger.log(LogTag.JFR, LogLevel.INFO, "Emergency dump path set to " + dumpPath); + printDumpPath(); + updated = true; + } + + if (stackDepth != null) { + Options.setStackDepth(stackDepth); + Logger.log(LogTag.JFR, LogLevel.INFO, "Stack depth set to " + stackDepth); + printStackDepth(); + updated = true; + } + + if (globalBufferCount != null) { + Options.setGlobalBufferCount(globalBufferCount); + Logger.log(LogTag.JFR, LogLevel.INFO, "Global buffer count set to " + globalBufferCount); + printGlobalBufferCount(); + updated = true; + } + + if (globalBufferSize != null) { + Options.setGlobalBufferSize(globalBufferSize); + Logger.log(LogTag.JFR, LogLevel.INFO, "Global buffer size set to " + globalBufferSize); + printGlobalBufferSize(); + updated = true; + } + + if (threadBufferSize != null) { + Options.setThreadBufferSize(threadBufferSize); + Logger.log(LogTag.JFR, LogLevel.INFO, "Thread buffer size set to " + threadBufferSize); + printThreadBufferSize(); + updated = true; + } + + if (memorySize != null) { + Options.setMemorySize(memorySize); + Logger.log(LogTag.JFR, LogLevel.INFO, "Memory size set to " + memorySize); + printMemorySize(); + updated = true; + } + + if (maxChunkSize != null) { + Options.setMaxChunkSize(maxChunkSize); + Logger.log(LogTag.JFR, LogLevel.INFO, "Max chunk size set to " + maxChunkSize); + printMaxChunkSize(); + updated = true; + } + + if (sampleThreads != null) { + Options.setSampleThreads(sampleThreads); + Logger.log(LogTag.JFR, LogLevel.INFO, "Sample threads set to " + sampleThreads); + printSampleThreads(); + updated = true; + } + + if (!updated) { + println("Current configuration:"); + println(); + printRepositoryPath(); + printStackDepth(); + printGlobalBufferCount(); + printGlobalBufferSize(); + printThreadBufferSize(); + printMemorySize(); + printMaxChunkSize(); + printSampleThreads(); + } + return getResult(); + } + + private void printRepositoryPath() { + print("Repository path: "); + printPath(Repository.getRepository().getRepositoryPath()); + println(); + } + + private void printDumpPath() { + print("Dump path: "); + printPath(Options.getDumpPath()); + println(); + } + + private void printSampleThreads() { + println("Sample threads: " + Options.getSampleThreads()); + } + + private void printStackDepth() { + println("Stack depth: " + Options.getStackDepth()); + } + + private void printGlobalBufferCount() { + println("Global buffer count: " + Options.getGlobalBufferCount()); + } + + private void printGlobalBufferSize() { + print("Global buffer size: "); + printBytes(Options.getGlobalBufferSize(), " "); + println(); + } + + private void printThreadBufferSize() { + print("Thread buffer size: "); + printBytes(Options.getThreadBufferSize(), " "); + println(); + } + + private void printMemorySize() { + print("Memory size: "); + printBytes(Options.getMemorySize(), " "); + println(); + } + + private void printMaxChunkSize() { + print("Max chunk size: "); + printBytes(Options.getMaxChunkSize(), " "); + println(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/dcmd/DCmdDump.java 2019-02-08 18:32:37.202124182 +0300 @@ -0,0 +1,205 @@ +/* + * 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.dcmd; + +import java.io.IOException; +import java.nio.file.InvalidPathException; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.internal.LogLevel; +import jdk.jfr.internal.LogTag; +import jdk.jfr.internal.Logger; +import jdk.jfr.internal.PlatformRecorder; +import jdk.jfr.internal.PlatformRecording; +import jdk.jfr.internal.PrivateAccess; +import jdk.jfr.internal.SecuritySupport.SafePath; +import jdk.jfr.internal.Utils; +import jdk.jfr.internal.WriteableUserPath; + +/** + * JFR.dump + * + */ +// Instantiated by native +final class DCmdDump extends AbstractDCmd { + /** + * Execute JFR.dump. + * + * @param name name or id of the recording to dump, or null to dump everything + * + * @param filename file path where recording should be written, not null + * @param maxAge how far back in time to dump, may be null + * @param maxSize how far back in size to dump data from, may be null + * @param begin point in time to dump data from, may be null + * @param end point in time to dump data to, may be null + * @param pathToGcRoots if Java heap should be swept for reference chains + * + * @return result output + * + * @throws DCmdException if the dump could not be completed + */ + public String execute(String name, String filename, Long maxAge, Long maxSize, String begin, String end, Boolean pathToGcRoots) throws DCmdException { + if (LogTag.JFR_DCMD.shouldLog(LogLevel.DEBUG)) { + Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, + "Executing DCmdDump: name=" + name + + ", filename=" + filename + + ", maxage=" + maxAge + + ", maxsize=" + maxSize + + ", begin=" + begin + + ", end" + end + + ", path-to-gc-roots=" + pathToGcRoots); + } + + if (FlightRecorder.getFlightRecorder().getRecordings().isEmpty()) { + throw new DCmdException("No recordings to dump from. Use JFR.start to start a recording."); + } + + if (maxAge != null) { + if (end != null || begin != null) { + throw new DCmdException("Dump failed, maxage can't be combined with begin or end."); + } + + if (maxAge < 0) { + throw new DCmdException("Dump failed, maxage can't be negative."); + } + if (maxAge == 0) { + maxAge = Long.MAX_VALUE / 2; // a high value that won't overflow + } + } + + if (maxSize!= null) { + if (maxSize < 0) { + throw new DCmdException("Dump failed, maxsize can't be negative."); + } + if (maxSize == 0) { + maxSize = Long.MAX_VALUE / 2; // a high value that won't overflow + } + } + + Instant beginTime = parseTime(begin, "begin"); + Instant endTime = parseTime(end, "end"); + + if (beginTime != null && endTime != null) { + if (endTime.isBefore(beginTime)) { + throw new DCmdException("Dump failed, begin must preceed end."); + } + } + + Duration duration = null; + if (maxAge != null) { + duration = Duration.ofNanos(maxAge); + beginTime = Instant.now().minus(duration); + } + Recording recording = null; + if (name != null) { + recording = findRecording(name); + } + PlatformRecorder recorder = PrivateAccess.getInstance().getPlatformRecorder(); + synchronized (recorder) { + dump(recorder, recording, name, filename, maxSize, pathToGcRoots, beginTime, endTime); + } + return getResult(); + } + + public void dump(PlatformRecorder recorder, Recording recording, String name, String filename, Long maxSize, Boolean pathToGcRoots, Instant beginTime, Instant endTime) throws DCmdException { + try (PlatformRecording r = newSnapShot(recorder, recording, pathToGcRoots)) { + r.filter(beginTime, endTime, maxSize); + if (r.getChunks().isEmpty()) { + throw new DCmdException("Dump failed. No data found in the specified interval."); + } + SafePath dumpFile = resolvePath(recording, filename); + + // Needed for JVM + Utils.touch(dumpFile.toPath()); + r.dumpStopped(new WriteableUserPath(dumpFile.toPath())); + reportOperationComplete("Dumped", name, dumpFile); + } catch (IOException | InvalidPathException e) { + throw new DCmdException("Dump failed. Could not copy recording data. %s", e.getMessage()); + } + } + + private Instant parseTime(String time, String parameter) throws DCmdException { + if (time == null) { + return null; + } + try { + return Instant.parse(time); + } catch (DateTimeParseException dtp) { + // fall through + } + try { + LocalDateTime ldt = LocalDateTime.parse(time); + return ZonedDateTime.of(ldt, ZoneId.systemDefault()).toInstant(); + } catch (DateTimeParseException dtp) { + // fall through + } + try { + LocalTime lt = LocalTime.parse(time); + LocalDate ld = LocalDate.now(); + Instant instant = ZonedDateTime.of(ld, lt, ZoneId.systemDefault()).toInstant(); + Instant now = Instant.now(); + if (instant.isAfter(now) && !instant.isBefore(now.plusSeconds(3600))) { + // User must have meant previous day + ld = ld.minusDays(1); + } + return ZonedDateTime.of(ld, lt, ZoneId.systemDefault()).toInstant(); + } catch (DateTimeParseException dtp) { + // fall through + } + + if (time.startsWith("-")) { + try { + long durationNanos = Utils.parseTimespan(time.substring(1)); + Duration duration = Duration.ofNanos(durationNanos); + return Instant.now().minus(duration); + } catch (NumberFormatException nfe) { + // fall through + } + } + throw new DCmdException("Dump failed, not a valid %s time.", parameter); + } + + private PlatformRecording newSnapShot(PlatformRecorder recorder, Recording recording, Boolean pathToGcRoots) throws DCmdException, IOException { + if (recording == null) { + // Operate on all recordings + PlatformRecording snapshot = recorder.newTemporaryRecording(); + recorder.fillWithRecordedData(snapshot, pathToGcRoots); + return snapshot; + } + + PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording); + return pr.newSnapshotClone("Dumped by user", pathToGcRoots); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/dcmd/DCmdException.java 2019-02-08 18:32:37.350119007 +0300 @@ -0,0 +1,69 @@ +/* + * 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.dcmd; + +import java.util.Formatter; + +/** + * Thrown to indicate that a diagnostic command could not be executed + * successfully. + */ +final class DCmdException extends Exception { + private static final long serialVersionUID = -3792411099340016465L; + + /** + * Constructs a new exception with message derived from a format string. + * + * @param format format string as described in {@link Formatter} class. + * + * @param args arguments referenced by the format specifiers in the format + * string. + * + */ + public DCmdException(String format, Object... args) { + super(format(format, args)); + } + + /** + * Constructs a new exception with message derived from a format string. + * + * @param cause exception that stopped the diagnostic command to complete. + * + * @param format format string as described in {@link Formatter} class. + * + * @param args arguments referenced by the format specifiers in the format + * string. + * + */ + public DCmdException(Throwable cause, String format, Object... args) { + super(format(format, args), cause); + } + + private static String format(String message, Object... args) { + try (Formatter formatter = new Formatter()) { + return formatter.format(message, args).toString(); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java 2019-02-08 18:32:37.494113972 +0300 @@ -0,0 +1,260 @@ +/* + * 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.dcmd; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.ParseException; +import java.time.Duration; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.internal.JVM; +import jdk.jfr.internal.LogLevel; +import jdk.jfr.internal.LogTag; +import jdk.jfr.internal.Logger; +import jdk.jfr.internal.OldObjectSample; +import jdk.jfr.internal.PrivateAccess; +import jdk.jfr.internal.SecuritySupport.SafePath; +import jdk.jfr.internal.Type; +import jdk.jfr.internal.jfc.JFC; + +/** + * JFR.start + * + */ +//Instantiated by native +final class DCmdStart extends AbstractDCmd { + + /** + * Execute JFR.start. + * + * @param name optional name that can be used to identify recording. + * @param settings names of settings files to use, i.e. "default" or + * "default.jfc". + * @param delay delay before recording is started, in nanoseconds. Must be + * at least 1 second. + * @param duration duration of the recording, in nanoseconds. Must be at + * least 1 second. + * @param disk if recording should be persisted to disk + * @param path file path where recording data should be written + * @param maxAge how long recording data should be kept in the disk + * repository, or 0 if no limit should be set. + * + * @param maxSize the minimum amount data to keep in the disk repository + * before it is discarded, or 0 if no limit should be + * set. + * + * @param dumpOnExit if recording should dump on exit + * + * @return result output + * + * @throws DCmdException if recording could not be started + */ + @SuppressWarnings("resource") + public String execute(String name, String[] settings, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Boolean dumpOnExit, Boolean pathToGcRoots) throws DCmdException { + if (LogTag.JFR_DCMD.shouldLog(LogLevel.DEBUG)) { + Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdStart: name=" + name + + ", settings=" + (settings != null ? Arrays.asList(settings) : "(none)") + + ", delay=" + delay + + ", duration=" + duration + + ", disk=" + disk+ + ", filename=" + path + + ", maxage=" + maxAge + + ", maxsize=" + maxSize + + ", dumponexit =" + dumpOnExit + + ", path-to-gc-roots=" + pathToGcRoots); + } + if (name != null) { + try { + Integer.parseInt(name); + throw new DCmdException("Name of recording can't be numeric"); + } catch (NumberFormatException nfe) { + // ok, can't be mixed up with name + } + } + + if (duration == null && Boolean.FALSE.equals(dumpOnExit) && path != null) { + throw new DCmdException("Filename can only be set for a time bound recording or if dumponexit=true. Set duration/dumponexit or omit filename."); + } + + + Map s = new HashMap<>(); + + if (settings == null || settings.length == 0) { + settings = new String[] { "default" }; + } + + for (String configName : settings) { + try { + s.putAll(JFC.createKnown(configName).getSettings()); + } catch (IOException | ParseException e) { + throw new DCmdException("Could not parse setting " + settings[0], e); + } + } + + OldObjectSample.updateSettingPathToGcRoots(s, pathToGcRoots); + + if (duration != null) { + if (duration < 1000L * 1000L * 1000L) { + // to avoid typo, duration below 1s makes no sense + throw new DCmdException("Could not start recording, duration must be at least 1 second."); + } + } + + if (delay != null) { + if (delay < 1000L * 1000L * 1000) { + // to avoid typo, delay shorter than 1s makes no sense. + throw new DCmdException("Could not start recording, delay must be at least 1 second."); + } + } + + if (!FlightRecorder.isInitialized() && delay == null) { + initializeWithForcedInstrumentation(s); + } + + Recording recording = new Recording(); + if (name != null) { + recording.setName(name); + } + + if (disk != null) { + recording.setToDisk(disk.booleanValue()); + } + recording.setSettings(s); + SafePath safePath = null; + + if (path != null) { + try { + if (dumpOnExit == null) { + // default to dumponexit=true if user specified filename + dumpOnExit = Boolean.TRUE; + } + Path p = Paths.get(path); + if (Files.isDirectory(p) && Boolean.TRUE.equals(dumpOnExit)) { + // Decide destination filename at dump time + // Purposely avoid generating filename in Recording#setDestination due to + // security concerns + PrivateAccess.getInstance().getPlatformRecording(recording).setDumpOnExitDirectory(new SafePath(p)); + } else { + safePath = resolvePath(recording, path); + recording.setDestination(safePath.toPath()); + } + } catch (IOException | InvalidPathException e) { + recording.close(); + throw new DCmdException("Could not start recording, not able to write to file %s. %s ", path, e.getMessage()); + } + } + + if (maxAge != null) { + recording.setMaxAge(Duration.ofNanos(maxAge)); + } + + if (maxSize != null) { + recording.setMaxSize(maxSize); + } + + if (duration != null) { + recording.setDuration(Duration.ofNanos(duration)); + } + + if (dumpOnExit != null) { + recording.setDumpOnExit(dumpOnExit); + } + + if (delay != null) { + Duration dDelay = Duration.ofNanos(delay); + recording.scheduleStart(dDelay); + print("Recording " + recording.getId() + " scheduled to start in "); + printTimespan(dDelay, " "); + print("."); + } else { + recording.start(); + print("Started recording " + recording.getId() + "."); + } + + if (recording.isToDisk() && duration == null && maxAge == null && maxSize == null) { + print(" No limit specified, using maxsize=250MB as default."); + recording.setMaxSize(250*1024L*1024L); + } + + if (safePath != null && duration != null) { + println(" The result will be written to:"); + println(); + printPath(safePath); + } else { + println(); + println(); + String cmd = duration == null ? "dump" : "stop"; + String fileOption = path == null ? "filename=FILEPATH " : ""; + String recordingspecifier = "name=" + recording.getId(); + // if user supplied a name, use it. + if (name != null) { + recordingspecifier = "name=" + quoteIfNeeded(name); + } + print("Use jcmd " + getPid() + " JFR." + cmd + " " + recordingspecifier + " " + fileOption + "to copy recording data to file."); + println(); + } + return getResult(); + } + + + // Instruments JDK-events on class load to reduce startup time + private void initializeWithForcedInstrumentation(Map settings) { + if (!hasJDKEvents(settings)) { + return; + } + JVM jvm = JVM.getJVM(); + try { + jvm.setForceInstrumentation(true); + FlightRecorder.getFlightRecorder(); + } finally { + jvm.setForceInstrumentation(false); + } + } + + private boolean hasJDKEvents(Map settings) { + String[] eventNames = new String[7]; + eventNames[0] = "FileRead"; + eventNames[1] = "FileWrite"; + eventNames[2] = "SocketRead"; + eventNames[3] = "SocketWrite"; + eventNames[4] = "JavaErrorThrow"; + eventNames[5] = "JavaExceptionThrow"; + eventNames[6] = "FileForce"; + for (String eventName : eventNames) { + if ("true".equals(settings.get(Type.EVENT_NAME_PREFIX + eventName + "#enabled"))) { + return true; + } + } + return false; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/dcmd/DCmdStop.java 2019-02-08 18:32:37.638108935 +0300 @@ -0,0 +1,87 @@ +/* + * 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.dcmd; + +import java.io.IOException; +import java.nio.file.InvalidPathException; +import java.nio.file.Paths; + +import jdk.jfr.Recording; +import jdk.jfr.internal.LogLevel; +import jdk.jfr.internal.LogTag; +import jdk.jfr.internal.Logger; +import jdk.jfr.internal.SecuritySupport.SafePath; + +/** + * JFR.stop + * + */ +// Instantiated by native +final class DCmdStop extends AbstractDCmd { + + /** + * Execute JFR.stop + * + * Requires that either name or id is set. + * + * @param name name or id of the recording to stop. + * + * @param filename file path where data should be written after recording has + * been stopped, or null if recording shouldn't be written + * to disk. + * @return result text + * + * @throws DCmdException if recording could not be stopped + */ + public String execute(String name, String filename) throws DCmdException { + if (LogTag.JFR_DCMD.shouldLog(LogLevel.DEBUG)) { + Logger.log(LogTag.JFR_DCMD, LogLevel.DEBUG, "Executing DCmdStart: name=" + name + ", filename=" + filename); + } + + try { + SafePath safePath = null; + Recording recording = findRecording(name); + if (filename != null) { + try { + // Ensure path is valid. Don't generate safePath if filename == null, as a user may + // want to stop recording without a dump + safePath = resolvePath(null, filename); + recording.setDestination(Paths.get(filename)); + } catch (IOException | InvalidPathException e) { + throw new DCmdException("Failed to stop %s. Could not set destination for \"%s\" to file %s", recording.getName(), filename, e.getMessage()); + } + } + recording.stop(); + reportOperationComplete("Stopped", recording.getName(), safePath); + recording.close(); + return getResult(); + } catch (InvalidPathException | DCmdException e) { + if (filename != null) { + throw new DCmdException("Could not write recording \"%s\" to file. %s", name, e.getMessage()); + } + throw new DCmdException(e, "Could not stop recording \"%s\".", name, e.getMessage()); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/handlers/EventHandler.java 2019-02-08 18:32:37.782103900 +0300 @@ -0,0 +1,117 @@ +/* + * 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.handlers; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import jdk.jfr.EventType; +import jdk.jfr.internal.EventControl; +import jdk.jfr.internal.JVM; +import jdk.jfr.internal.PlatformEventType; +import jdk.jfr.internal.PrivateAccess; +import jdk.jfr.internal.StringPool; + +// Users should not be subclass for security reasons. +public abstract class EventHandler { + // Accessed by generated sub class + protected final PlatformEventType platformEventType; + + private final EventType eventType; + private final EventControl eventControl; + + // Accessed by generated sub class + EventHandler(boolean registered, EventType eventType, EventControl eventControl) { + this.eventType = eventType; + this.platformEventType = PrivateAccess.getInstance().getPlatformEventType(eventType); + this.eventControl = eventControl; + platformEventType.setRegistered(registered); + } + + final protected StringPool createStringFieldWriter() { + return new StringPool(); + } + + // Accessed by generated code in event class + public final boolean shouldCommit(long duration) { + return isEnabled() && duration >= platformEventType.getThresholdTicks(); + } + + // Accessed by generated code in event class + // Accessed by generated sub class + public final boolean isEnabled() { + return platformEventType.isCommitable(); + } + + public final EventType getEventType() { + return eventType; + } + + public final PlatformEventType getPlatformEventType() { + return platformEventType; + } + + public final EventControl getEventControl() { + return eventControl; + } + + public static long timestamp() { + return JVM.counterTime(); + } + + public static long duration(long startTime) { + if (startTime == 0) { + // User forgot to invoke begin, or instrumentation was + // added after the user invoked begin. + // Returning 0 will make it an instant event + return 0; + } + return timestamp() - startTime; + } + + // Prevent a malicious user from instantiating a generated event handlers. + @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"); + } + + public boolean isRegistered() { + return platformEventType.isRegistered(); + } + + public boolean setRegistered(boolean registered) { + return platformEventType.setRegistered(registered); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/ConstructorTracerWriter.java 2019-02-08 18:32:37.926098864 +0300 @@ -0,0 +1,86 @@ +/* + * 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.instrument; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +import jdk.internal.org.objectweb.asm.ClassReader; +import jdk.internal.org.objectweb.asm.ClassVisitor; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; +import jdk.internal.org.objectweb.asm.Type; + +final class ConstructorTracerWriter extends ClassVisitor { + + private ConstructorWriter useInputParameter, noUseInputParameter; + + static byte[] generateBytes(Class clz, byte[] oldBytes) throws IOException { + InputStream in = new ByteArrayInputStream(oldBytes); + ClassReader cr = new ClassReader(in); + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); + ConstructorTracerWriter ctw = new ConstructorTracerWriter(cw, clz); + cr.accept(ctw, 0); + return cw.toByteArray(); + } + + private ConstructorTracerWriter(ClassVisitor cv, Class classToChange) { + super(Opcodes.ASM5, cv); + useInputParameter = new ConstructorWriter(classToChange, true); + noUseInputParameter = new ConstructorWriter(classToChange, false); + } + + private boolean isConstructor(String name) { + return name.equals(""); + } + + private boolean takesStringParameter(String desc) { + Type[] types = Type.getArgumentTypes(desc); + if (types.length > 0 && types[0].getClassName().equals(String.class.getName())) { + return true; + } + return false; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + + // Get a hold of the constructors that takes a String as a parameter + if (isConstructor(name)) { + if (takesStringParameter(desc)) { + useInputParameter.setMethodVisitor(mv); + return useInputParameter; + } + noUseInputParameter.setMethodVisitor(mv); + return noUseInputParameter; + } + return mv; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/ConstructorWriter.java 2019-02-08 18:32:38.074093690 +0300 @@ -0,0 +1,89 @@ +/* + * 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.instrument; + +import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL; +import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; +import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC; +import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; + +final class ConstructorWriter extends MethodVisitor { + + private boolean useInputParameter; + private String shortClassName; + private String fullClassName; + + ConstructorWriter(Class classToChange, boolean useInputParameter) { + super(Opcodes.ASM5); + this.useInputParameter = useInputParameter; + shortClassName = classToChange.getSimpleName(); + fullClassName = classToChange.getName().replace('.', '/'); + } + + @Override + public void visitInsn(int opcode) + { + if (opcode == RETURN) { + if (useInputParameter) { + useInput(); + } else { + noInput(); + } + } + mv.visitInsn(opcode); + } + @SuppressWarnings("deprecation") + private void useInput() + { + //Load 'this' from local variable 0 + //Load first input parameter + //Invoke ThrowableTracer.traceCLASS(this, parameter) for current class + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKESTATIC, "jdk/jfr/internal/instrument/ThrowableTracer", + "trace" + shortClassName, "(L" + fullClassName + + ";Ljava/lang/String;)V"); + } + + @SuppressWarnings("deprecation") + private void noInput() + { + //Load 'this' from local variable 0 + //Load "" + //Invoke ThrowableTracer.traceCLASS(this, "") for current class + mv.visitVarInsn(ALOAD, 0); + mv.visitInsn(ACONST_NULL); + mv.visitMethodInsn(INVOKESTATIC, "jdk/jfr/internal/instrument/ThrowableTracer", + "trace" + shortClassName, "(L" + fullClassName + + ";Ljava/lang/String;)V"); + } + + public void setMethodVisitor(MethodVisitor mv) { + this.mv = mv; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/FileChannelImplInstrumentor.java 2019-02-08 18:32:38.218088654 +0300 @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2013, 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.instrument; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import jdk.jfr.events.FileForceEvent; +import jdk.jfr.events.FileReadEvent; +import jdk.jfr.events.FileWriteEvent; + +/** + * See {@link JITracer} for an explanation of this code. + */ +@JIInstrumentationTarget("sun.nio.ch.FileChannelImpl") +final class FileChannelImplInstrumentor { + + private FileChannelImplInstrumentor() { + } + + private String path; + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public void force(boolean metaData) throws IOException { + FileForceEvent event = FileForceEvent.EVENT.get(); + if (!event.isEnabled()) { + force(metaData); + return; + } + try { + event.begin(); + force(metaData); + } finally { + event.path = path; + event.metaData = metaData; + event.commit(); + event.reset(); + } + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public int read(ByteBuffer dst) throws IOException { + FileReadEvent event = FileReadEvent.EVENT.get(); + if (!event.isEnabled()) { + return read(dst); + } + int bytesRead = 0; + try { + event.begin(); + bytesRead = read(dst); + } finally { + if (bytesRead < 0) { + event.endOfFile = true; + } else { + event.bytesRead = bytesRead; + } + event.path = path; + event.commit(); + event.reset(); + } + return bytesRead; + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public int read(ByteBuffer dst, long position) throws IOException { + FileReadEvent event = FileReadEvent.EVENT.get(); + if (!event.isEnabled()) { + return read(dst, position); + } + int bytesRead = 0; + try { + event.begin(); + bytesRead = read(dst, position); + } finally { + if (bytesRead < 0) { + event.endOfFile = true; + } else { + event.bytesRead = bytesRead; + } + event.path = path; + event.commit(); + event.reset(); + } + return bytesRead; + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public long read(ByteBuffer[] dsts, int offset, int length) throws IOException { + FileReadEvent event = FileReadEvent.EVENT.get(); + if (!event.isEnabled()) { + return read(dsts, offset, length); + } + long bytesRead = 0; + try { + event.begin(); + bytesRead = read(dsts, offset, length); + } finally { + if (bytesRead < 0) { + event.endOfFile = true; + } else { + event.bytesRead = bytesRead; + } + event.path = path; + event.commit(); + event.reset(); + } + return bytesRead; + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public int write(ByteBuffer src) throws IOException { + FileWriteEvent event = FileWriteEvent.EVENT.get(); + if (!event.isEnabled()) { + return write(src); + } + int bytesWritten = 0; + try { + event.begin(); + bytesWritten = write(src); + } finally { + event.bytesWritten = bytesWritten > 0 ? bytesWritten : 0; + event.path = path; + event.commit(); + event.reset(); + } + return bytesWritten; + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public int write(ByteBuffer src, long position) throws IOException { + FileWriteEvent event = FileWriteEvent.EVENT.get(); + if (!event.isEnabled()) { + return write(src, position); + } + + int bytesWritten = 0; + try { + event.begin(); + bytesWritten = write(src, position); + } finally { + event.bytesWritten = bytesWritten > 0 ? bytesWritten : 0; + event.path = path; + event.commit(); + event.reset(); + } + return bytesWritten; + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public long write(ByteBuffer[] srcs, int offset, int length) throws IOException { + FileWriteEvent event = FileWriteEvent.EVENT.get(); + if (!event.isEnabled()) { + return write(srcs, offset, length); + } + long bytesWritten = 0; + try { + event.begin(); + bytesWritten = write(srcs, offset, length); + } finally { + event.bytesWritten = bytesWritten > 0 ? bytesWritten : 0; + event.path = path; + event.commit(); + event.reset(); + } + return bytesWritten; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/FileInputStreamInstrumentor.java 2019-02-08 18:32:38.362083619 +0300 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2013, 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.instrument; + +import java.io.IOException; + +import jdk.jfr.events.FileReadEvent; + +/** + * See {@link JITracer} for an explanation of this code. + */ +@JIInstrumentationTarget("java.io.FileInputStream") +final class FileInputStreamInstrumentor { + + private FileInputStreamInstrumentor() { + } + + private String path; + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public int read() throws IOException { + FileReadEvent event = FileReadEvent.EVENT.get(); + if (!event.isEnabled()) { + return read(); + } + int result = 0; + try { + event.begin(); + result = read(); + if (result < 0) { + event.endOfFile = true; + } else { + event.bytesRead = 1; + } + } finally { + event.path = path; + event.commit(); + event.reset(); + } + return result; + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public int read(byte b[]) throws IOException { + FileReadEvent event = FileReadEvent.EVENT.get(); + if (!event.isEnabled()) { + return read(b); + } + int bytesRead = 0; + try { + event.begin(); + bytesRead = read(b); + } finally { + if (bytesRead < 0) { + event.endOfFile = true; + } else { + event.bytesRead = bytesRead; + } + event.path = path; + event.commit(); + event.reset(); + } + return bytesRead; + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public int read(byte b[], int off, int len) throws IOException { + FileReadEvent event = FileReadEvent.EVENT.get(); + if (!event.isEnabled()) { + return read(b, off, len); + } + int bytesRead = 0; + try { + event.begin(); + bytesRead = read(b, off, len); + } finally { + if (bytesRead < 0) { + event.endOfFile = true; + } else { + event.bytesRead = bytesRead; + } + event.path = path; + event.commit(); + event.reset(); + } + return bytesRead; + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/FileOutputStreamInstrumentor.java 2019-02-08 18:32:38.502078724 +0300 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2013, 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.instrument; + +import java.io.IOException; + +import jdk.jfr.events.FileWriteEvent; + +/** + * See {@link JITracer} for an explanation of this code. + */ +@JIInstrumentationTarget("java.io.FileOutputStream") +final class FileOutputStreamInstrumentor { + + private FileOutputStreamInstrumentor() { + } + + private String path; + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public void write(int b) throws IOException { + FileWriteEvent event = FileWriteEvent.EVENT.get(); + if (!event.isEnabled()) { + write(b); + return; + } + try { + event.begin(); + write(b); + event.bytesWritten = 1; + } finally { + event.path = path; + event.commit(); + event.reset(); + } + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public void write(byte b[]) throws IOException { + FileWriteEvent event = FileWriteEvent.EVENT.get(); + if (!event.isEnabled()) { + write(b); + return; + } + try { + event.begin(); + write(b); + event.bytesWritten = b.length; + } finally { + event.path = path; + event.commit(); + event.reset(); + } + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public void write(byte b[], int off, int len) throws IOException { + FileWriteEvent event = FileWriteEvent.EVENT.get(); + if (!event.isEnabled()) { + write(b, off, len); + return; + } + try { + event.begin(); + write(b, off, len); + event.bytesWritten = len; + } finally { + event.path = path; + event.commit(); + event.reset(); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/JDKEvents.java 2019-02-08 18:32:38.650073548 +0300 @@ -0,0 +1,146 @@ +/* + * 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.instrument; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.FlightRecorder; +import jdk.jfr.events.ActiveRecordingEvent; +import jdk.jfr.events.ActiveSettingEvent; +import jdk.jfr.events.ErrorThrownEvent; +import jdk.jfr.events.ExceptionStatisticsEvent; +import jdk.jfr.events.ExceptionThrownEvent; +import jdk.jfr.events.FileForceEvent; +import jdk.jfr.events.FileReadEvent; +import jdk.jfr.events.FileWriteEvent; +import jdk.jfr.events.SocketReadEvent; +import jdk.jfr.events.SocketWriteEvent; +import jdk.jfr.internal.JVM; +import jdk.jfr.internal.LogLevel; +import jdk.jfr.internal.LogTag; +import jdk.jfr.internal.Logger; +import jdk.jfr.internal.RequestEngine; +import jdk.jfr.internal.SecuritySupport; +import jdk.jfr.internal.Utils; + +public final class JDKEvents { + + private static final Class[] eventClasses = { + FileForceEvent.class, + FileReadEvent.class, + FileWriteEvent.class, + SocketReadEvent.class, + SocketWriteEvent.class, + ExceptionThrownEvent.class, + ExceptionStatisticsEvent.class, + ErrorThrownEvent.class, + ActiveSettingEvent.class, + ActiveRecordingEvent.class + }; + + // This is a list of the classes with instrumentation code that should be applied. + private static final Class[] instrumentationClasses = new Class[] { + FileInputStreamInstrumentor.class, + FileOutputStreamInstrumentor.class, + RandomAccessFileInstrumentor.class, + FileChannelImplInstrumentor.class, + SocketInputStreamInstrumentor.class, + SocketOutputStreamInstrumentor.class, + SocketChannelImplInstrumentor.class + }; + + private static final Class[] targetClasses = new Class[instrumentationClasses.length]; + private static final JVM jvm = JVM.getJVM(); + private static final Runnable emitExceptionStatistics = JDKEvents::emitExceptionStatistics; + private static boolean initializationTriggered; + + @SuppressWarnings("unchecked") + public synchronized static void initialize() { + try { + if (initializationTriggered == false) { + for (Class eventClass : eventClasses) { + SecuritySupport.registerEvent((Class) eventClass); + } + initializationTriggered = true; + FlightRecorder.addPeriodicEvent(ExceptionStatisticsEvent.class, emitExceptionStatistics); + } + } catch (Exception e) { + Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Could not initialize JDK events. " + e.getMessage()); + } + } + + public static void addInstrumentation() { + try { + List> list = new ArrayList<>(); + for (int i = 0; i < instrumentationClasses.length; i++) { + JIInstrumentationTarget tgt = instrumentationClasses[i].getAnnotation(JIInstrumentationTarget.class); + Class clazz = Class.forName(tgt.value()); + targetClasses[i] = clazz; + list.add(clazz); + } + list.add(java.lang.Throwable.class); + list.add(java.lang.Error.class); + Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Retransformed JDK classes"); + jvm.retransformClasses(list.toArray(new Class[list.size()])); + } catch (Exception e) { + Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Could not add instrumentation for JDK events. " + e.getMessage()); + } + } + + private static void emitExceptionStatistics() { + ExceptionStatisticsEvent t = new ExceptionStatisticsEvent(); + t.throwables = ThrowableTracer.numThrowables(); + t.commit(); + } + + @SuppressWarnings("deprecation") + public static byte[] retransformCallback(Class klass, byte[] oldBytes) throws Throwable { + if (java.lang.Throwable.class == klass) { + Logger.log(LogTag.JFR_SYSTEM, LogLevel.TRACE, "Instrumenting java.lang.Throwable"); + return ConstructorTracerWriter.generateBytes(java.lang.Throwable.class, oldBytes); + } + + if (java.lang.Error.class == klass) { + Logger.log(LogTag.JFR_SYSTEM, LogLevel.TRACE, "Instrumenting java.lang.Error"); + return ConstructorTracerWriter.generateBytes(java.lang.Error.class, oldBytes); + } + + for (int i = 0; i < targetClasses.length; i++) { + if (targetClasses[i].equals(klass)) { + Class c = instrumentationClasses[i]; + Logger.log(LogTag.JFR_SYSTEM, LogLevel.TRACE, () -> "Processing instrumentation class: " + c); + return new JIClassInstrumentation(instrumentationClasses[i], klass, oldBytes).getNewBytes(); + } + } + return oldBytes; + } + + public static void remove() { + RequestEngine.removeHook(JDKEvents::emitExceptionStatistics); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/JIClassInstrumentation.java 2019-02-08 18:32:38.794068513 +0300 @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2013, 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.instrument; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import jdk.internal.org.objectweb.asm.ClassReader; +import jdk.internal.org.objectweb.asm.ClassVisitor; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.Opcodes; +import jdk.internal.org.objectweb.asm.tree.ClassNode; +import jdk.jfr.internal.SecuritySupport; +import jdk.jfr.internal.Utils; + +/** + * This class will perform byte code instrumentation given an "instrumentor" class. + * + * @see JITracer + * + * @author Staffan Larsen + */ +@Deprecated +final class JIClassInstrumentation { + private final Class instrumentor; + private final String targetName; + private final String instrumentorName; + private final byte[] newBytes; + private final ClassReader targetClassReader; + private final ClassReader instrClassReader; + + /** + * Creates an instance and performs the instrumentation. + * + * @param instrumentor instrumentor class + * @param target target class + * @param old_target_bytes bytes in target + * + * @throws ClassNotFoundException + * @throws IOException + */ + JIClassInstrumentation(Class instrumentor, Class target, byte[] old_target_bytes) throws ClassNotFoundException, IOException { + instrumentorName = instrumentor.getName(); + this.targetName = target.getName(); + this.instrumentor = instrumentor; + this.targetClassReader = new ClassReader(old_target_bytes); + this.instrClassReader = new ClassReader(getOriginalClassBytes(instrumentor)); + this.newBytes = makeBytecode(); + Utils.writeGeneratedASM(target.getName(), newBytes); + } + + private static byte[] getOriginalClassBytes(Class clazz) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + String name = "/" + clazz.getName().replace(".", "/") + ".class"; + InputStream is = SecuritySupport.getResourceAsStream(name); + int bytesRead; + byte[] buffer = new byte[16384]; + while ((bytesRead = is.read(buffer, 0, buffer.length)) != -1) { + baos.write(buffer, 0, bytesRead); + } + baos.flush(); + is.close(); + return baos.toByteArray(); + } + + private byte[] makeBytecode() throws IOException, ClassNotFoundException { + + // Find the methods to instrument and inline + + final List instrumentationMethods = new ArrayList<>(); + for (final Method m : instrumentor.getDeclaredMethods()) { + JIInstrumentationMethod im = m.getAnnotation(JIInstrumentationMethod.class); + if (im != null) { + instrumentationMethods.add(m); + } + } + + // We begin by inlining the target's methods into the instrumentor + + ClassNode temporary = new ClassNode(); + ClassVisitor inliner = new JIInliner( + Opcodes.ASM5, + temporary, + targetName, + instrumentorName, + targetClassReader, + instrumentationMethods); + instrClassReader.accept(inliner, ClassReader.EXPAND_FRAMES); + + // Now we have the target's methods inlined into the instrumentation code (in 'temporary'). + // We now need to replace the target's method with the code in the + // instrumentation method. + + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + JIMethodMergeAdapter ma = new JIMethodMergeAdapter( + cw, + temporary, + instrumentationMethods, + instrumentor.getAnnotationsByType(JITypeMapping.class)); + targetClassReader.accept(ma, ClassReader.EXPAND_FRAMES); + + return cw.toByteArray(); + } + + /** + * Get the instrumented byte codes that can be used to retransform the class. + * + * @return bytes + */ + public byte[] getNewBytes() { + return newBytes.clone(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/JIInliner.java 2019-02-08 18:32:38.942063338 +0300 @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2013, 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.instrument; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.List; + +import jdk.internal.org.objectweb.asm.ClassReader; +import jdk.internal.org.objectweb.asm.ClassVisitor; +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.tree.ClassNode; +import jdk.internal.org.objectweb.asm.tree.MethodNode; +import jdk.jfr.internal.LogLevel; +import jdk.jfr.internal.LogTag; +import jdk.jfr.internal.Logger; + +@Deprecated +final class JIInliner extends ClassVisitor { + private final String targetClassName; + private final String instrumentationClassName; + private final ClassNode targetClassNode; + private final List instrumentationMethods; + + /** + * A ClassVisitor which will check all methods of the class it visits against the instrumentationMethods + * list. If a method is on that list, the method will be further processed for inlining into that + * method. + */ + JIInliner(int api, ClassVisitor cv, String targetClassName, String instrumentationClassName, + ClassReader targetClassReader, + List instrumentationMethods) { + super(api, cv); + this.targetClassName = targetClassName; + this.instrumentationClassName = instrumentationClassName; + this.instrumentationMethods = instrumentationMethods; + + ClassNode cn = new ClassNode(Opcodes.ASM5); + targetClassReader.accept(cn, ClassReader.EXPAND_FRAMES); + this.targetClassNode = cn; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + + if (isInstrumentationMethod(name, desc)) { + MethodNode methodToInline = findTargetMethodNode(name, desc); + if (methodToInline == null) { + throw new IllegalArgumentException("Could not find the method to instrument in the target class"); + } + if (Modifier.isNative(methodToInline.access)) { + throw new IllegalArgumentException("Cannot instrument native methods: " + targetClassNode.name + "." + methodToInline.name + methodToInline.desc); + } + + Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.DEBUG, "Inliner processing method " + name + desc); + + JIMethodCallInliner mci = new JIMethodCallInliner(access, + desc, + mv, + methodToInline, + targetClassName, + instrumentationClassName); + return mci; + } + + return mv; + } + + private boolean isInstrumentationMethod(String name, String desc) { + for(Method m : instrumentationMethods) { + if (m.getName().equals(name) && Type.getMethodDescriptor(m).equals(desc)) { + return true; + } + } + return false; + } + + private MethodNode findTargetMethodNode(String name, String desc) { + for (MethodNode mn : targetClassNode.methods) { + if (mn.desc.equals(desc) && mn.name.equals(name)) { + return mn; + } + } + throw new IllegalArgumentException("could not find MethodNode for " + + name + desc); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/JIInstrumentationMethod.java 2019-02-08 18:32:39.086058303 +0300 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013, 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.instrument; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@interface JIInstrumentationMethod { +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/JIInstrumentationTarget.java 2019-02-08 18:32:39.230053269 +0300 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013, 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.instrument; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@interface JIInstrumentationTarget { + String value(); +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/JIMethodCallInliner.java 2019-02-08 18:32:39.374048234 +0300 @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2013, 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.instrument; + +import java.util.ArrayList; +import java.util.List; + +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.commons.LocalVariablesSorter; +import jdk.internal.org.objectweb.asm.commons.Remapper; +import jdk.internal.org.objectweb.asm.commons.SimpleRemapper; +import jdk.internal.org.objectweb.asm.tree.MethodNode; +import jdk.jfr.internal.LogLevel; +import jdk.jfr.internal.LogTag; +import jdk.jfr.internal.Logger; + +/** + * Class responsible for finding the call to inline and inlining it. + * + * This code is heavily influenced by section 3.2.6 "Inline Method" in + * "Using ASM framework to implement common bytecode transformation patterns", + * E. Kuleshov, AOSD.07, March 2007, Vancouver, Canada. + * http://asm.ow2.org/index.html + */ +@Deprecated +final class JIMethodCallInliner extends LocalVariablesSorter { + + private final String oldClass; + private final String newClass; + private final MethodNode inlineTarget; + private final List blocks = new ArrayList<>(); + private boolean inlining; + + /** + * inlineTarget defines the method to inline and also contains the actual + * code to inline. + * + * @param access + * @param desc + * @param mv + * @param inlineTarget + * @param oldClass + * @param newClass + * @param logger + */ + public JIMethodCallInliner(int access, String desc, MethodVisitor mv, + MethodNode inlineTarget, String oldClass, String newClass) { + super(Opcodes.ASM5, access, desc, mv); + this.oldClass = oldClass; + this.newClass = newClass; + this.inlineTarget = inlineTarget; + + Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.DEBUG, "MethodCallInliner: targetMethod=" + newClass + "." + + inlineTarget.name + inlineTarget.desc); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, + String desc, boolean itf) { + // Now we are looking at method call in the source method + if (!shouldBeInlined(owner, name, desc)) { + // If this method call should not be inlined, just keep it + mv.visitMethodInsn(opcode, owner, name, desc, itf); + return; + } + // If the call should be inlined, we create a MethodInliningAdapter + // The MIA will walk the instructions in the inlineTarget and add them + // to the current method, doing the necessary name remappings. + Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.DEBUG, "Inlining call to " + name + desc); + Remapper remapper = new SimpleRemapper(oldClass, newClass); + Label end = new Label(); + inlining = true; + inlineTarget.instructions.resetLabels(); + JIMethodInliningAdapter mia = new JIMethodInliningAdapter(this, end, + opcode == Opcodes.INVOKESTATIC ? Opcodes.ACC_STATIC : 0, desc, + remapper); + inlineTarget.accept(mia); + inlining = false; + super.visitLabel(end); + } + + /** + * Determine if the method should be inlined or not. + */ + private boolean shouldBeInlined(String owner, String name, String desc) { + return inlineTarget.desc.equals(desc) && inlineTarget.name.equals(name) + && owner.equals(newClass.replace('.', '/')); + } + + @Override + public void visitTryCatchBlock(Label start, Label end, Label handler, + String type) { + if (!inlining) { + // try-catch blocks are saved here and replayed at the end + // of the method (in visitMaxs) + blocks.add(new CatchBlock(start, end, handler, type)); + } else { + super.visitTryCatchBlock(start, end, handler, type); + } + } + + @Override + public void visitMaxs(int stack, int locals) { + for (CatchBlock b : blocks) { + super.visitTryCatchBlock(b.start, b.end, b.handler, b.type); + } + super.visitMaxs(stack, locals); + } + + static final class CatchBlock { + + final Label start; + final Label end; + final Label handler; + final String type; + + CatchBlock(Label start, Label end, Label handler, String type) { + this.start = start; + this.end = end; + this.handler = handler; + this.type = type; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/JIMethodInliningAdapter.java 2019-02-08 18:32:39.522043059 +0300 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2013, 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.instrument; + +import jdk.internal.org.objectweb.asm.Label; +import jdk.internal.org.objectweb.asm.Opcodes; +import jdk.internal.org.objectweb.asm.Type; +import jdk.internal.org.objectweb.asm.commons.LocalVariablesSorter; +import jdk.internal.org.objectweb.asm.commons.Remapper; +import jdk.internal.org.objectweb.asm.commons.RemappingMethodAdapter; + +@Deprecated +final class JIMethodInliningAdapter extends RemappingMethodAdapter { + private final LocalVariablesSorter lvs; + private final Label end; + + public JIMethodInliningAdapter(LocalVariablesSorter mv, Label end, int acc, String desc, Remapper remapper) { + super(acc, desc, mv, remapper); + this.lvs = mv; + this.end = end; + int offset = isStatic(acc) ? 0 : 1; + Type[] args = Type.getArgumentTypes(desc); + for (int i = args.length - 1; i >= 0; i--) { + super.visitVarInsn(args[i].getOpcode(Opcodes.ISTORE), i + offset); + } + if (offset > 0) { + super.visitVarInsn(Opcodes.ASTORE, 0); + } + } + + private boolean isStatic(int acc) { + return (acc & Opcodes.ACC_STATIC) != 0; + } + + @Override + public void visitInsn(int opcode) { + if (opcode == Opcodes.RETURN || opcode == Opcodes.IRETURN + || opcode == Opcodes.ARETURN || opcode == Opcodes.LRETURN) { + super.visitJumpInsn(Opcodes.GOTO, end); + } else { + super.visitInsn(opcode); + } + } + + @Override + public void visitMaxs(int stack, int locals) { + } + + @Override + protected int newLocalMapping(Type type) { + return lvs.newLocal(type); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/JIMethodMergeAdapter.java 2019-02-08 18:32:39.662038164 +0300 @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2013, 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.instrument; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.internal.org.objectweb.asm.ClassVisitor; +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.RemappingMethodAdapter; +import jdk.internal.org.objectweb.asm.commons.SimpleRemapper; +import jdk.internal.org.objectweb.asm.tree.ClassNode; +import jdk.internal.org.objectweb.asm.tree.MethodNode; +import jdk.jfr.internal.LogLevel; +import jdk.jfr.internal.LogTag; +import jdk.jfr.internal.Logger; + +/** + * This class will merge (some) methods from one class into another one. + * + * @author Staffan Larsen + */ +@Deprecated +final class JIMethodMergeAdapter extends ClassVisitor { + + private final ClassNode cn; + private final List methodFilter; + private final Map typeMap; + + /** + * Methods in methodFilter that exist in cn will be merged into cv. If the method already exists, + * the original method will be deleted. + * + * @param cv + * @param cn - a ClassNode with Methods that will be merged into this class + * @param methodFilter - only methods in this list will be merged + * @param typeMappings - while merging, type references in the methods will be changed according to this map + */ + public JIMethodMergeAdapter(ClassVisitor cv, ClassNode cn, List methodFilter, JITypeMapping[] typeMappings) { + super(Opcodes.ASM5, cv); + this.cn = cn; + this.methodFilter = methodFilter; + + this.typeMap = new HashMap<>(); + for (JITypeMapping tm : typeMappings) { + typeMap.put(tm.from().replace('.', '/'), tm.to().replace('.', '/')); + } + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + super.visit(version, access, name, signature, superName, interfaces); + typeMap.put(cn.name, name); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if(methodInFilter(name, desc)) { + // If the method is one that we will be replacing, delete the method + Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.DEBUG, "Deleting " + name + desc); + return null; + } + return super.visitMethod(access, name, desc, signature, exceptions); + } + + @Override + public void visitEnd() { + SimpleRemapper remapper = new SimpleRemapper(typeMap); + for (MethodNode mn : cn.methods) { + // Check if the method is in the list of methods to copy + if (methodInFilter(mn.name, mn.desc)) { + Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.DEBUG, "Copying method: " + mn.name + mn.desc); + Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.DEBUG, " with mapper: " + typeMap); + + String[] exceptions = new String[mn.exceptions.size()]; + mn.exceptions.toArray(exceptions); + MethodVisitor mv = cv.visitMethod(mn.access, mn.name, mn.desc, mn.signature, exceptions); + mn.instructions.resetLabels(); + mn.accept(new RemappingMethodAdapter(mn.access, mn.desc, mv, remapper)); + } + } + super.visitEnd(); + } + + private boolean methodInFilter(String name, String desc) { + for(Method m : methodFilter) { + if (m.getName().equals(name) && Type.getMethodDescriptor(m).equals(desc)) { + return true; + } + } + return false; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/JITypeMapping.java 2019-02-08 18:32:39.806033129 +0300 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2013, 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.instrument; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +@interface JITypeMapping { + String from(); + String to(); +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/RandomAccessFileInstrumentor.java 2019-02-08 18:32:39.950028094 +0300 @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2013, 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.instrument; + +import java.io.IOException; + +import jdk.jfr.events.FileReadEvent; +import jdk.jfr.events.FileWriteEvent; + +/** + * See {@link JITracer} for an explanation of this code. + */ +@JIInstrumentationTarget("java.io.RandomAccessFile") +final class RandomAccessFileInstrumentor { + + private RandomAccessFileInstrumentor() { + } + + private String path; + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public int read() throws IOException { + FileReadEvent event = FileReadEvent.EVENT.get(); + if (!event.isEnabled()) { + return read(); + } + int result = 0; + try { + event.begin(); + result = read(); + if (result < 0) { + event.endOfFile = true; + } else { + event.bytesRead = 1; + } + } finally { + event.path = path; + event.commit(); + event.reset(); + } + return result; + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public int read(byte b[]) throws IOException { + FileReadEvent event = FileReadEvent.EVENT.get(); + if (!event.isEnabled()) { + return read(b); + } + int bytesRead = 0; + try { + event.begin(); + bytesRead = read(b); + } finally { + if (bytesRead < 0) { + event.endOfFile = true; + } else { + event.bytesRead = bytesRead; + } + event.path = path; + event.commit(); + event.reset(); + } + return bytesRead; + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public int read(byte b[], int off, int len) throws IOException { + FileReadEvent event = FileReadEvent.EVENT.get(); + if (!event.isEnabled()) { + return read(b, off, len); + } + int bytesRead = 0; + try { + event.begin(); + bytesRead = read(b, off, len); + } finally { + if (bytesRead < 0) { + event.endOfFile = true; + } else { + event.bytesRead = bytesRead; + } + event.path = path; + event.commit(); + event.reset(); + } + return bytesRead; + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public void write(int b) throws IOException { + FileWriteEvent event = FileWriteEvent.EVENT.get(); + if (!event.isEnabled()) { + write(b); + return; + } + try { + event.begin(); + write(b); + event.bytesWritten = 1; + } finally { + event.path = path; + event.commit(); + event.reset(); + } + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public void write(byte b[]) throws IOException { + FileWriteEvent event = FileWriteEvent.EVENT.get(); + if (!event.isEnabled()) { + write(b); + return; + } + try { + event.begin(); + write(b); + event.bytesWritten = b.length; + } finally { + event.path = path; + event.commit(); + event.reset(); + } + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public void write(byte b[], int off, int len) throws IOException { + FileWriteEvent event = FileWriteEvent.EVENT.get(); + if (!event.isEnabled()) { + write(b, off, len); + return; + } + try { + event.begin(); + write(b, off, len); + event.bytesWritten = len; + } finally { + event.path = path; + event.commit(); + event.reset(); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/SocketChannelImplInstrumentor.java 2019-02-08 18:32:40.094023060 +0300 @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2013, 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.instrument; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; + +import jdk.jfr.events.SocketReadEvent; +import jdk.jfr.events.SocketWriteEvent; + +/** + * See {@link JITracer} for an explanation of this code. + */ +@JIInstrumentationTarget("sun.nio.ch.SocketChannelImpl") +final class SocketChannelImplInstrumentor { + + private SocketChannelImplInstrumentor() { + } + + private InetSocketAddress remoteAddress; + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public int read(ByteBuffer dst) throws IOException { + SocketReadEvent event = SocketReadEvent.EVENT.get(); + if (!event.isEnabled()) { + return read(dst); + } + int bytesRead = 0; + try { + event.begin(); + bytesRead = read(dst); + } finally { + event.end(); + if (event.shouldCommit()) { + String hostString = remoteAddress.getAddress().toString(); + int delimiterIndex = hostString.lastIndexOf('/'); + + event.host = hostString.substring(0, delimiterIndex); + event.address = hostString.substring(delimiterIndex + 1); + event.port = remoteAddress.getPort(); + if (bytesRead < 0) { + event.endOfStream = true; + } else { + event.bytesRead = bytesRead; + } + event.timeout = 0; + + event.commit(); + event.reset(); + } + } + return bytesRead; + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public long read(ByteBuffer[] dsts, int offset, int length) throws IOException { + SocketReadEvent event = SocketReadEvent.EVENT.get(); + if(!event.isEnabled()) { + return read(dsts, offset, length); + } + + long bytesRead = 0; + try { + event.begin(); + bytesRead = read(dsts, offset, length); + } finally { + event.end(); + if (event.shouldCommit()) { + String hostString = remoteAddress.getAddress().toString(); + int delimiterIndex = hostString.lastIndexOf('/'); + + event.host = hostString.substring(0, delimiterIndex); + event.address = hostString.substring(delimiterIndex + 1); + event.port = remoteAddress.getPort(); + if (bytesRead < 0) { + event.endOfStream = true; + } else { + event.bytesRead = bytesRead; + } + event.timeout = 0; + + event.commit(); + event.reset(); + } + } + return bytesRead; + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public int write(ByteBuffer buf) throws IOException { + SocketWriteEvent event = SocketWriteEvent.EVENT.get(); + if (!event.isEnabled()) { + return write(buf); + } + + int bytesWritten = 0; + try { + event.begin(); + bytesWritten = write(buf); + } finally { + event.end(); + if (event.shouldCommit()) { + String hostString = remoteAddress.getAddress().toString(); + int delimiterIndex = hostString.lastIndexOf('/'); + + event.host = hostString.substring(0, delimiterIndex); + event.address = hostString.substring(delimiterIndex + 1); + event.port = remoteAddress.getPort(); + event.bytesWritten = bytesWritten < 0 ? 0 : bytesWritten; + + event.commit(); + event.reset(); + } + } + return bytesWritten; + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + public long write(ByteBuffer[] srcs, int offset, int length) throws IOException { + SocketWriteEvent event = SocketWriteEvent.EVENT.get(); + if (!event.isEnabled()) { + return write(srcs, offset, length); + } + long bytesWritten = 0; + try { + event.begin(); + bytesWritten = write(srcs, offset, length); + } finally { + event.end(); + if (event.shouldCommit()) { + String hostString = remoteAddress.getAddress().toString(); + int delimiterIndex = hostString.lastIndexOf('/'); + + event.host = hostString.substring(0, delimiterIndex); + event.address = hostString.substring(delimiterIndex + 1); + event.port = remoteAddress.getPort(); + event.bytesWritten = bytesWritten < 0 ? 0 : bytesWritten; + + event.commit(); + event.reset(); + } + } + return bytesWritten; + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/SocketInputStreamInstrumentor.java 2019-02-08 18:32:40.238018026 +0300 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2013, 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.instrument; + +import java.io.IOException; +import java.net.InetAddress; + +import jdk.jfr.events.SocketReadEvent; + +/** + * See {@link JITracer} for an explanation of this code. + */ +@JIInstrumentationTarget("java.net.SocketInputStream") +@JITypeMapping(from = "jdk.jfr.internal.instrument.SocketInputStreamInstrumentor$AbstractPlainSocketImpl", + to = "java.net.AbstractPlainSocketImpl") +final class SocketInputStreamInstrumentor { + + private SocketInputStreamInstrumentor() { + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + int read(byte b[], int off, int length, int timeout) throws IOException { + SocketReadEvent event = SocketReadEvent.EVENT.get(); + if (!event.isEnabled()) { + return read(b, off, length, timeout); + } + int bytesRead = 0; + try { + event.begin(); + bytesRead = read(b, off, length, timeout); + } finally { + event.end(); + if (event.shouldCommit()) { + String hostString = impl.address.toString(); + int delimiterIndex = hostString.lastIndexOf('/'); + + event.host = hostString.substring(0, delimiterIndex); + event.address = hostString.substring(delimiterIndex + 1); + event.port = impl.port; + if (bytesRead < 0) { + event.endOfStream = true; + } else { + event.bytesRead = bytesRead; + } + event.timeout = timeout; + + event.commit(); + event.reset(); + } + } + return bytesRead; + } + + private AbstractPlainSocketImpl impl = null; + + void silenceFindBugsUnwrittenField(InetAddress dummy) { + impl.address = dummy; + } + + static class AbstractPlainSocketImpl { + InetAddress address; + int port; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/SocketOutputStreamInstrumentor.java 2019-02-08 18:32:40.382012991 +0300 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2013, 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.instrument; + +import java.io.IOException; +import java.net.InetAddress; + +import jdk.jfr.events.SocketWriteEvent; + +/** + * See {@link JITracer} for an explanation of this code. + */ +@JIInstrumentationTarget("java.net.SocketOutputStream") +@JITypeMapping(from = "jdk.jfr.internal.instrument.SocketOutputStreamInstrumentor$AbstractPlainSocketImpl", + to = "java.net.AbstractPlainSocketImpl") +final class SocketOutputStreamInstrumentor { + + private SocketOutputStreamInstrumentor() { + } + + @SuppressWarnings("deprecation") + @JIInstrumentationMethod + private void socketWrite(byte b[], int off, int len) throws IOException { + SocketWriteEvent event = SocketWriteEvent.EVENT.get(); + if (!event.isEnabled()) { + socketWrite(b, off, len); + return; + } + int bytesWritten = 0; + try { + event.begin(); + socketWrite(b, off, len); + bytesWritten = len; + } finally { + event.end() ; + if (event.shouldCommit()) { + String hostString = impl.address.toString(); + int delimiterIndex = hostString.lastIndexOf('/'); + + event.host = hostString.substring(0, delimiterIndex); + event.address = hostString.substring(delimiterIndex + 1); + event.port = impl.port; + event.bytesWritten = bytesWritten < 0 ? 0 : bytesWritten; + + event.commit(); + event.reset(); + } + } + } + + private AbstractPlainSocketImpl impl = null; + + void silenceFindBugsUnwrittenField(InetAddress dummy) { + impl.address = dummy; + } + + static class AbstractPlainSocketImpl { + InetAddress address; + int port; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/instrument/ThrowableTracer.java 2019-02-08 18:32:40.526007957 +0300 @@ -0,0 +1,64 @@ +/* + * 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.instrument; + +import java.util.concurrent.atomic.AtomicLong; + +import jdk.jfr.events.ErrorThrownEvent; +import jdk.jfr.events.ExceptionThrownEvent; + +public final class ThrowableTracer { + + private static AtomicLong numThrowables = new AtomicLong(0); + + public static void traceError(Error e, String message) { + if (e instanceof OutOfMemoryError) { + return; + } + ErrorThrownEvent errorEvent = new ErrorThrownEvent(); + errorEvent.message = message; + errorEvent.thrownClass = e.getClass(); + errorEvent.commit(); + + ExceptionThrownEvent exceptionEvent = new ExceptionThrownEvent(); + exceptionEvent.message = message; + exceptionEvent.thrownClass = e.getClass(); + exceptionEvent.commit(); + numThrowables.incrementAndGet(); + } + + public static void traceThrowable(Throwable t, String message) { + ExceptionThrownEvent event = new ExceptionThrownEvent(); + event.message = message; + event.thrownClass = t.getClass(); + event.commit(); + numThrowables.incrementAndGet(); + } + + public static long numThrowables() { + return numThrowables.get(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/jfc/JFC.java 2019-02-08 18:32:40.670002922 +0300 @@ -0,0 +1,243 @@ +/* + * 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.jfc; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import jdk.jfr.Configuration; +import jdk.jfr.internal.LogLevel; +import jdk.jfr.internal.LogTag; +import jdk.jfr.internal.Logger; +import jdk.jfr.internal.SecuritySupport; +import jdk.jfr.internal.SecuritySupport.SafePath; + +/** + * {@link Configuration} factory for JFC files. * + */ +public final class JFC { + private static final int BUFFER_SIZE = 8192; + private static final int MAXIMUM_FILE_SIZE = 1024 * 1024; + private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; + private static volatile List knownConfigurations; + + /** + * Reads a known configuration file (located into a string, but doesn't + * parse it until it's being used. + */ + private static final class KnownConfiguration { + private final String content; + private final String filename; + private final String name; + private Configuration configuration; + + public KnownConfiguration(SafePath knownPath) throws IOException { + this.content = readContent(knownPath); + this.name = nameFromPath(knownPath.toPath()); + this.filename = nullSafeFileName(knownPath.toPath()); + } + + public boolean isNamed(String name) { + return filename.equals(name) || this.name.equals(name); + } + + public Configuration getConfigurationFile() throws IOException, ParseException { + if (configuration == null) { + configuration = JFCParser.createConfiguration(name, content); + } + return configuration; + } + + public String getName() { + return name; + } + + private static String readContent(SafePath knownPath) throws IOException { + if (SecuritySupport.getFileSize(knownPath) > MAXIMUM_FILE_SIZE) { + throw new IOException("Configuration with more than " + + MAXIMUM_FILE_SIZE + " characters can't be read."); + } + try (InputStream r = SecuritySupport.newFileInputStream(knownPath)) { + return JFC.readContent(r); + } + } + } + + private JFC() { + // private utility class + } + + /** + * Reads a configuration from a file. + * + * @param path the file containing the configuration, not {@code null} + * @return {@link Configuration}, not {@code null} + * @throws ParseException if the file can't be parsed + * @throws IOException if the file can't be read + * + * @throws SecurityException if a security manager exists and its + * checkRead method denies read access to the file. + * @see java.io.File#getPath() + * @see java.lang.SecurityManager#checkRead(java.lang.String) + */ + public static Configuration create(String name, Reader reader) throws IOException, ParseException { + return JFCParser.createConfiguration(name, reader); + } + + private static String nullSafeFileName(Path file) throws IOException { + Path filename = file.getFileName(); + if (filename == null) { + throw new IOException("Path has no file name"); + } + return filename.toString(); + } + + public static String nameFromPath(Path file) throws IOException { + String f = nullSafeFileName(file); + return f.substring(0, f.length() - JFCParser.FILE_EXTENSION.length()); + } + + // Invoked by DCmdStart + public static Configuration createKnown(String name) throws IOException, ParseException { + // Known name, no need for permission + for (KnownConfiguration known : getKnownConfigurations()) { + if (known.isNamed(name)) { + return known.getConfigurationFile(); + } + } + // Check JFC directory + SafePath path = SecuritySupport.JFC_DIRECTORY; + if (path != null && SecuritySupport.exists(path)) { + for (String extension : Arrays.asList("", JFCParser.FILE_EXTENSION)) { + SafePath file = new SafePath(path.toPath().resolveSibling(name + extension)); + if (SecuritySupport.exists(file) && !SecuritySupport.isDirectory(file)) { + try (Reader r = SecuritySupport.newFileReader(file)) { + String jfcName = nameFromPath(file.toPath()); + return JFCParser.createConfiguration(jfcName, r); + } + } + } + } + + // Assume path included in name + + Path localPath = Paths.get(name); + String jfcName = nameFromPath(localPath); + try (Reader r = Files.newBufferedReader(localPath)) { + return JFCParser.createConfiguration(jfcName, r); + } + } + + private static String readContent(InputStream source) throws IOException { + byte[] bytes = read(source, BUFFER_SIZE); + return new String(bytes, StandardCharsets.UTF_8); + } + + // copied from java.io.file.Files to avoid dependency on JDK 9 code + private static byte[] read(InputStream source, int initialSize) throws IOException { + int capacity = initialSize; + byte[] buf = new byte[capacity]; + int nread = 0; + int n; + for (;;) { + // read to EOF which may read more or less than initialSize (eg: file + // is truncated while we are reading) + while ((n = source.read(buf, nread, capacity - nread)) > 0) + nread += n; + + // if last call to source.read() returned -1, we are done + // otherwise, try to read one more byte; if that failed we're done too + if (n < 0 || (n = source.read()) < 0) + break; + + // one more byte was read; need to allocate a larger buffer + if (capacity <= MAX_BUFFER_SIZE - capacity) { + capacity = Math.max(capacity << 1, BUFFER_SIZE); + } else { + if (capacity == MAX_BUFFER_SIZE) + throw new OutOfMemoryError("Required array size too large"); + capacity = MAX_BUFFER_SIZE; + } + buf = Arrays.copyOf(buf, capacity); + buf[nread++] = (byte)n; + } + return (capacity == nread) ? buf : Arrays.copyOf(buf, nread); + } + + + /** + * Returns list of predefined configurations available. + * + * @return list of configurations, not null + */ + public static List getConfigurations() { + List configs = new ArrayList<>(); + for (KnownConfiguration knownConfig : getKnownConfigurations()) { + try { + configs.add(knownConfig.getConfigurationFile()); + } catch (IOException e) { + Logger.log(LogTag.JFR, LogLevel.WARN, "Could not load configuration " + knownConfig.getName() + ". " + e.getMessage()); + } catch (ParseException e) { + Logger.log(LogTag.JFR, LogLevel.WARN, "Could not parse configuration " + knownConfig.getName() + ". " + e.getMessage()); + } + } + return configs; + } + + private static List getKnownConfigurations() { + if (knownConfigurations == null) { + List configProxies = new ArrayList<>(); + for (SafePath p : SecuritySupport.getPredefinedJFCFiles()) { + try { + configProxies.add(new KnownConfiguration(p)); + } catch (IOException ioe) { + // ignore + } + } + knownConfigurations = configProxies; + } + return knownConfigurations; + } + + public static Configuration getPredefined(String name) throws IOException, ParseException { + for (KnownConfiguration knownConfig : getKnownConfigurations()) { + if (knownConfig.getName().equals(name)) { + return knownConfig.getConfigurationFile(); + } + } + throw new NoSuchFileException("Could not locate configuration with name " + name); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/jfc/JFCParser.java 2019-02-08 18:32:40.813997888 +0300 @@ -0,0 +1,85 @@ +/* + * 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.jfc; + +import java.io.CharArrayReader; +import java.io.CharArrayWriter; +import java.io.IOException; +import java.io.Reader; +import java.text.ParseException; +import jdk.internal.org.xml.sax.InputSource; +import jdk.internal.org.xml.sax.SAXException; +import jdk.internal.util.xml.SAXParser; +import jdk.internal.util.xml.impl.SAXParserImpl; +import jdk.jfr.Configuration; + +import jdk.jfr.internal.PrivateAccess; + +/** + * Parses a JDK Flight Recorder Configuration file (.jfc) + */ +final class JFCParser { + static final String FILE_EXTENSION = ".jfc"; + private static final int MAXIMUM_FILE_SIZE = 1024 * 1024; + + public static Configuration createConfiguration(String name, Reader reader) throws IOException, ParseException { + return createConfiguration(name, readContent(reader)); + } + + public static Configuration createConfiguration(String name, String content) throws IOException, ParseException { + try { + JFCParserHandler ch = new JFCParserHandler(); + parseXML(content, ch); + return PrivateAccess.getInstance().newConfiguration(name, ch.label, ch.description, ch.provider, ch.settings, content); + } catch (IllegalArgumentException iae) { + throw new ParseException(iae.getMessage(), -1); + } catch (SAXException e) { + ParseException pe = new ParseException("Error reading JFC file. " + e.getMessage(), -1); + pe.initCause(e); + throw pe; + } + } + + private static void parseXML(String content, JFCParserHandler ch) throws SAXException, IOException { + CharArrayReader r = new CharArrayReader(content.toCharArray()); + SAXParser parser = new SAXParserImpl(); + parser.parse(new InputSource(r), ch); + } + + private static String readContent(Reader r) throws IOException { + CharArrayWriter writer = new CharArrayWriter(1024); + int count = 0; + int ch; + while ((ch = r.read()) != -1) { + writer.write(ch); + count++; + if (count >= MAXIMUM_FILE_SIZE) { + throw new IOException("Presets with more than " + MAXIMUM_FILE_SIZE + " characters can't be read."); + } + } + return new String(writer.toCharArray()); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/jfc/JFCParserHandler.java 2019-02-08 18:32:40.957992854 +0300 @@ -0,0 +1,106 @@ +/* + * 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.jfc; +import java.util.LinkedHashMap; +import java.util.Map; + +import jdk.internal.org.xml.sax.Attributes; +import jdk.internal.org.xml.sax.SAXException; +import jdk.internal.org.xml.sax.helpers.DefaultHandler; + +final class JFCParserHandler extends DefaultHandler { + private static final String ELEMENT_CONFIGURATION = "configuration"; + private static final String ELEMENT_EVENT_TYPE = "event"; + private static final String ELEMENT_SETTING = "setting"; + private static final String ATTRIBUTE_NAME = "name"; + private static final String ATTRIBUTE_LABEL = "label"; + private static final String ATTRIBUTE_DESCRIPTION = "description"; + private static final String ATTRIBUTE_PROVIDER = "provider"; + private static final String ATTRIBUTE_VERSION = "version"; + + final Map settings = new LinkedHashMap(); + private String currentEventPath; + private String currentSettingsName; + private StringBuilder currentCharacters; + String label; + String provider; + String description; + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + switch (qName.toLowerCase()) { + case ELEMENT_CONFIGURATION: + String version = attributes.getValue(ATTRIBUTE_VERSION); + if (version == null || !version.startsWith("2.")) { + throw new SAXException("This version of Flight Recorder can only read JFC file format version 2.x"); + } + label = attributes.getValue(ATTRIBUTE_LABEL); + description = getOptional(attributes, ATTRIBUTE_DESCRIPTION, ""); + provider = getOptional(attributes, ATTRIBUTE_PROVIDER, ""); + break; + case ELEMENT_EVENT_TYPE: + currentEventPath = attributes.getValue(ATTRIBUTE_NAME); + break; + case ELEMENT_SETTING: + currentSettingsName = attributes.getValue(ATTRIBUTE_NAME); + break; + } + currentCharacters = null; + } + + private String getOptional(Attributes attributes, String name, String defaultValue) { + String value = attributes.getValue(name); + return value == null ? defaultValue : value; + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + if (currentCharacters == null) { + currentCharacters = new StringBuilder(length); + } + currentCharacters.append(ch, start, length); + } + + @Override + public void endElement(String uri, String localName, String qName) { + switch (qName.toLowerCase()) { + case ELEMENT_CONFIGURATION: + break; + case ELEMENT_EVENT_TYPE: + currentEventPath = null; + break; + case ELEMENT_SETTING: + String settingsValue = currentCharacters == null ? "" : currentCharacters.toString(); + settings.put(currentEventPath + "#" + currentSettingsName, "" + settingsValue); + currentSettingsName = null; + break; + } + } + + public Map getSettings() { + return settings; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/jfc/jfc.xsd 2019-02-08 18:32:41.101987820 +0300 @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/management/ManagementSupport.java 2019-02-08 18:32:41.245982786 +0300 @@ -0,0 +1,89 @@ +/* + * 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.management; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import jdk.jfr.EventType; +import jdk.jfr.internal.JVMSupport; +import jdk.jfr.internal.LogLevel; +import jdk.jfr.internal.LogTag; +import jdk.jfr.internal.Logger; +import jdk.jfr.internal.MetadataRepository; +import jdk.jfr.internal.Utils; +import jdk.jfr.internal.instrument.JDKEvents; + +/** + * The management API in module jdk.management.jfr should be built on top of the + * public API in jdk.jfr. Before putting more functionality here, consider if it + * should not be part of the public API, and if not, please provide motivation + * + */ +public final class ManagementSupport { + + // Purpose of this method is to expose the event types to the + // FlightRecorderMXBean without instantiating Flight Recorder. + // + // This allows: + // + // 1) discoverability, so event settings can be exposed without the need to + // create a new Recording in FlightrecorderMXBean. + // + // 2) a graphical JMX client to list all attributes to the user, without + // loading JFR memory buffers. This is especially important when there is + // no intent to use Flight Recorder. + // + // An alternative design would be to make FlightRecorder#getEventTypes + // static, but it would the make the API look strange + // + public static List getEventTypes() { + // would normally be checked when a Flight Recorder instance is created + Utils.checkAccessFlightRecorder(); + if (JVMSupport.isNotAvailable()) { + return new ArrayList<>(); + } + JDKEvents.initialize(); // make sure JDK events are available + return Collections.unmodifiableList(MetadataRepository.getInstance().getRegisteredEventTypes()); + } + + // Reuse internal code for parsing a timespan + public static long parseTimespan(String s) { + return Utils.parseTimespan(s); + } + + // Reuse internal code for formatting settings + public static final String formatTimespan(Duration dValue, String separation) { + return Utils.formatTimespan(dValue, separation); + } + + // Reuse internal logging mechanism + public static void logError(String message) { + Logger.log(LogTag.JFR, LogLevel.ERROR, message); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/settings/BooleanValue.java 2019-02-08 18:32:41.389977752 +0300 @@ -0,0 +1,74 @@ +/* + * 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.settings; + +import java.util.Set; + +/** + * Helper class for settings that use boolean numbers + * + */ +final class BooleanValue { + private String value = "false"; + private boolean booleanValue; + + private BooleanValue(boolean b) { + booleanValue = b; + value = b ? "true" : "false"; + } + + public String union(Set values) { + for (String v : values) { + if ("true".equals(v)) { + return "true"; + } + } + return "false"; + } + + public void setValue(String value) { + this.value = value; + this.booleanValue = Boolean.valueOf(value); + } + + public final String getValue() { + return this.value; + } + + public boolean getBoolean() { + return booleanValue; + } + + public static BooleanValue valueOf(String defaultValue) { + if ("true".equals(defaultValue)) { + return new BooleanValue(true); + } + if ("false".equals(defaultValue)) { + return new BooleanValue(false); + } + throw new InternalError("Unknown default value for settings '" + defaultValue + "'"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/settings/CutoffSetting.java 2019-02-08 18:32:41.533972718 +0300 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 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.settings; + +import java.util.Objects; +import java.util.Set; + +import jdk.jfr.BooleanFlag; +import jdk.jfr.Description; +import jdk.jfr.Label; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.Name; +import jdk.jfr.internal.Control; +import jdk.jfr.internal.PlatformEventType; +import jdk.jfr.internal.Type; +import jdk.jfr.internal.Utils; + +@MetadataDefinition +@Label("Cutoff") +@Description("Limit running time of event") +@Name(Type.SETTINGS_PREFIX + "Cutoff") +@BooleanFlag +public final class CutoffSetting extends Control { + private final static long typeId = Type.getTypeId(CutoffSetting.class); + + private String value = "0 ns"; + private final PlatformEventType eventType; + + public CutoffSetting(PlatformEventType eventType, String defaultValue) { + super(defaultValue); + this.eventType = Objects.requireNonNull(eventType); + } + + @Override + public String combine(Set values) { + long max = 0; + String text = "0 ns"; + for (String value : values) { + long l = parseValue(value); + if (l > max) { + text = value; + max = l; + } + } + return text; + } + + @Override + public void setValue(String value) { + long l = parseValue(value); + this.value = value; + eventType.setCutoff(l); + } + + private long parseValue(String value) { + return isInfinity(value) ? Long.MAX_VALUE : Utils.parseTimespan(value); + } + + @Override + public String getValue() { + return value; + } + + public static boolean isType(long typeId) { + return CutoffSetting.typeId == typeId; + } + + private static boolean isInfinity(String s) { + return s.equals("infinity"); + } + + public static long parseValueSafe(String value) { + if (value == null) { + return 0L; + } + try { + return isInfinity(value) ? Long.MAX_VALUE : Utils.parseTimespan(value); + } catch (NumberFormatException nfe) { + return 0L; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/settings/EnabledSetting.java 2019-02-08 18:32:41.677967684 +0300 @@ -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.settings; + +import java.util.Objects; +import java.util.Set; + +import jdk.jfr.Description; +import jdk.jfr.BooleanFlag; +import jdk.jfr.Label; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.Name; +import jdk.jfr.internal.PlatformEventType; +import jdk.jfr.internal.Type; +import jdk.jfr.internal.Control; + +@MetadataDefinition +@Label("Enabled") +@Description("Record event") +@Name(Type.SETTINGS_PREFIX + "Enabled") +@BooleanFlag +public final class EnabledSetting extends Control { + private final BooleanValue booleanValue; + private final PlatformEventType eventType; + + public EnabledSetting(PlatformEventType eventType, String defaultValue) { + super(defaultValue); + this.booleanValue = BooleanValue.valueOf(defaultValue); + this.eventType = Objects.requireNonNull(eventType); + } + + @Override + public String combine(Set values) { + return booleanValue.union(values); + } + + @Override + public void setValue(String value) { + booleanValue.setValue(value); + eventType.setEnabled(booleanValue.getBoolean()); + if (eventType.isEnabled() && !eventType.isJVM()) { + if (!eventType.isInstrumented()) { + eventType.markForInstrumentation(true); + } + } + } + + @Override + public String getValue() { + return booleanValue.getValue(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/settings/PeriodSetting.java 2019-02-08 18:32:41.829962370 +0300 @@ -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.internal.settings; + +import java.util.Objects; +import java.util.Set; + +import jdk.jfr.Description; +import jdk.jfr.Label; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.Name; +import jdk.jfr.internal.PlatformEventType; +import jdk.jfr.internal.Control; +import jdk.jfr.internal.Type; +import jdk.jfr.internal.Utils; + +@MetadataDefinition +@Label("Period") +@Description("Record event at interval") +@Name(Type.SETTINGS_PREFIX + "Period") +public final class PeriodSetting extends Control { + private static final long typeId = Type.getTypeId(PeriodSetting.class); + + public static final String EVERY_CHUNK = "everyChunk"; + public static final String BEGIN_CHUNK = "beginChunk"; + public static final String END_CHUNK = "endChunk"; + public static final String NAME = "period"; + private final PlatformEventType eventType; + private String value = EVERY_CHUNK; + + public PeriodSetting(PlatformEventType eventType, String defaultValue) { + super(defaultValue); + this.eventType = Objects.requireNonNull(eventType); + } + + @Override + public String combine(Set values) { + long min = Long.MAX_VALUE; + boolean beginChunk = false; + boolean endChunk = false; + String text = EVERY_CHUNK; + for (String value : values) { + switch (value) { + case EVERY_CHUNK: + beginChunk = true; + endChunk = true; + break; + case BEGIN_CHUNK: + beginChunk = true; + break; + case END_CHUNK: + endChunk = true; + break; + default: + long l = Utils.parseTimespan(value); + if (l < min) { + text = value; + min = l; + } + } + } + if (min != Long.MAX_VALUE) { + return text; + } + if (beginChunk && !endChunk) { + return BEGIN_CHUNK; + } + if (!beginChunk && endChunk) { + return END_CHUNK; + } + return text; + } + + @Override + public void setValue(String value) { + switch (value) { + case EVERY_CHUNK: + eventType.setPeriod(0, true, true); + break; + case BEGIN_CHUNK: + eventType.setPeriod(0, true, false); + break; + case END_CHUNK: + eventType.setPeriod(0, false, true); + break; + default: + eventType.setPeriod(Utils.parseTimespan(value) / 1_000_000, false, false); + } + this.value = value; + } + + @Override + public String getValue() { + return value; + } + + public static boolean isType(long typeId) { + return PeriodSetting.typeId == typeId; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/settings/StackTraceSetting.java 2019-02-08 18:32:41.973957336 +0300 @@ -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.settings; + +import java.util.Objects; +import java.util.Set; + +import jdk.jfr.Description; +import jdk.jfr.BooleanFlag; +import jdk.jfr.Label; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.Name; +import jdk.jfr.internal.PlatformEventType; +import jdk.jfr.internal.Control; +import jdk.jfr.internal.Type; + +@MetadataDefinition +@Label("Stack Trace") +@Name(Type.SETTINGS_PREFIX + "StackTrace") +@Description("Record stack traces") +@BooleanFlag +public final class StackTraceSetting extends Control { + private final static long typeId = Type.getTypeId(StackTraceSetting.class); + private final BooleanValue booleanValue; + private final PlatformEventType eventType; + + public StackTraceSetting(PlatformEventType eventType, String defaultValue) { + super(defaultValue); + this.booleanValue = BooleanValue.valueOf(defaultValue); + this.eventType = Objects.requireNonNull(eventType); + } + + @Override + public String combine(Set values) { + return booleanValue.union(values); + } + + @Override + public void setValue(String value) { + booleanValue.setValue(value); + eventType.setStackTraceEnabled(booleanValue.getBoolean()); + } + + @Override + public String getValue() { + return booleanValue.getValue(); + } + + public static boolean isType(long typeId) { + return StackTraceSetting.typeId == typeId; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/settings/ThresholdSetting.java 2019-02-08 18:32:42.121952163 +0300 @@ -0,0 +1,84 @@ +/* + * 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.settings; + +import java.util.Objects; +import java.util.Set; + +import jdk.jfr.Description; +import jdk.jfr.Label; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.Name; +import jdk.jfr.Timespan; +import jdk.jfr.internal.PlatformEventType; +import jdk.jfr.internal.Control; +import jdk.jfr.internal.Type; +import jdk.jfr.internal.Utils; +@MetadataDefinition +@Label("Threshold") +@Name(Type.SETTINGS_PREFIX + "Threshold") +@Description("Record event with duration above or equal to threshold") +@Timespan +public final class ThresholdSetting extends Control { + private final static long typeId = Type.getTypeId(ThresholdSetting.class); + private String value = "0 ns"; + private final PlatformEventType eventType; + + public ThresholdSetting(PlatformEventType eventType, String defaultValue) { + super(defaultValue); + this.eventType = Objects.requireNonNull(eventType); + } + + @Override + public String combine(Set values) { + long min = Long.MAX_VALUE; + String text = "0 ns"; + for (String value : values) { + long l = Utils.parseTimespan(value); + if (l < min) { + text = value; + min = l; + } + } + return text; + } + + @Override + public void setValue(String value) { + long l = Utils.parseTimespan(value); + this.value = value; + eventType.setThreshold(l); + } + + @Override + public String getValue() { + return value; + } + + public static boolean isType(long typeId) { + return ThresholdSetting.typeId == typeId; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/jfr/internal/test/WhiteBox.java 2019-02-08 18:32:42.269946990 +0300 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 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.test; + +public final class WhiteBox { + + private static boolean writeAllObjectSamples; + + /** + * If OldObjectSample event is enabled, calling this method + * ensures that all object samples are written, including short-lived objects. + * Purpose of this method is to increase determinism in tests. + * + * @param writeAllObjectSamples if all samples should be written or not + * + */ + public static void setWriteAllObjectSamples(boolean writeAllSamples) { + writeAllObjectSamples = writeAllSamples; + } + + public static boolean getWriteAllObjectSamples() { + return writeAllObjectSamples; + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/management/jfr/ConfigurationInfo.java 2019-02-08 18:32:42.413941956 +0300 @@ -0,0 +1,241 @@ +/* + * 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.management.jfr; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; + +import jdk.jfr.Configuration; + + +/** + * Management representation of a {@code Configuration}. + * + * @see Configuration + * + * @since 9 + */ +public final class ConfigurationInfo { + private final Map settings; + private final String name; + private final String label; + private final String description; + private final String provider; + private final String contents; + + ConfigurationInfo(Configuration config) { + this.settings = config.getSettings(); + this.name = config.getName(); + this.label = config.getLabel(); + this.description = config.getDescription(); + this.provider = config.getProvider(); + this.contents = config.getContents(); + } + + private ConfigurationInfo(CompositeData cd) { + this.settings = createMap(cd.get("settings")); + this.name = (String) cd.get("name"); + this.label = (String) cd.get("label"); + this.description = (String) cd.get("description"); + this.provider = (String) cd.get("provider"); + this.contents = (String) cd.get("contents"); + } + + private static Map createMap(Object o) { + if (o instanceof TabularData) { + TabularData td = (TabularData) o; + Collection values = td.values(); + Map map = new HashMap<>(values.size()); + for (Object value : td.values()) { + if (value instanceof CompositeData) { + CompositeData cdRow = (CompositeData) value; + Object k = cdRow.get("key"); + Object v = cdRow.get("value"); + if (k instanceof String && v instanceof String) { + map.put((String) k, (String) v); + } + } + } + return Collections.unmodifiableMap(map); + } + return Collections.emptyMap(); + } + + /** + * Returns the provider of the configuration associated with this + * {@code ConfigurationInfo} (for example, {@code "OpenJDK"}). + * + * @return the provider, or {@code null} if doesn't exist + * + * @see Configuration#getProvider() + */ + public String getProvider() { + return provider; + } + + /** + * Returns the textual representation of the configuration associated with + * this {@code ConfigurationInfo}, typically the contents of the + * configuration file that was used to create the configuration. + * + * @return contents, or {@code null} if doesn't exist + * + * @see Configuration#getContents() + */ + public String getContents() { + return contents; + } + + /** + * Returns the settings for the configuration associated with this + * {@code ConfigurationInfo}. + * + * @return a {@code Map} with settings, not {@code null} + * + * @see Configuration#getSettings() + */ + public Map getSettings() { + return settings; + } + + /** + * Returns the human-readable name (for example, {@code "Continuous"} or {@code "Profiling"}) for + * the configuration associated with this {@code ConfigurationInfo} + * + * @return the label, or {@code null} if doesn't exist + * + * @see Configuration#getLabel() + */ + public String getLabel() { + return label; + } + + /** + * Returns the name of the configuration associated with this + * {@code ConfigurationInfo} (for example, {@code "default"}). + * + * @return the name, or {@code null} if doesn't exist + * + * @see Configuration#getLabel() + */ + public String getName() { + return name; + } + + /** + * Returns a short sentence that describes the configuration associated with + * this {@code ConfigurationInfo} (for example, {@code "Low + * overhead configuration safe for continuous use in production + * environments"}. + * + * @return the description, or {@code null} if doesn't exist + */ + public String getDescription() { + return description; + } + + /** + * Returns a {@code ConfigurationInfo} object represented by the specified + * {@code CompositeData}. + *

+ * The following table shows the required attributes that the specified {@code CompositeData} must contain. + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Required names and types for CompositeData
NameType
name{@code String}
label{@code String}
description{@code String}
provider{@code String}
contents{@code String}
settings{@code javax.management.openmbean.TabularData} with a + * {@code TabularType} with the keys {@code "key"} and {@code "value"}, both + * of the {@code String} type
+ *
+ * + * @param cd {@code CompositeData} representing a {@code ConfigurationInfo} + * + * @throws IllegalArgumentException if {@code cd} does not represent a + * {@code ConfigurationInfo} with the required attributes + * + * @return a {@code ConfigurationInfo} object represented by {@code cd} if + * {@code cd} is not {@code null}, {@code null} otherwise + */ + public static ConfigurationInfo from(CompositeData cd) { + if (cd == null) { + return null; + } + return new ConfigurationInfo(cd); + } + + /** + * Returns a description of the configuration that is associated with this + * {@code ConfigurationInfo}. + * + * @return the description of the configuration, not {@code null} + */ + @Override + public String toString() { + Stringifier s = new Stringifier(); + s.add("name", name); + s.add("label", label); + s.add("description", description); + s.add("provider", provider); + return s.toString(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/management/jfr/EventTypeInfo.java 2019-02-08 18:32:42.553937062 +0300 @@ -0,0 +1,262 @@ +/* + * 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.management.jfr; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.StringJoiner; + +import javax.management.openmbean.CompositeData; + +import jdk.jfr.Category; +import jdk.jfr.EventType; +import jdk.jfr.SettingDescriptor; + +/** + * Management representation of an {@code EventType}. + * + * @see EventType + * + * @since 9 + */ +public final class EventTypeInfo { + private final List settings; + private final long id; + private final String name; + private final String description; + private final String label; + private final List categoryNames; + + // package private + EventTypeInfo(EventType eventType) { + this.settings = creatingSettingDescriptorInfos(eventType); + this.id = eventType.getId(); + this.name = eventType.getName(); + this.label = eventType.getLabel(); + this.description = eventType.getDescription(); + this.categoryNames = eventType.getCategoryNames(); + } + + private EventTypeInfo(CompositeData cd) { + this.settings = createSettings(cd.get("settings")); + this.id = (long) cd.get("id"); + this.name = (String) cd.get("name"); + this.label = (String) cd.get("label"); + this.description = (String) cd.get("description"); + this.categoryNames = createCategoryNames((Object[]) cd.get("category")); + } + + private static List createCategoryNames(Object[] array) { + List list = new ArrayList<>(array.length); + for (int i = 0; i < array.length; i++) { + list.add((String) array[i]); + } + return Collections.unmodifiableList(list); + } + + private static List creatingSettingDescriptorInfos(EventType eventType) { + List settings = eventType.getSettingDescriptors(); + List settingDescriptorInfos = new ArrayList<>(settings.size()); + for (SettingDescriptor s : settings) { + settingDescriptorInfos.add(new SettingDescriptorInfo(s)); + } + return Collections.unmodifiableList(settingDescriptorInfos); + } + + private static List createSettings(Object settings) { + if (settings != null && settings.getClass().isArray()) { + Object[] settingsArray = (Object[]) settings; + List list = new ArrayList<>(settingsArray.length); + for (Object cd : settingsArray) { + if (cd instanceof CompositeData) { + list.add(SettingDescriptorInfo.from((CompositeData) cd)); + } + } + return Collections.unmodifiableList(list); + } + return Collections.emptyList(); + } + + /** + * Returns the label, a human-readable name, associated with the event type + * for this {@code EventTypeInfo} (for example, {@code "Garbage Collection"}). + * + * @return the label, or {@code null} if a label is not set + * + * @see EventType#getLabel() + */ + public String getLabel() { + return label; + } + + /** + * + * Returns the list of human-readable names that makes up the category for this + * {@code EventTypeInfo} (for example, {@code "Java Virtual Machine"} or {@code "Garbage Collector"}). + * + * @return an immutable list of category names, or a list with the name + * {@code "Uncategorized"} if no category has been set + * + * @see EventType#getCategoryNames() + * @see Category + */ + public List getCategoryNames() { + return categoryNames; + } + + /** + * Returns the unique ID for the event type associated with this + * {@code EventTypeInfo}, not guaranteed to be the same for different Java + * Virtual Machines (JVMs) instances. + * + * @return the ID + * + * @see EventType#getId() + */ + public long getId() { + return id; + } + + /** + * Returns the name for the event type associated with this + * {@code EventTypeInfo} (for example, {@code "jdk.GarbageCollection"}). + * + * @return the name, not {@code null} + * + * @see EventType#getName() + */ + public String getName() { + return name; + } + + /** + * Returns a short sentence or two describing the event type associated with + * this {@code EventTypeInfo}, for example + * {@code "Garbage collection performed by the JVM""}. + * + * @return the description, or {@code null} if no description exists + * + * @see EventType#getDescription() + */ + public String getDescription() { + return description; + } + + /** + * Returns settings for the event type associated with this + * {@code EventTypeInfo}. + * + * @return the settings, not {@code null} + * + * @see EventType#getSettingDescriptors() + */ + public List getSettingDescriptors() { + return settings; + } + + /** + * Returns a description of this {@code EventTypeInfo}. + * + * @return description, not {@code null} + */ + @Override + public String toString() { + Stringifier s = new Stringifier(); + s.add("id", id); + s.add("name", name); + s.add("label", label); + s.add("description", description); + StringJoiner sj = new StringJoiner(", ", "{", "}"); + for (String categoryName : categoryNames) { + sj.add(categoryName); + } + s.add("category", sj.toString()); + return s.toString(); + } + + /** + * Returns an {@code EventType} represented by the specified + * {@code CompositeData} + *

+ * The supplied {@code CompositeData} must have the following item names and + * item types to be valid.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
The name and type the specified CompositeData must contain
NameType
id{@code Long}
name{@code String}
label{@code String}
description{@code String}
categoryArrayType(1, SimpleType.STRING)
settings{@code javax.management.openmbean.CompositeData[]} whose element type + * is the mapped type for {@link SettingDescriptorInfo} as specified in the + * {@link SettingDescriptorInfo#from} method.
+ *
+ * + * @param cd {@code CompositeData} representing the {@code EventTypeInfo} to + * return + * + * @throws IllegalArgumentException if {@code cd} does not represent a valid + * {@code EventTypeInfo} + * + * @return an {@code EventTypeInfo}, or {@code null} if {@code cd} is + * {@code null} + */ + public static EventTypeInfo from(CompositeData cd) { + if (cd == null) { + return null; + } + return new EventTypeInfo(cd); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/management/jfr/FlightRecorderMXBean.java 2019-02-08 18:32:42.701931889 +0300 @@ -0,0 +1,640 @@ +/* + * 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.management.jfr; + +import java.io.IOException; +import java.lang.management.PlatformManagedObject; +import java.time.Instant; +import java.util.List; +import java.util.Map; + +import jdk.jfr.Configuration; +import jdk.jfr.EventType; +import jdk.jfr.Recording; + +/** + * Management interface for controlling Flight Recorder. + *

+ * The object name for identifying the MXBean in the platform MBean + * server is:

{@code jdk.management.jfr:type=FlightRecorder}
+ *

+ * Flight Recorder can be configured in the following ways: + *

    + *
  • Recording options
    + * Specify how long a recording should last, and where and when data + * should be dumped.
  • + *
  • Settings
    + * Specify which events should be enabled and what kind information each + * event should capture.
  • + *
  • Configurations
    + * Predefined sets of settings, typically derived from a settings file, + * that specify the configuration of multiple events simultaneously.
  • + *
+ *

+ * See the package {@code jdk.jfr} documentation for descriptions of the settings + * syntax and the {@link ConfigurationInfo} class documentation for configuration information. + * + *

Recording options

+ *

+ * The following table shows the options names to use with {@link #setRecordingOptions(long, Map)} + * and {@link #getRecordingOptions(long)}. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Recording options
NameDescripionDefault valueFormatExample values
{@code name}Sets a human-readable recording nameString representation of the recording id{@code String}{@code "My Recording"},
+ * {@code "profiling"}
{@code maxAge}Specify the length of time that the data is kept in the disk repository until the + * oldest data may be deleted. Only works if {@code disk=true}, otherwise + * this parameter is ignored.{@code "0"} (no limit){@code "0"} if no limit is imposed, otherwise a 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)
+ *
{@code "2 h"},
+ * {@code "24 h"},
+ * {@code "2 d"},
+ * {@code "0"}
{@code maxSize}Specifies the size, measured in bytes, at which data is kept in disk + * repository. Only works if + * {@code disk=true}, otherwise this parameter is ignored.{@code "0"} (no limit)String representation of a {@code Long} value, must be positive{@code "0"},
+ * {@code "1000000000"}
{@code dumpOnExit}Dumps recording data to disk on Java Virtual Machine (JVM) exit{@code "false"}String representation of a {@code Boolean} value, {@code "true"} or + * {@code "false"}{@code "true"},
+ * {@code "false"}
{@code destination}Specifies the path where recording data is written when the recording stops.{@code "false"}See {@code Paths#getPath} for format.
+ * If this method is invoked from another process, the data is written on the + * machine where the target JVM is running. If destination is a relative path, it + * is relative to the working directory where the target JVM was started.}
{@code "c:\recording\recotding.jfr"},
+ * {@code "/recordings/recording.jfr"}, {@code "recording.jfr"}
{@code disk}Stores recorded data as it is recorded"false"String representation of a {@code Boolean} value, {@code "true"} or + * {@code "false"}{@code "true"},
+ * {@code "false"}
{@code duration}Sets how long the recording should be running{@code "0"} (no limit, continuous){@code "0"} if no limit should be imposed, otherwise a string + * representation of a positive {@code Long} 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)
+ *
{@code "60 s"},
+ * {@code "10 m"},
+ * {@code "4 h"},
+ * {@code "0"}
+ * + * @since 9 + */ +public interface FlightRecorderMXBean extends PlatformManagedObject { + /** + * String representation of the {@code ObjectName} for the + * {@code FlightRecorderMXBean}. + */ + public static final String MXBEAN_NAME = "jdk.management.jfr:type=FlightRecorder"; + + /** + * Creates a recording, but doesn't start it. + * + * @return a unique ID that can be used to start, stop, close and + * configure the recording + * + * @throws IllegalStateException if Flight Recorder can't be created (for + * example, if the Java Virtual Machine (JVM) lacks Flight Recorder + * support, or if the file repository can't be created or accessed) + * + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("control")} + * + * @see Recording + */ + long newRecording() throws IllegalStateException, SecurityException; + + /** + * Creates a snapshot recording of all available recorded data. + *

+ * A snapshot is a synthesized recording in a stopped state. If no data is + * available, a recording with size {@code 0} is returned. + *

+ * A snapshot provides stable access to data for later operations (for example, + * operations to change the time interval or to reduce the data size). + *

+ * The caller must close the recording when access to the data is no longer + * needed. + * + * @return a snapshot of all available recording data, not {@code null} + * + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("control")} + * + * @return a unique ID that can be used for reading recording data. + * + * @see Recording + */ + public long takeSnapshot(); + + /** + * Creates a copy of an existing recording, useful for extracting parts of a + * recording. + *

+ * The cloned recording contains the same recording data as the + * original, but it has a new ID and a name prefixed with + * {@code "Clone of recording"}. If the original recording is running, then + * the clone is also running. + * + * @param recordingId the recording ID of the recording to create a clone + * from + * + * @param stop if the newly created clone is stopped before + * returning. + * + * @return a unique ID that can be used to start, stop, + * close and configure the recording + * + * @throws IllegalArgumentException if a recording with the specified ID + * doesn't exist + * + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("control")} + * + * @see Recording + */ + long cloneRecording(long recordingId, boolean stop) throws IllegalArgumentException, SecurityException; + + /** + * Starts the recording with the specified ID. + *

+ * A recording that is stopped can't be restarted. + * + * @param recordingId ID of the recording to start + * + * @throws IllegalArgumentException if a recording with the specified ID + * doesn't exist + * + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("control")} + * + * @see Recording + */ + void startRecording(long recordingId) throws IllegalStateException, SecurityException; + + /** + * Stops the running recording with the specified ID. + * + * @param recordingId the ID of the recording to stop + * + * @return {@code true} if the recording is stopped, {@code false} + * otherwise + * + * @throws IllegalArgumentException if a recording with the specified ID + * doesn't exist + * @throws IllegalStateException if the recording is not running + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("control")} + * + * @see #newRecording() + */ + boolean stopRecording(long recordingId) throws IllegalArgumentException, IllegalStateException, SecurityException; + + /** + * Closes the recording with the specified ID and releases any system + * resources that are associated with the recording. + *

+ * If the recording is already closed, invoking this method has no effect. + * + * @param recordingId the ID of the recording to close + * + * @throws IllegalArgumentException if a recording with the specified ID + * doesn't exist + * @throws IOException if an I/O error occurs + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("control")} + * + * @see #newRecording() + */ + void closeRecording(long recordingId) throws IOException; + + /** + * Opens a data stream for the recording with the specified ID, or {@code 0} + * to get data irrespective of recording. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Recording stream options
NameDescripionDefault valueFormatExample values
{@code startTime}Specifies the point in time to start a recording stream. Due to + * how data is stored, some events that start or end prior to the + * start time may be included.{@code Instant.MIN_VALUE.toString()}ISO-8601. See {@link Instant#toString}
+ * or milliseconds since epoch
{@code "2015-11-03T00:00"},
+ * {@code "1446508800000"}
{@code endTime}Specifies the point in time to end a recording stream. Due to how + * data is stored, some events that start or end after the end time may + * be included.{@code Instant.MAX_VALUE.toString()}ISO-8601. See {@link Instant#toString}
+ * or milliseconds since epoch
{@code "2015-11-03T01:00"},
+ * {@code "1446512400000"}
{@code blockSize}Specifies the maximum number of bytes to read with a call to {@code readStream} + * {@code "50000"}A positive {@code long} value.
+ *
+ * Setting {@code blockSize} to a very high value may result in + * {@link OutOfMemoryError} or an {@link IllegalArgumentException}, if the + * Java Virtual Machine (JVM) deems the value too large to handle.
{@code "50000"},
+ * {@code "1000000"},
+ *
+ * If an option is omitted from the map the default value is used. + *

+ * The recording with the specified ID must be stopped before a stream can + * be opened. This restriction might be lifted in future releases. + * + * @param recordingId ID of the recording to open the stream for + * + * @param streamOptions a map that contains the options that controls the amount of data + * and how it is read, or {@code null} to get all data for the + * recording with the default block size + * + * @return a unique ID for the stream. + * + * @throws IllegalArgumentException if a recording with the iD doesn't + * exist, or if {@code options} contains invalid values + * + * @throws IOException if the recording is closed, an I/O error occurs, or + * no data is available for the specified recording or + * interval + * + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("control")} + */ + long openStream(long recordingId, Map streamOptions) throws IOException; + + /** + * Closes the recording stream with the specified ID and releases any system + * resources that are associated with the stream. + *

+ * If the stream is already closed, invoking this method has no effect. + * + * @param streamId the ID of the stream + * + * @throws IllegalArgumentException if a stream with the specified ID doesn't + * exist + * @throws IOException if an I/O error occurs while trying to close the stream + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("control")} + * + * @see #openStream(long, Map) + */ + void closeStream(long streamId) throws IOException; + + /** + * Reads a portion of data from the stream with the specified ID, or returns + * {@code null} if no more data is available. + *

+ * To read all data for a recording, invoke this method repeatedly until + * {@code null} is returned. + * + * @param streamId the ID of the stream + * + * @return byte array that contains recording data, or {@code null} when no more + * data is available + * @throws IOException if the stream is closed, or an I/O error occurred while + * trying to read the stream + * @throws IllegalArgumentException if no recording with the stream ID exists + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("monitor")} + */ + byte[] readStream(long streamId) throws IOException; + + /** + * Returns a map that contains the options for the recording with the + * specified ID (for example, the destination file or time span to keep + * recorded data). + *

+ * See {@link FlightRecorderMXBean} for available option names. + * + * @param recordingId the ID of the recording to get options for + * + * @return a map describing the recording options, not {@code null} + * + * @throws IllegalArgumentException if no recording with the + * specified ID exists + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("monitor")} + * + */ + Map getRecordingOptions(long recordingId) throws IllegalArgumentException; + + /** + * Returns a {@code Map} that contains the settings for the recording with the specified ID, + * (for example, the event thresholds) + *

+ * If multiple recordings are running at the same time, more data could be + * recorded than what is specified in the {@code Map} object. + *

+ * The name in the {@code Map} is the event name and the setting name separated by + * {@code "#"} (for example, {@code "jdk.VMInfo#period"}). The value + * is a textual representation of the settings value (for example, + * {@code "60 s"}). + * + * @param recordingId the ID of the recordings to get settings for + * + * @return a map that describes the recording settings, not {@code null} + * + * @throws IllegalArgumentException if no recording with the specified ID exists + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("monitor")} + */ + Map getRecordingSettings(long recordingId) throws IllegalArgumentException; + + /** + * Sets a configuration as a string representation for the recording with the + * specified ID. + * + * @param recordingId ID of the recording + * @param contents a string representation of the configuration file to use, + * not {@code null} + * @throws IllegalArgumentException if no recording with the + * specified ID exists or if the configuration could not be parsed. + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("control")} + * + * @see Configuration#getContents() + */ + void setConfiguration(long recordingId, String contents) throws IllegalArgumentException; + + /** + * Sets a predefined configuration for the recording with the specified ID. + * + * @param recordingId ID of the recording to set the configuration for + * @param configurationName the name of the configuration (for example, + * {@code "profile"} or {@code "default"}), not {@code null} + * @throws IllegalArgumentException if no recording with the + * specified ID exists + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("control")} + * + * @see #getConfigurations() + */ + void setPredefinedConfiguration(long recordingId, String configurationName) throws IllegalArgumentException; + + /** + * Sets and replaces all previous settings for the specified recording. + *

+ * A setting consists of a name/value pair, where name specifies the + * event and setting to configure, and the value specifies what to set + * it to. + *

+ * The name can be formed in the following ways: + *

+ * {@code + * + "#" + + * } + *

+ * or + *

+ * {@code + * + "#" + + * } + *

+ * For example, to set the sample interval of the CPU Load event to once every + * second, use the name {@code "jdk.CPULoad#period"} and the value + * {@code "1 s"}. If multiple events use the same name, for example if an event + * class is loaded in multiple class loaders, and differentiation is needed + * between them, then the name is {@code "56#period"}. The ID for an event is + * obtained by invoking {@link jdk.jfr.EventType#getId()} method and is valid + * for the Java Virtual Machine (JVM) instance that the event is registered in. + *

+ * A list of available event names is retrieved by invoking + * {@link jdk.jfr.FlightRecorder#getEventTypes()} and + * {@link jdk.jfr.EventType#getName()}. A list of available settings for an + * event type is obtained by invoking + * {@link jdk.jfr.EventType#getSettingDescriptors()} and + * {@link jdk.jfr.ValueDescriptor#getName()}. + *

+ * + * @param recordingId ID of the recording + * + * @param settings name value map of the settings to set, not {@code null} + * + * @throws IllegalArgumentException if no recording with the specified ID exists + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("control")} + * + * @see Recording#getId() + */ + void setRecordingSettings(long recordingId, Map settings) throws IllegalArgumentException; + + /** + * Configures the recording options (for example, destination file and time span + * to keep data). + *

+ * See {@link FlightRecorderMXBean} for a description of the options and values + * that can be used. Setting a value to {@code null} restores the value to the + * default value. + * + * @param recordingId the ID of the recording to set options for + * + * @param options name/value map of the settings to set, not {@code null} + * + * @throws IllegalArgumentException if no recording with the specified ID exists + * @throws java.lang.SecurityException if a security manager exists, and the + * caller does not have {@code ManagementPermission("control")} or an + * option contains a file that the caller does not have permission to + * operate on. + * @see Recording#getId() + */ + void setRecordingOptions(long recordingId, Map options) throws IllegalArgumentException; + + /** + * Returns the list of the available recordings, not necessarily running. + *

+ * MBeanServer access:
+ * The mapped type of {@code RecordingInfo} is {@code CompositeData} with + * attributes as specified in the {@link RecordingInfo#from + * RecordingInfo.from} method. + * + * @return list of recordings, not {@code null} + * + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("monitor")} + * + * @see RecordingInfo + * @see Recording + */ + List getRecordings(); + + /** + * Returns the list of predefined configurations for this Java Virtual Machine (JVM). + *

+ * MBeanServer access:
+ * The mapped type of {@code ConfigurationInfo} is {@code CompositeData} + * with attributes as specified in the {@link ConfigurationInfo#from + * ConfigurationInfo.from} method. + * + * @return the list of predefined configurations, not {@code null} + * + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("monitor")} + * + * @see ConfigurationInfo + * @see Configuration + */ + List getConfigurations(); + + /** + * Returns the list of currently registered event types. + *

+ * MBeanServer access:
+ * The mapped type of {@code EventTypeInfo} is {@code CompositeData} with + * attributes as specified in the {@link EventTypeInfo#from + * EventTypeInfo.from} method. + * + * @return the list of registered event types, not {@code null} + * + * @throws java.lang.SecurityException if a security manager exists and the + * caller does not have {@code ManagementPermission("monitor")} + * + * @see EventTypeInfo + * @see EventType + */ + List getEventTypes(); + + /** + * Writes recording data to the specified file. + *

+ * If this method is invoked remotely from another process, the data is written + * to a file named {@code outputFile} on the machine where the target Java + * Virtual Machine (JVM) is running. If the file location is a relative path, it + * is relative to the working directory where the target JVM was started. + * + * @param recordingId the ID of the recording to dump data for + * + * @param outputFile the system-dependent file name where data is written, not + * {@code null} + * + * @throws IOException if the recording can't be dumped due to an I/O error (for + * example, an invalid path) + * + * @throws IllegalArgumentException if a recording with the specified ID doesn't + * exist + * + * @throws IllegalStateException if the recording is not yet started or if it is + * already closed + * + * @throws SecurityException if a security manager exists and its + * {@code SecurityManager.checkWrite(java.lang.String)} method denies + * write access to the named file or the caller does not have + * {@code ManagmentPermission("control")} + * + * @see java.nio.file.Path#toString() + * @see Recording#dump(java.nio.file.Path) + */ + void copyTo(long recordingId, String outputFile) throws IOException, SecurityException; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/management/jfr/FlightRecorderMXBeanImpl.java 2019-02-08 18:32:42.845926856 +0300 @@ -0,0 +1,440 @@ +/* + * 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.management.jfr; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.nio.file.Paths; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.text.ParseException; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +import javax.management.AttributeChangeNotification; +import javax.management.AttributeNotFoundException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanNotificationInfo; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.ReflectionException; +import javax.management.StandardEmitterMBean; + +import jdk.jfr.Configuration; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.FlightRecorderListener; +import jdk.jfr.FlightRecorderPermission; +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.jfr.internal.management.ManagementSupport; + +// Instantiated by service provider +final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements FlightRecorderMXBean, NotificationEmitter { + + final class MXBeanListener implements FlightRecorderListener { + private final NotificationListener listener; + private final NotificationFilter filter; + private final Object handback; + private final AccessControlContext context; + + public MXBeanListener(NotificationListener listener, NotificationFilter filter, Object handback) { + this.context = AccessController.getContext(); + this.listener = listener; + this.filter = filter; + this.handback = handback; + } + + public void recordingStateChanged(Recording recording) { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + sendNotification(createNotication(recording)); + return null; + } + }, context); + } + } + + private static final String ATTRIBUTE_RECORDINGS = "Recordings"; + private static final String OPTION_MAX_SIZE = "maxSize"; + private static final String OPTION_MAX_AGE = "maxAge"; + private static final String OPTION_NAME = "name"; + private static final String OPTION_DISK = "disk"; + private static final String OPTION_DUMP_ON_EXIT = "dumpOnExit"; + private static final String OPTION_DURATION = "duration"; + private static final List OPTIONS = Arrays.asList(new String[] { OPTION_DUMP_ON_EXIT, OPTION_DURATION, OPTION_NAME, OPTION_MAX_AGE, OPTION_MAX_SIZE, OPTION_DISK, }); + private final StreamManager streamHandler = new StreamManager(); + private final Map changes = new ConcurrentHashMap<>(); + private final AtomicLong sequenceNumber = new AtomicLong(); + private final List listeners = new CopyOnWriteArrayList<>(); + private FlightRecorder recorder; + + FlightRecorderMXBeanImpl() { + super(FlightRecorderMXBean.class, true, new NotificationBroadcasterSupport(createNotificationInfo())); + } + + @Override + public void startRecording(long id) { + MBeanUtils.checkControl(); + getExistingRecording(id).start(); + } + + @Override + public boolean stopRecording(long id) { + MBeanUtils.checkControl(); + return getExistingRecording(id).stop(); + } + + @Override + public void closeRecording(long id) { + MBeanUtils.checkControl(); + getExistingRecording(id).close(); + } + + @Override + public long openStream(long id, Map options) throws IOException { + MBeanUtils.checkControl(); + if (!FlightRecorder.isInitialized()) { + throw new IllegalArgumentException("No recording available with id " + id); + } + // Make local copy to prevent concurrent modification + Map s = options == null ? new HashMap<>() : new HashMap<>(options); + Instant starttime = MBeanUtils.parseTimestamp(s.get("startTime"), Instant.MIN); + Instant endtime = MBeanUtils.parseTimestamp(s.get("endTime"), Instant.MAX); + int blockSize = MBeanUtils.parseBlockSize(s.get("blockSize"), StreamManager.DEFAULT_BLOCK_SIZE); + InputStream is = getExistingRecording(id).getStream(starttime, endtime); + if (is == null) { + throw new IOException("No recording data available"); + } + return streamHandler.create(is, blockSize).getId(); + } + + @Override + public void closeStream(long streamIdentifier) throws IOException { + MBeanUtils.checkControl(); + streamHandler.getStream(streamIdentifier).close(); + } + + @Override + public byte[] readStream(long streamIdentifier) throws IOException { + MBeanUtils.checkMonitor(); + return streamHandler.getStream(streamIdentifier).read(); + } + + @Override + public List getRecordings() { + MBeanUtils.checkMonitor(); + if (!FlightRecorder.isInitialized()) { + return Collections.emptyList(); + } + return MBeanUtils.transformList(getRecorder().getRecordings(), RecordingInfo::new); + } + + @Override + public List getConfigurations() { + MBeanUtils.checkMonitor(); + return MBeanUtils.transformList(Configuration.getConfigurations(), ConfigurationInfo::new); + } + + @Override + public List getEventTypes() { + MBeanUtils.checkMonitor(); + List eventTypes = AccessController.doPrivileged(new PrivilegedAction>() { + @Override + public List run() { + return ManagementSupport.getEventTypes(); + } + }, null, new FlightRecorderPermission("accessFlightRecorder")); + + return MBeanUtils.transformList(eventTypes, EventTypeInfo::new); + } + + @Override + public Map getRecordingSettings(long recording) throws IllegalArgumentException { + MBeanUtils.checkMonitor(); + return getExistingRecording(recording).getSettings(); + } + + @Override + public void setRecordingSettings(long recording, Map values) throws IllegalArgumentException { + Objects.requireNonNull(values); + MBeanUtils.checkControl(); + getExistingRecording(recording).setSettings(values); + } + + @Override + public long newRecording() { + MBeanUtils.checkControl(); + getRecorder(); // ensure notification listener is setup + return AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Recording run() { + return new Recording(); + } + }, null, new FlightRecorderPermission("accessFlightRecorder")).getId(); + } + + @Override + public long takeSnapshot() { + MBeanUtils.checkControl(); + return getRecorder().takeSnapshot().getId(); + } + + @Override + public void setConfiguration(long recording, String configuration) throws IllegalArgumentException { + Objects.requireNonNull(configuration); + MBeanUtils.checkControl(); + try { + Configuration c = Configuration.create(new StringReader(configuration)); + getExistingRecording(recording).setSettings(c.getSettings()); + } catch (IOException | ParseException e) { + throw new IllegalArgumentException("Could not parse configuration", e); + } + } + + @Override + public void setPredefinedConfiguration(long recording, String configurationName) throws IllegalArgumentException { + Objects.requireNonNull(configurationName); + MBeanUtils.checkControl(); + Recording r = getExistingRecording(recording); + for (Configuration c : Configuration.getConfigurations()) { + if (c.getName().equals(configurationName)) { + r.setSettings(c.getSettings()); + return; + } + } + throw new IllegalArgumentException("Could not find configuration with name " + configurationName); + } + + @Override + public void copyTo(long recording, String path) throws IOException { + Objects.requireNonNull(path); + MBeanUtils.checkControl(); + getExistingRecording(recording).dump(Paths.get(path)); + } + + @Override + public void setRecordingOptions(long recording, Map options) throws IllegalArgumentException { + Objects.requireNonNull(options); + MBeanUtils.checkControl(); + // Make local copy to prevent concurrent modification + Map ops = new HashMap(options); + for (Map.Entry entry : ops.entrySet()) { + Object key = entry.getKey(); + Object value = entry.getValue(); + if (!(key instanceof String)) { + throw new IllegalArgumentException("Option key must not be null, or other type than " + String.class); + } + if (!OPTIONS.contains(key)) { + throw new IllegalArgumentException("Unknown recording option: " + key + ". Valid options are " + OPTIONS + "."); + } + if (value != null && !(value instanceof String)) { + throw new IllegalArgumentException("Incorrect value for option " + key + ". Values must be of type " + String.class + " ."); + } + } + + Recording r = getExistingRecording(recording); + validateOption(ops, OPTION_DUMP_ON_EXIT, MBeanUtils::booleanValue); + validateOption(ops, OPTION_DISK, MBeanUtils::booleanValue); + validateOption(ops, OPTION_NAME, Function.identity()); + validateOption(ops, OPTION_MAX_AGE, MBeanUtils::duration); + validateOption(ops, OPTION_MAX_SIZE, MBeanUtils::size); + validateOption(ops, OPTION_DURATION, MBeanUtils::duration); + + // All OK, now set them.atomically + setOption(ops, OPTION_DUMP_ON_EXIT, "false", MBeanUtils::booleanValue, x -> r.setDumpOnExit(x)); + setOption(ops, OPTION_DISK, "true", MBeanUtils::booleanValue, x -> r.setToDisk(x)); + setOption(ops, OPTION_NAME, String.valueOf(r.getId()), Function.identity(), x -> r.setName(x)); + setOption(ops, OPTION_MAX_AGE, null, MBeanUtils::duration, x -> r.setMaxAge(x)); + setOption(ops, OPTION_MAX_SIZE, "0", MBeanUtils::size, x -> r.setMaxSize(x)); + setOption(ops, OPTION_DURATION, null, MBeanUtils::duration, x -> r.setDuration(x)); + } + + @Override + public Map getRecordingOptions(long recording) throws IllegalArgumentException { + MBeanUtils.checkMonitor(); + Recording r = getExistingRecording(recording); + Map options = new HashMap<>(10); + options.put(OPTION_DUMP_ON_EXIT, String.valueOf(r.getDumpOnExit())); + options.put(OPTION_DISK, String.valueOf(r.isToDisk())); + options.put(OPTION_NAME, String.valueOf(r.getName())); + options.put(OPTION_MAX_AGE, ManagementSupport.formatTimespan(r.getMaxAge(), " ")); + Long maxSize = r.getMaxSize(); + options.put(OPTION_MAX_SIZE, String.valueOf(maxSize == null ? "0" : maxSize.toString())); + options.put(OPTION_DURATION, ManagementSupport.formatTimespan(r.getDuration(), " ")); + return options; + } + + @Override + public long cloneRecording(long id, boolean stop) throws IllegalStateException, SecurityException { + MBeanUtils.checkControl(); + return getRecording(id).copy(stop).getId(); + } + + @Override + public ObjectName getObjectName() { + return MBeanUtils.createObjectName(); + } + + private Recording getExistingRecording(long id) { + if (FlightRecorder.isInitialized()) { + Recording recording = getRecording(id); + if (recording != null) { + return recording; + } + } + throw new IllegalArgumentException("No recording available with id " + id); + } + + private Recording getRecording(long id) { + List recs = getRecorder().getRecordings(); + return recs.stream().filter(r -> r.getId() == id).findFirst().orElse(null); + } + + private static void setOption(Map options, String name, String defaultValue, Function converter, Consumer setter) { + if (!options.containsKey(name)) { + return; + } + String v = options.get(name); + if (v == null) { + v = defaultValue; + } + try { + setter.accept(converter.apply(v)); + } catch (IllegalArgumentException iae) { + throw new IllegalArgumentException("Not a valid value for option '" + name + "'. " + iae.getMessage()); + } + } + + private static void validateOption(Map options, String name, Function validator) { + try { + String v = options.get(name); + if (v == null) { + return; // OK, will set default + } + validator.apply(v); + } catch (IllegalArgumentException iae) { + throw new IllegalArgumentException("Not a valid value for option '" + name + "'. " + iae.getMessage()); + } + } + + private FlightRecorder getRecorder() throws SecurityException { + // Synchronize on some private object that is always available + synchronized (streamHandler) { + if (recorder == null) { + recorder = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public FlightRecorder run() { + return FlightRecorder.getFlightRecorder(); + } + }, null, new FlightRecorderPermission("accessFlightRecorder")); + } + return recorder; + } + } + + private static MBeanNotificationInfo[] createNotificationInfo() { + String[] types = new String[] { AttributeChangeNotification.ATTRIBUTE_CHANGE }; + String name = AttributeChangeNotification.class.getName(); + String description = "Notifies if the RecordingState has changed for one of the recordings, for example if a recording starts or stops"; + MBeanNotificationInfo info = new MBeanNotificationInfo(types, name, description); + return new MBeanNotificationInfo[] { info }; + } + + @Override + public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) { + MXBeanListener mxbeanListener = new MXBeanListener(listener, filter, handback); + listeners.add(mxbeanListener); + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run(){ + FlightRecorder.addListener(mxbeanListener); + return null; + } + }, null, new FlightRecorderPermission("accessFlightRecorder")); + super.addNotificationListener(listener, filter, handback); + } + + @Override + public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { + removeListeners( x -> listener == x.listener); + super.removeNotificationListener(listener); + } + + @Override + public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException { + removeListeners( x -> listener == x.listener && filter == x.filter && handback == x.handback); + super.removeNotificationListener(listener, filter, handback); + } + + private void removeListeners(Predicate p) { + List toBeRemoved = new ArrayList<>(listeners.size()); + for (MXBeanListener l : listeners) { + if (p.test(l)) { + toBeRemoved.add(l); + FlightRecorder.removeListener(l); + } + } + listeners.removeAll(toBeRemoved); + } + + private Notification createNotication(Recording recording) { + try { + Long id = recording.getId(); + Object oldValue = changes.get(recording.getId()); + Object newValue = getAttribute(ATTRIBUTE_RECORDINGS); + if (recording.getState() != RecordingState.CLOSED) { + changes.put(id, newValue); + } else { + changes.remove(id); + } + return new AttributeChangeNotification(getObjectName(), sequenceNumber.incrementAndGet(), System.currentTimeMillis(), "Recording " + recording.getName() + " is " + + recording.getState(), ATTRIBUTE_RECORDINGS, newValue.getClass().getName(), oldValue, newValue); + } catch (AttributeNotFoundException | MBeanException | ReflectionException e) { + throw new RuntimeException("Could not create notifcation for FlightRecorderMXBean. " + e.getMessage(), e); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/management/jfr/MBeanUtils.java 2019-02-08 18:32:42.989921822 +0300 @@ -0,0 +1,130 @@ +/* + * 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.management.jfr; + +import java.lang.management.ManagementPermission; +import java.security.Permission; +import java.time.DateTimeException; +import java.time.Duration; +import java.time.Instant; +import java.time.format.DateTimeParseException; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +import jdk.jfr.internal.management.ManagementSupport; + +final class MBeanUtils { + + private static final Permission monitor = new ManagementPermission("monitor"); + private static final Permission control = new ManagementPermission("control"); + + static ObjectName createObjectName() { + try { + return new ObjectName(FlightRecorderMXBean.MXBEAN_NAME); + } catch (MalformedObjectNameException mne) { + throw new Error("Can't happen", mne); + } + } + + static void checkControl() { + SecurityManager secManager = System.getSecurityManager(); + if (secManager != null) { + secManager.checkPermission(control); + } + } + + static void checkMonitor() { + SecurityManager secManager = System.getSecurityManager(); + if (secManager != null) { + secManager.checkPermission(monitor); + } + } + + static List transformList(List source, Function function) { + return source.stream().map(function).collect(Collectors.toList()); + } + + static boolean booleanValue(String s) { + if ("true".equals(s)) { + return true; + } + if ("false".equals(s)) { + return false; + } + throw new IllegalArgumentException("Value must be true or false."); + } + + static Duration duration(String s) throws NumberFormatException { + if (s == null) { + return null; + } + long l = ManagementSupport.parseTimespan(s); + if (l == 0) { + return null; + } + return Duration.ofNanos(l); + } + + public static Instant parseTimestamp(String s, Instant defaultValue) { + if (s == null) { + return defaultValue; + } + try { + return Instant.parse(s); + } catch(DateTimeParseException e ) { + // OK, try with milliseconds since epoch + // before giving up. + } + try { + return Instant.ofEpochMilli(Long.parseLong(s)); + } catch (NumberFormatException | DateTimeException nfr) { + throw new IllegalArgumentException("Not a valid timestamp " + s); + } + } + + static Long size(String s) throws NumberFormatException { + long size = Long.parseLong(s); + if (size < 0) { + throw new IllegalArgumentException("Negative size not allowed"); + } + return size; + } + + public static int parseBlockSize(String string, int defaultSize) { + if (string == null) { + return defaultSize; + } + int size = Integer.parseInt(string); + if (size <1) { + throw new IllegalArgumentException("Block size must be at least 1 byte"); + } + return size; + } +} + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/management/jfr/RecordingInfo.java 2019-02-08 18:32:43.137916649 +0300 @@ -0,0 +1,398 @@ +/* + * 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.management.jfr; + +import java.nio.file.Path; +import java.time.Duration; +import java.time.Instant; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.management.openmbean.CompositeData; +import javax.management.openmbean.TabularData; + +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; + +/** + * Management representation of a {@code Recording}. + * + * @see Recording + * + * @since 9 + */ +public final class RecordingInfo { + private final long id; + private final String name; + private final String state; + private final boolean dumpOnExit; + private final long size; + private final boolean disk; + private final long maxAge; + private final long maxSize; + private final long startTime; + private final long stopTime; + private final String destination; + private final long durationInSeconds; + private final Map settings; + + // package private + RecordingInfo(Recording recording) { + id = recording.getId(); + name = recording.getName(); + state = recording.getState().toString(); + dumpOnExit = recording.getDumpOnExit(); + size = recording.getSize(); + disk = recording.isToDisk(); + + Duration d = recording.getMaxAge(); + if (d == null) { + maxAge = 0; + } else { + maxAge = d.getSeconds(); + } + maxSize = recording.getMaxSize(); + Instant s = recording.getStartTime(); + startTime = s == null ? 0L : s.toEpochMilli(); + Instant st = recording.getStopTime(); + stopTime = st == null ? 0L : st.toEpochMilli(); + Path p = recording.getDestination(); + destination = p == null ? null : p.toString(); + Duration duration = recording.getDuration(); + durationInSeconds = duration == null ? 0 : duration.getSeconds(); + settings = recording.getSettings(); + } + + private RecordingInfo(CompositeData cd) { + id = (int) cd.get("id"); + name = (String) cd.get("name"); + state = (String) cd.get("state"); + dumpOnExit = (boolean) cd.get("dumpOnExit"); + size = (long) cd.get("size"); + disk = (boolean) cd.get("disk"); + maxAge = (Long) cd.get("maxAge"); + maxSize = (Long) cd.get("maxSize"); + startTime = (Long) cd.get("startTime"); + stopTime = (Long) cd.get("stopTime"); + destination = (String) cd.get("destination"); + durationInSeconds = (long) cd.get("duration"); + settings = new LinkedHashMap<>(); + Object map = cd.get("settings"); + if (map instanceof TabularData) { + TabularData td = (TabularData) map; + List keyNames = td.getTabularType().getIndexNames(); + int size = keyNames.size(); + for (Object keys : td.keySet()) { + Object[] keyValues = ((List) keys).toArray(); + for (int i = 0; i < size; i++) { + String key = keyNames.get(i); + Object value = keyValues[i]; + if (value instanceof String) { + settings.put(key, (String) value); + } + } + } + } + } + + /** + * Returns the name of the recording associated with this + * {@code RecordingInfo}. + * + * @return the recording name, not {@code null} + * + * @see Recording#getName() + */ + public String getName() { + return name; + } + + /** + * Returns the unique ID for the recording associated with this + * {@code RecordingInfo}. + * + * @return the recording ID + * + * @see Recording#getId() + */ + public long getId() { + return id; + } + + /** + * Returns if the recording associated with this {@code RecordingInfo} + * should be dumped to file when the JVM exits. + * + * @return {@code true} if recording should be dumped on exit, {@code false} + * otherwise + * + * @see Recording#getDumpOnExit() + */ + public boolean getDumpOnExit() { + return dumpOnExit; + } + + /** + * Returns how many seconds data should be kept on disk, or {@code 0} if + * data is to be kept forever. + *

+ * In-memory recordings are not affected by maximum age. + * + * @see Recording#getMaxAge() + * @see Recording#setToDisk(boolean) + * @return how long data should be kept on disk, measured in seconds + * + */ + public long getMaxAge() { + return maxAge; + } + + /** + * Returns the amount of data, measured in bytes, the recording associated + * with this {@code RecordingInfo}, should be kept on disk, before it's + * rotated away, or {@code 0} if data is to be kept indefinitely. + *

+ * In-memory recordings are not affected by maximum size. + * + * @return the amount of data should be kept on disk, in bytes + * + * @see Recording#setToDisk(boolean) + * @see Recording#getMaxSize() + */ + public long getMaxSize() { + return maxSize; + } + + /** + * Returns a {@code String} representation of state of the recording + * associated with this {@code RecordingInfo}. + *

+ * Valid return values are {@code "NEW"}, {@code "DELAYED"}, {@code "STARTING"}, + * {@code "RUNNING"}, {@code "STOPPING"}, {@code "STOPPED"} and {@code "CLOSED"}. + * + * @return the recording state, not {@code null} + * + * @see RecordingState#toString() + * @see Recording#getState() + */ + public String getState() { + return state; + } + + /** + * Returns start time of the recording associated with this + * {@code RecordingInfo}, measured as ms since epoch, or {@code null} if the + * recording hasn't started. + * + * @return the start time of the recording, or {@code null} if the recording + * hasn't started + * + * @see Recording#getStartTime() + */ + public long getStartTime() { + return startTime; + } + + /** + * Returns the actual or expected stop time of the recording associated with + * this {@code RecordingInfo}, measured as ms since epoch, or {@code null} + * if the expected or actual stop time is not known, which can only happen + * if the recording has not yet been stopped. + * + * @return the stop time of recording, or {@code null} if recording hasn't + * been stopped. + * + * @see Recording#getStopTime() + */ + public long getStopTime() { + return stopTime; + } + + /** + * Returns the settings for the recording associated with this + * {@code RecordingInfo}. + * + * @return the recording settings, not {@code null} + * + * @see Recording#getSettings() + */ + public Map getSettings() { + return settings; + } + + /** + * Returns destination path where data, for the recording associated with + * this {@link RecordingInfo}, should be written when the recording stops, + * or {@code null} if the recording should not be written. + * + * @return the destination, or {@code null} if not set + * + * @see Recording#getDestination() + */ + public String getDestination() { + return destination; + } + + /** + * Returns a string description of the recording associated with this + * {@code RecordingInfo} + * + * @return description, not {@code null} + */ + @Override + public String toString() { + Stringifier s = new Stringifier(); + s.add("name", name); + s.add("id", id); + s.add("maxAge", maxAge); + s.add("maxSize", maxSize); + return s.toString(); + } + + /** + * Returns the amount data recorded by recording. associated with this + * {@link RecordingInfo}. + * + * @return the amount of recorded data, measured in bytes + */ + public long getSize() { + return size; + } + + /** + * Returns {@code true} if the recording associated with this + * {@code RecordingInfo} should be flushed to disk, when memory buffers are + * full, {@code false} otherwise. + * + * @return {@code true} if recording is to disk, {@code false} otherwise + */ + public boolean isToDisk() { + return disk; + } + + /** + * Returns the desired duration, measured in seconds, of the recording + * associated with this {@link RecordingInfo}, or {code 0} if no duration + * has been set. + * + * @return the desired duration, or {code 0} if no duration has been set + * + * @see Recording#getDuration() + */ + public long getDuration() { + return durationInSeconds; + } + + /** + * Returns a {@code RecordingInfo} represented by the specified + * {@code CompositeData} object. + *

+ * The specified {@code CompositeData} must have the following item names and + * item types to be valid.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Supported names and types in a specified {@code CompositeData} object
NameType
id{@code Long}
name{@code String}
state{@code String}
dumpOnExit{@code Boolean}
size{@code Long}
disk{@code Boolean}
maxAge{@code Long}
maxSize{@code Long}
startTime{@code Long}
stopTime{@code Long}
destination{@code String}
duration{@code Long}
settings{@code javax.management.openmbean.CompositeData[]} whose element type + * is the mapped type for {@link SettingDescriptorInfo} as specified in the + * {@link SettingDescriptorInfo#from} method.
+ *
+ * + * @param cd {@code CompositeData} representing the {@code RecordingInfo} to + * return + * + * @throws IllegalArgumentException if {@code cd} does not represent a valid + * {@code RecordingInfo} + * + * @return the {@code RecordingInfo} represented by {@code cd}, or + * {@code null} if {@code cd} is {@code null} + */ + public static RecordingInfo from(CompositeData cd) { + if (cd == null) { + return null; + } + return new RecordingInfo(cd); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/management/jfr/SettingDescriptorInfo.java 2019-02-08 18:32:43.285911475 +0300 @@ -0,0 +1,232 @@ +/* + * 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.management.jfr; + +import java.util.concurrent.Callable; + +import javax.management.openmbean.CompositeData; + +import jdk.jfr.EventType; +import jdk.jfr.SettingDescriptor; +import jdk.management.jfr.internal.FlightRecorderMXBeanProvider; + +/** + * Management class that describes a setting, for example name, description and + * default value. + * + * @see EventType#getSettingDescriptors() + * + * @since 9 + */ +public final class SettingDescriptorInfo { + + // Purpose of this static initializer is to allow + // FlightRecorderMXBeanProvider + // to be in an internal package and not visible, but at the same time allow + // it to instantiate FlightRecorderMXBeanImpl. + // + // The reason the mechanism is in this class is because it is light weight + // and can easily be triggered from FlightRecorderMXBeanProvider. + static { + FlightRecorderMXBeanProvider.setFlightRecorderMXBeanFactory(new Callable() { + @Override + public FlightRecorderMXBean call() throws Exception { + return new FlightRecorderMXBeanImpl(); + } + }); + } + + private final String name; + private final String label; + private final String description; + private final String typeName; + private final String contentType; + private final String defaultValue; + + // package private + SettingDescriptorInfo(SettingDescriptor settingDescriptor) { + this.name = settingDescriptor.getName(); + this.label = settingDescriptor.getLabel(); + this.description = settingDescriptor.getDescription(); + this.typeName = settingDescriptor.getTypeName(); + this.contentType = settingDescriptor.getContentType(); + this.defaultValue = settingDescriptor.getDefaultValue(); + } + + private SettingDescriptorInfo(CompositeData cd) { + this.name = (String) cd.get("name"); + this.label = (String) cd.get("label"); + this.description = (String) cd.get("description"); + this.typeName = (String) cd.get("typeName"); + this.defaultValue = (String) cd.get("defaultValue"); + this.contentType = (String) cd.get("contentType"); + } + + /** + * Returns the human-readable name of the setting associated with this + * {@code SettingDescriptorInfo} (for example, {@code "Threshold"}). + * + * @return the label for this setting, not {@code null} + */ + public String getLabel() { + return label; + } + + /** + * Returns the name of the setting associated with this + * {@code SettingDescriptorInfo} (for example, {@code "threshold"}). + * + * @return the name of this setting, not {@code null} + */ + public String getName() { + return name; + } + + /** + * Returns the description of the setting associated this + * {@code SettingDescriptorInfo} (for example, + * {@code "The duration an event must exceed to be be recorded"}). + * + * @return the description of this setting, not null + */ + public String getDescription() { + return description; + } + + /** + * Returns the type name of the setting associated this + * {@code SettingDescriptorInfo} (for example, + * {@code "jdk.settings.Threshold"}). + *

+ * The type can be used to identify what type of setting this is. + * + * @return the name of this settings type, not {@code null} + */ + public String getTypeName() { + return typeName; + } + + /** + * Returns the content type of the setting associated this + * {@code SettingDescriptorInfo} (for example, {@code "jdk.jfr.Timespan"}). + *

+ * The content type can be used to determine how the setting should be + * rendered in a graphical user interface. + * + * @return the name of this settings type, not {@code null} + */ + public String getContentType() { + return contentType; + } + + /** + * Returns the default value of the setting associated this + * {@code SettingDescriptorInfo} (for example, {@code "20 ms"}). + * + * @return default value for this setting, not {@code null} + * + * @see SettingDescriptor#getDefaultValue() + */ + public String getDefaultValue() { + return defaultValue; + } + + /** + * Returns an {@code SettingDescriptorInfo} represented by the specified + * {@code CompositeData} + *

+ * The supplied {@code CompositeData} must have the following item names and + * item types to be valid.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
The name and type the specified CompositeData must contain
NameType
name{@code String}
label{@code String}
description{@code String}
typeName{@code String}
contentType{@code String}
defaultValue{@code String}
+ *
+ * + * @param cd {@code CompositeData} representing the {@code SettingDescriptorInfo} to + * return + * + * @throws IllegalArgumentException if {@code cd} does not represent a valid + * {@code EventTypeInfo} + * + * @return a {@code SettingDescriptorInfo}, or {@code null} if {@code cd} is + * {@code null} + */ + public static SettingDescriptorInfo from(CompositeData cd) { + if (cd == null) { + return null; + } + return new SettingDescriptorInfo(cd); + } + + /** + * Returns a {@code String} description of this {@code SettingDescriptorInfo}. + * + * @return a string describing this setting, not {@code null} + */ + @Override + public String toString() { + Stringifier s = new Stringifier(); + s.add("name", name); + s.add("label", label); + s.add("description", description); + s.add("typeName", typeName); + s.add("contentType", contentType); + s.add("defaultValue", defaultValue); + return s.toString(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/management/jfr/Stream.java 2019-02-08 18:32:43.429906443 +0300 @@ -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.management.jfr; + +import java.io.BufferedInputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; + +final class Stream implements Closeable { + + private final long identifier; + private final BufferedInputStream inputStream; + private final byte[] buffer; + + private volatile long time; + + Stream(InputStream is, long identifier, int blockSize) { + this.inputStream = new BufferedInputStream(is, 50000); + this.identifier = identifier; + this.buffer = new byte[blockSize]; + } + + private void touch() { + time = System.currentTimeMillis(); + } + + public long getLastTouched() { + return time; + } + + public byte[] read() throws IOException { + // OK to reuse buffer since this + // is only used for serialization + touch(); + int read = inputStream.read(buffer); + if (read == -1) { + // null indicate no more data + return null; + } + if (read != buffer.length) { + byte[] smallerBuffer = new byte[read]; + System.arraycopy(buffer, 0, smallerBuffer, 0, read); + return smallerBuffer; + } + + return buffer; + } + + @Override + public void close() throws IOException { + inputStream.close(); + } + + public long getId() { + return identifier; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/management/jfr/StreamCleanupTask.java 2019-02-08 18:32:43.573901410 +0300 @@ -0,0 +1,50 @@ +/* + * 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.management.jfr; + +import java.util.TimerTask; + +final class StreamCleanupTask extends TimerTask { + + private final Stream stream; + private final StreamManager manager; + + StreamCleanupTask(StreamManager streamManager, Stream stream) { + this.stream = stream; + this.manager = streamManager; + } + + @Override + public void run() { + long lastTouched = stream.getLastTouched(); + long now = System.currentTimeMillis(); + if (now - lastTouched >= StreamManager.TIME_OUT) { + manager.destroy(stream); + } else { + manager.scheduleAbort(stream, lastTouched + StreamManager.TIME_OUT); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/management/jfr/StreamManager.java 2019-02-08 18:32:43.721896236 +0300 @@ -0,0 +1,82 @@ +/* + * 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.management.jfr; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Timer; +import java.util.concurrent.TimeUnit; + +final class StreamManager { + + public static final long TIME_OUT = TimeUnit.MINUTES.toMillis(2); + public static final int DEFAULT_BLOCK_SIZE = 50000; + + private static long idCounter = 0; + + private final Map streams = new HashMap<>(); + private Timer timer; + + public synchronized Stream getStream(long streamIdentifer) { + Stream stream = streams.get(streamIdentifer); + if (stream == null) { + throw new IllegalArgumentException("Unknown stream identifier " + streamIdentifer); + } + return stream; + } + + public synchronized Stream create(InputStream is, int blockSize) { + idCounter++; + Stream stream = new Stream(is, idCounter, blockSize); + streams.put(stream.getId(), stream); + + scheduleAbort(stream, System.currentTimeMillis() + TIME_OUT); + return stream; + } + + public synchronized void destroy(Stream stream) { + try { + stream.close(); + } catch (IOException e) { + // OK + } + streams.remove(stream.getId()); + if (streams.isEmpty()) { + timer.cancel(); + timer = null; + } + } + + public synchronized void scheduleAbort(Stream s, long when) { + if (timer == null) { + timer = new Timer(true); + } + timer.schedule(new StreamCleanupTask(this, s), new Date(when + TIME_OUT)); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/management/jfr/Stringifier.java 2019-02-08 18:32:43.861891343 +0300 @@ -0,0 +1,60 @@ +/* + * 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.management.jfr; +/** + * Helper class for generating toString() + * + */ +final class Stringifier { + private final StringBuilder sb = new StringBuilder(); + private boolean first = true; + + public void add(String name, Object value) { + if (first) { + first = false; + } else { + sb.append(" "); + } + boolean isString = value instanceof String; + sb.append(name).append("="); + if (value == null) { + sb.append("null"); + } else { + if (isString) { + sb.append("\""); + } + sb.append(value); + if (isString) { + sb.append("\""); + } + } + } + + @Override + public String toString() { + return sb.toString(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/src/share/classes/jdk/management/jfr/internal/FlightRecorderMXBeanProvider.java 2019-02-08 18:32:44.009886170 +0300 @@ -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.management.jfr.internal; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; + +import jdk.jfr.internal.management.ManagementSupport; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.management.jfr.SettingDescriptorInfo; +// XXX TODO +//import sun.management.spi.PlatformMBeanProvider; + +public final class FlightRecorderMXBeanProvider /*extends PlatformMBeanProvider */{ + + private final static class SingleMBeanComponent + /*implements PlatformComponent*/ { + private final String objectName; + private final Class mbeanInterface; + + public SingleMBeanComponent(String objectName, + Class mbeanInterface) { + this.objectName = objectName; + this.mbeanInterface = mbeanInterface; + } + +// @Override +// public Set mbeanInterfaceNames() { +// return Collections.singleton(mbeanInterface.getName()); +// } +// +// @Override +// public Map nameToMBeanMap() { +// FlightRecorderMXBean bean = getFlightRecorderMXBean(); +// if (bean != null) { +// return Collections.singletonMap(objectName, bean); +// } else { +// return Collections.emptyMap(); +// } +// } +// +// @Override +// public String getObjectNamePattern() { +// return objectName; +// } +// +// @Override +// public Set> mbeanInterfaces() { +// return Collections.singleton(mbeanInterface); +// } + } + + private static Callable flightRecorderMXBeanFactory; + + private static volatile FlightRecorderMXBean flightRecorderMXBean; + + public static FlightRecorderMXBean getFlightRecorderMXBean() { + FlightRecorderMXBean bean = flightRecorderMXBean; + if (bean == null) { + SettingDescriptorInfo.from(null); // Sets flightRecorderMXBeanFactory under lock + synchronized (flightRecorderMXBeanFactory) { + bean = flightRecorderMXBean; + if (bean != null) { + return bean; + } + try { + bean = flightRecorderMXBean = flightRecorderMXBeanFactory.call(); + } catch (Exception e) { + ManagementSupport.logError("Could not create Flight Recorder " + + "instance for MBeanServer. " + e.getMessage()); + } + } + } + return bean; + } + + public static void setFlightRecorderMXBeanFactory(Callable factory) { + flightRecorderMXBeanFactory = factory; + } + +// @Override +// public List> getPlatformComponentList() { +// String objectName = FlightRecorderMXBean.MXBEAN_NAME; +// Class mbeanInterface = FlightRecorderMXBean.class; +// return Collections.singletonList(new SingleMBeanComponent(objectName, mbeanInterface)); +// } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/TEST.properties 2019-02-08 18:32:44.157880997 +0300 @@ -0,0 +1,2 @@ +modules = jdk.jfr + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TEST.properties 2019-02-08 18:32:44.301875965 +0300 @@ -0,0 +1,2 @@ +modules = java.management + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestFieldAccess.java 2019-02-08 18:32:44.445870932 +0300 @@ -0,0 +1,151 @@ +/* + * Copyright (c) 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.api.consumer; + +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestFieldAccess + */ +public class TestFieldAccess { + + private static class MyEvent extends Event { + String stringField = "Hello"; + int intField = 4711; + long longField = 4712L; + short shortField = (short)67; + double doubleField = Double.NaN; + float floatField = Float.MIN_VALUE; + boolean booleanField = false; + Thread threadField = Thread.currentThread(); + Class classField = MyEvent.class; + } + + public static void main(String[] args) throws Throwable { + try (Recording r = new Recording()) { + r.enable(MyEvent.class); + r.start(); + MyEvent myEvent = new MyEvent(); + myEvent.commit(); + r.stop(); + List events = Events.fromRecording(r); + Events.hasEvents(events); + RecordedEvent event = events.get(0); + testHasField(event); + testGetField(event, myEvent); + } + } + + private static void testGetField(RecordedEvent event, MyEvent myEvent) { + String stringField = event.getValue("stringField"); + Asserts.assertEquals(stringField, myEvent.stringField); + + int intField = event.getValue("intField"); + Asserts.assertEquals(intField, myEvent.intField); + + long longField = event.getValue("longField"); + Asserts.assertEquals(longField, myEvent.longField); + + short shortField = event.getValue("shortField"); + Asserts.assertEquals(shortField, myEvent.shortField); + + double doubleField = event.getValue("doubleField"); + Asserts.assertEquals(doubleField, myEvent.doubleField); + + float floatField = event.getValue("floatField"); + Asserts.assertEquals(floatField, myEvent.floatField); + + boolean booleanField = event.getValue("booleanField"); + Asserts.assertEquals(booleanField, myEvent.booleanField); + + RecordedThread threadField = event.getValue("eventThread"); + Asserts.assertEquals(threadField.getJavaName(), myEvent.threadField.getName()); + String threadGroupName = event.getValue("eventThread.group.name"); + Asserts.assertEquals(threadField.getThreadGroup().getName(), threadGroupName); + + RecordedClass classField = event.getValue("classField"); + Asserts.assertEquals(classField.getName(), myEvent.classField.getName()); + String className = event.getValue("classField.name"); + Asserts.assertEquals(classField.getName(), className.replace("/", ".")); + + + try { + event.getValue("doesnotexist"); + } catch (IllegalArgumentException iae) { + // as expected + } + + try { + event.getValue("classField.doesnotexist"); + } catch (IllegalArgumentException iae) { + // as expected + } + + try { + event.getValue(null); + } catch (NullPointerException npe) { + // as expected + } + } + + private static void testHasField(RecordedEvent event) { + System.out.println(event); + Asserts.assertTrue(event.hasField("stringField")); + Asserts.assertTrue(event.hasField("intField")); + Asserts.assertTrue(event.hasField("longField")); + Asserts.assertTrue(event.hasField("shortField")); + Asserts.assertTrue(event.hasField("doubleField")); + Asserts.assertTrue(event.hasField("floatField")); + Asserts.assertTrue(event.hasField("threadField")); + Asserts.assertTrue(event.hasField("classField")); + Asserts.assertTrue(event.hasField("classField.name")); + Asserts.assertTrue(event.hasField("eventThread")); + Asserts.assertTrue(event.hasField("eventThread.group.name")); + Asserts.assertTrue(event.hasField("startTime")); + Asserts.assertTrue(event.hasField("stackTrace")); + Asserts.assertTrue(event.hasField("duration")); + Asserts.assertFalse(event.hasField("doesNotExist")); + Asserts.assertFalse(event.hasField("classField.doesNotExist")); + Asserts.assertFalse(event.hasField("")); + try { + event.hasField(null); + } catch (NullPointerException npe) { + // as expected + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestGetStackTrace.java 2019-02-08 18:32:44.589865899 +0300 @@ -0,0 +1,103 @@ +/* + * Copyright (c) 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.api.consumer; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertNotNull; +import static jdk.test.lib.Asserts.assertNull; +import static jdk.test.lib.Asserts.assertTrue; + +import java.util.List; +import java.util.function.Consumer; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedMethod; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEvent; + +/** + * @test + * @summary Verifies that a recorded JFR event has the correct stack trace info + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestGetStackTrace + */ +public class TestGetStackTrace { + + public static void main(String[] args) throws Throwable { + testStackTrace(r -> r.enable(SimpleEvent.class), TestGetStackTrace::assertNoStackTrace); + testStackTrace(r -> r.enable(SimpleEvent.class).withoutStackTrace(), TestGetStackTrace::assertStackTrace); + } + + private static void testStackTrace(Consumer recordingConfigurer, Consumer asserter) throws Throwable { + Recording r = new Recording(); + recordingConfigurer.accept(r); + r.start(); + SimpleEvent event = new SimpleEvent(); + event.commit(); + r.stop(); + List events = Events.fromRecording(r); + r.close(); + Events.hasEvents(events); + } + + private static void assertNoStackTrace(RecordedEvent re) { + assertNull(re.getStackTrace()); + } + + private static void assertStackTrace(RecordedEvent re) { + assertNotNull(re.getStackTrace()); + RecordedStackTrace strace = re.getStackTrace(); + assertEquals(strace.isTruncated(), false); + List frames = strace.getFrames(); + assertTrue(frames.size() > 0); + for (RecordedFrame frame : frames) { + assertFrame(frame); + } + } + + private static void assertFrame(RecordedFrame frame) { + int bci = frame.getBytecodeIndex(); + int line = frame.getLineNumber(); + boolean javaFrame = frame.isJavaFrame(); + RecordedMethod method = frame.getMethod(); + String type = frame.getType(); + System.out.println("*** Frame Info ***"); + System.out.println("bci=" + bci); + System.out.println("line=" + line); + System.out.println("type=" + type); + System.out.println("method=" + method); + System.out.println("***"); + Asserts.assertTrue(javaFrame, "Only Java frame are currently supported"); + Asserts.assertGreaterThanOrEqual(bci, -1); + Asserts.assertNotNull(method, "Method should not be null"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestHiddenMethod.java 2019-02-08 18:32:44.733860866 +0300 @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2014, 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.api.consumer; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; + +import java.time.Duration; +import java.util.List; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.test.lib.jfr.Events; + + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + + * + * @run main/othervm jdk.jfr.api.consumer.TestHiddenMethod + */ +public final class TestHiddenMethod { + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(MyEvent.class).withThreshold(Duration.ofMillis(0)); + recording.start(); + + // Commit event with hidden methods + ScriptEngineManager manager = new ScriptEngineManager(); + ScriptEngine engine = manager.getEngineByName("nashorn"); + engine.eval( + "function emit() {" + + " print('About to emit event from Javascript');" + + " var TestEvent = Java.type(\"jdk.jfr.api.consumer.TestHiddenMethod$MyEvent\");" + + " var event = new TestEvent;" + + " event.begin();" + + " event.end();" + + " event.commit();" + + " print('Event emitted from Javascript!');" + + "}" + + "emit();"); + + // Commit event with visible method + MyEvent visible = new MyEvent(); + visible.begin(); + visible.end(); + visible.commit(); + recording.stop(); + + List events = Events.fromRecording(recording); + assertEquals(2, events.size(), "Expected two events"); + RecordedEvent hiddenEvent = events.get(0); + RecordedEvent visibleEvent = events.get(1); + + System.out.println("hiddenEvent:" + hiddenEvent); + System.out.println("visibleEvent:" + visibleEvent); + + assertTrue(hasHiddenStackFrame(hiddenEvent), "No hidden frame in hidden event: " + hiddenEvent); + assertFalse(hasHiddenStackFrame(visibleEvent), "Hidden frame in visible event: " + visibleEvent); + } + + private static boolean hasHiddenStackFrame(RecordedEvent event) throws Throwable { + RecordedStackTrace stacktrace = event.getStackTrace(); + List frames = stacktrace.getFrames(); + assertFalse(frames.isEmpty(), "Stacktrace frames was empty"); + for (RecordedFrame frame : frames) { + if (frame.getMethod().isHidden()) { + return true; + } + } + return false; + } + + public static class MyEvent extends Event { + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestMethodGetModifiers.java 2019-02-08 18:32:44.881855694 +0300 @@ -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.api.consumer; + +import static jdk.test.lib.Asserts.assertNotNull; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedMethod; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEvent; + + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm -Xint jdk.jfr.api.consumer.TestMethodGetModifiers + */ +public final class TestMethodGetModifiers { + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.start(); + + SimpleEvent ev = new SimpleEvent(); + ev.commit(); + recording.stop(); + + List recordedEvents = Events.fromRecording(recording); + Events.hasEvents(recordedEvents); + RecordedEvent recordedEvent = recordedEvents.get(0); + + System.out.println("recorded event:" + recordedEvent); + + RecordedStackTrace stacktrace = recordedEvent.getStackTrace(); + List frames = stacktrace.getFrames(); + for (RecordedFrame frame : frames) { + RecordedMethod method = frame.getMethod(); + if (method.getName().equals("main")) { + System.out.println("'main' method: " + method); + int modifiers = TestMethodGetModifiers.class.getDeclaredMethod("main", (Class)String[].class).getModifiers(); + System.out.println("modifiers: " + modifiers); + Asserts.assertEquals(method.getModifiers(), modifiers, "Incorrect method modifier reported"); + RecordedClass type = method.getType(); + assertNotNull(type, "Recorded class can not be null"); + } + + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestReadTwice.java 2019-02-08 18:32:45.021850801 +0300 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 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.api.consumer; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.LinkedList; +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + + +/** + * @test + * @summary Reads the recorded file two times and verifies that both reads are the same + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestReadTwice + */ +public class TestReadTwice { + + private static class MyEvent extends Event { + } + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + r.enable(MyEvent.class).withoutStackTrace(); + r.start(); + + // Commit a single event to the recording + MyEvent event = new MyEvent(); + event.commit(); + + r.stop(); + + // Dump the recording to a file + Path path = Utils.createTempFile("read-twice", ".jfr"); + System.out.println("Dumping to " + path); + r.dump(path); + r.close(); + + // Read all events from the file in one go + List events = RecordingFile.readAllEvents(path); + + // Read again the same events one by one + RecordingFile rfile = new RecordingFile(path); + List events2 = new LinkedList<>(); + while (rfile.hasMoreEvents()) { + events2.add(rfile.readEvent()); + } + + // Compare sizes + Asserts.assertEquals(events.size(), events2.size()); + rfile.close(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestRecordedClassLoader.java 2019-02-08 18:32:45.165845769 +0300 @@ -0,0 +1,120 @@ +/* + * Copyright (c) 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.api.consumer; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedClassLoader; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.TestClassLoader; + +/** + * @test + * @summary Verifies the methods of the RecordedClassLoader + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestRecordedClassLoader + */ +public class TestRecordedClassLoader { + private final static String TEST_CLASS_NAME = "jdk.jfr.api.consumer.TestRecordedClassLoader$MyTestClass"; + private final static String EVENT_NAME = EventNames.ClassDefine; + + static class MyTestClass { + } + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withoutStackTrace(); + TestClassLoader cl = new TestClassLoader(); + recording.start(); + cl.loadClass(TEST_CLASS_NAME); + recording.stop(); + + List events = Events.fromRecording(recording); + boolean isDefined = false; + for (RecordedEvent event : events) { + RecordedClass definedClass = event.getValue("definedClass"); + if (TEST_CLASS_NAME.equals(definedClass.getName())) { + System.out.println(event); + + // get the RecordedClassLoader from the RecordedClass, the "definedClass" + RecordedClassLoader definingClassLoader = definedClass.getClassLoader(); + Asserts.assertNotNull(definingClassLoader, "Defining Class Loader should not be null"); + + // invoke RecordedClassLoader.getType() in order to validate the type of the RecordedClassLoader + RecordedClass definingClassLoaderType = definingClassLoader.getType(); + Asserts.assertNotNull(definingClassLoaderType, "The defining Class Loader type should not be null"); + + // verify matching types + Asserts.assertEquals(cl.getClass().getName(), definingClassLoaderType.getName(), + "Expected type " + cl.getClass().getName() + ", got type " + definingClassLoaderType.getName()); + + // get a RecordedClassLoader directly from the "definingClassLoader" field as well + RecordedClassLoader definingClassLoaderFromField = event.getValue("definingClassLoader"); + Asserts.assertNotNull(definingClassLoaderFromField, + "Defining Class Loader instantatiated from field should not be null"); + + // ensure that the class loader instance used in the test actually has a name + //Asserts.assertNotNull(cl.getName(), + // "Expected a valid name for the TestClassLoader"); + + // invoke RecordedClassLoader.getName() to get the name of the class loader instance + //Asserts.assertEquals(cl.getName(), definingClassLoader.getName(), + // "Defining Class Loader should have the same name as the original class loader"); + Asserts.assertEquals(definingClassLoaderFromField.getName(), definingClassLoader.getName(), + "Defining Class Loader representations should have the same class loader name"); + + // invoke uniqueID() + Asserts.assertGreaterThan(definingClassLoader.getId(), 0L, "Invalid id assignment"); + + // second order class loader information ("check class loader of the class loader") + RecordedClassLoader classLoaderOfDefClassLoader = definingClassLoaderType.getClassLoader(); + Asserts.assertNotNull(classLoaderOfDefClassLoader, + "The class loader for the definining class loader should not be null"); + // Asserts.assertEquals(cl.getClass().getClassLoader().getName(), classLoaderOfDefClassLoader.getName(), + // "Expected class loader name " + cl.getClass().getClassLoader().getName() + ", got name " + classLoaderOfDefClassLoader.getName()); + + RecordedClass classLoaderOfDefClassLoaderType = classLoaderOfDefClassLoader.getType(); + Asserts.assertNotNull(classLoaderOfDefClassLoaderType, + "The class loader type for the defining class loader should not be null"); + Asserts.assertEquals(cl.getClass().getClassLoader().getClass().getName(), classLoaderOfDefClassLoaderType.getName(), + "Expected type " + cl.getClass().getClassLoader().getClass().getName() + ", got type " + classLoaderOfDefClassLoaderType.getName()); + + Asserts.assertGreaterThan(definingClassLoader.getId(), classLoaderOfDefClassLoader.getId(), + "expected id assignment invariant broken for Class Loaders"); + + isDefined = true; + } + } + Asserts.assertTrue(isDefined, "No class define event found to verify RecordedClassLoader"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestRecordedEvent.java 2019-02-08 18:32:45.309840736 +0300 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 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.api.consumer; + +import java.util.List; + +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedClassLoader; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Verifies the methods of the RecordedEvent + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestRecordedEvent + */ +public class TestRecordedEvent { + + static class MyClass { + } + + static class TestEvent extends Event { + + @Description("MyField") + Class clzField = String.class; + int intField; + String stringField = "myString"; + Class myClass = MyClass.class; + } + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + r.start(); + TestEvent t = new TestEvent(); + t.commit(); + r.stop(); + List events = Events.fromRecording(r); + Events.hasEvents(events); + + Asserts.assertEquals(events.size(), 1); + + RecordedEvent event = events.get(0); + + List descriptors = event.getFields(); + + System.out.println("Descriptors"); + for (ValueDescriptor descriptor : descriptors) { + System.out.println(descriptor.getName()); + System.out.println(descriptor.getTypeName()); + } + System.out.println("Descriptors end"); + + Object recordedClass = event.getValue("clzField"); + Asserts.assertTrue(recordedClass instanceof RecordedClass, "Expected Recorded Class got " + recordedClass); + + Object recordedInt = event.getValue("intField"); + Asserts.assertTrue(recordedInt instanceof Integer); + + Object recordedString = event.getValue("stringField"); + System.out.println("recordedString class: " + recordedString.getClass()); + Asserts.assertTrue(recordedString instanceof String); + + Object myClass = event.getValue("myClass"); + Asserts.assertTrue(myClass instanceof RecordedClass, "Expected Recorded Class got " + recordedClass); + + RecordedClass myRecClass = (RecordedClass) myClass; + Asserts.assertEquals(MyClass.class.getName(), myRecClass.getName(), "Got " + myRecClass.getName()); + + Object recordedClassLoader = myRecClass.getValue("classLoader"); + Asserts.assertTrue(recordedClassLoader instanceof RecordedClassLoader, "Expected Recorded ClassLoader got " + recordedClassLoader); + + RecordedClassLoader myRecClassLoader = (RecordedClassLoader)recordedClassLoader; + ClassLoader cl = MyClass.class.getClassLoader(); + Asserts.assertEquals(cl.getClass().getName(), myRecClassLoader.getType().getName(), "Expected Recorded ClassLoader type to equal loader type"); + + Asserts.assertNotNull(myRecClass.getModifiers()); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestRecordedEventGetThread.java 2019-02-08 18:32:45.453835703 +0300 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 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.api.consumer; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEvent; + +/** + * @test + * @summary Tests that the RecordedEvent.getThread() returns th expected info + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestRecordedEventGetThread + */ +public class TestRecordedEventGetThread { + + private static final String MY_THREAD_NAME = "MY_THREAD_NAME"; + + public static void main(String[] args) throws Throwable { + Thread currentThread = Thread.currentThread(); + currentThread.setName(MY_THREAD_NAME); + long expectedThreadId = currentThread.getId(); + + Recording r = new Recording(); + r.start(); + SimpleEvent t = new SimpleEvent(); + t.commit(); + r.stop(); + List events = Events.fromRecording(r); + r.close(); + Events.hasEvents(events); + RecordedEvent event = events.get(0); + RecordedThread recordedThread = event.getThread(); + + Asserts.assertNotNull(recordedThread); + Asserts.assertEquals(recordedThread.getJavaName(), MY_THREAD_NAME); + Asserts.assertEquals(recordedThread.getJavaThreadId(), expectedThreadId); + Asserts.assertNotNull(recordedThread.getOSThreadId()); + Asserts.assertNotNull(recordedThread.getId()); + Asserts.assertEquals(recordedThread.getOSName(), MY_THREAD_NAME); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestRecordedEventGetThreadOther.java 2019-02-08 18:32:45.601830532 +0300 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 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.api.consumer; + +import java.nio.file.Path; +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +/** + * @test + * @summary Tests that the RecordedEvent.getThread() returns th expected info + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestRecordedEventGetThreadOther + */ +public class TestRecordedEventGetThreadOther { + + private static final String MY_THREAD_NAME = "MY_THREAD_NAME"; + private static long expectedThreadId; + private static Path dumpFilePath; + + static class TestEvent extends Event { + } + + static class PostingThread extends Thread { + + PostingThread() { + setName(MY_THREAD_NAME); + expectedThreadId = getId(); + } + + @Override + public void run() { + try { + System.out.println("Starting thread..."); + dumpFilePath = postEventAndDumpToFile(); + System.out.println("events dumped to the file " + dumpFilePath); + } catch (Throwable t) { + t.printStackTrace(); + Asserts.fail(); + } + } + } + + public static void main(String[] args) throws Throwable { + Thread.currentThread().setName("MyMainThread"); + + PostingThread thread = new PostingThread(); + thread.start(); + thread.join(); + System.out.println("testing dump in file " + dumpFilePath); + + List events = RecordingFile.readAllEvents(dumpFilePath); + Asserts.assertEquals(events.size(), 1); + + RecordedEvent event = events.get(0); + RecordedThread recordedThread = event.getThread(); + + Asserts.assertNotNull(recordedThread); + Asserts.assertEquals(recordedThread.getJavaName(), MY_THREAD_NAME); + Asserts.assertEquals(recordedThread.getJavaThreadId(), expectedThreadId); + Asserts.assertNotNull(recordedThread.getId()); + Asserts.assertEquals(recordedThread.getOSName(), MY_THREAD_NAME); + } + + private static Path postEventAndDumpToFile() throws Throwable { + Recording r = new Recording(); + r.start(); + TestEvent t = new TestEvent(); + t.commit(); + r.stop(); + Path path = Utils.createTempFile("event-thread", ".jfr"); + System.out.println("Created path: " + path); + r.dump(path); + r.close(); + return path; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestRecordedFrame.java 2019-02-08 18:32:45.745825499 +0300 @@ -0,0 +1,122 @@ +/* + * 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.api.consumer; + +import java.io.IOException; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedMethod; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEvent; + + +/** + * @test + * @summary Simple test for RecordedFrame APIs + * @key jfr + * + * @library /lib / + * @run main/othervm -Xint -XX:+UseInterpreter -Dinterpreted=true jdk.jfr.api.consumer.TestRecordedFrame + * @run main/othervm -Xcomp -XX:-UseInterpreter -Dinterpreted=false jdk.jfr.api.consumer.TestRecordedFrame + */ +public final class TestRecordedFrame { + + public static void main(String[] args) throws IOException { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + doTest(getLineNumber("main", stackTrace) + 1); + } + + /** + * Returns line number of the passed method for the passed stacktrace + */ + private static int getLineNumber(String methodName, StackTraceElement[] stackTrace) { + for (StackTraceElement ste : stackTrace) { + if (methodName.equals(ste.getMethodName())) { + return ste.getLineNumber(); + } + } + throw new RuntimeException("Unexpected error: could not analyze stacktrace"); + } + + public static void doTest(int lineNumber) throws IOException { + + System.out.println("Enetring method"); + + Recording recording = new Recording(); + recording.start(); + + SimpleEvent ev = new SimpleEvent(); + commitEvent(ev); + recording.stop(); + + List recordedEvents = Events.fromRecording(recording); + Events.hasEvents(recordedEvents); + RecordedEvent recordedEvent = recordedEvents.get(0); + + RecordedStackTrace stacktrace = recordedEvent.getStackTrace(); + List frames = stacktrace.getFrames(); + for (RecordedFrame frame : frames) { + + // All frames are java frames + Asserts.assertTrue(frame.isJavaFrame()); + // Verify the main() method frame + RecordedMethod method = frame.getMethod(); + if (method.getName().equals("main")) { + + // Frame type + String type = frame.getType(); + System.out.println("type: " + type); + Asserts.assertTrue( + type.equals("Interpreted") + || type.equals("JIT compiled") + || type.equals("Inlined")); + + Asserts.assertEquals(lineNumber, frame.getLineNumber()); + + boolean isInterpreted = "Interpreted".equals(type); + boolean expectedInterpreted = "true".equals(System.getProperty("interpreted")); + Asserts.assertEquals(isInterpreted, expectedInterpreted); + + int bci = frame.getBytecodeIndex(); + + System.out.println("bci: " + bci); + Asserts.assertTrue(bci > 0); + } + + } + + } + + private static void commitEvent(SimpleEvent ev) { + System.out.println("commit"); + ev.commit(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestRecordedFullStackTrace.java 2019-02-08 18:32:45.885820607 +0300 @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2013, 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.api.consumer; + +import java.nio.file.Path; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.RecurseThread; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestRecordedFullStackTrace + */ +public class TestRecordedFullStackTrace { + + private final static String EVENT_NAME = EventNames.ExecutionSample; + private final static int MAX_DEPTH = 64; // currently hardcoded in jvm + + public static void main(String[] args) throws Throwable { + + RecurseThread[] threads = new RecurseThread[3]; + for (int i = 0; i < threads.length; ++i) { + int depth = MAX_DEPTH - 1 + i; + threads[i] = new RecurseThread(depth); + threads[i].setName("recursethread-" + depth); + threads[i].start(); + } + + for (RecurseThread thread : threads) { + while (!thread.isInRunLoop()) { + Thread.sleep(20); + } + } + + assertStackTraces(threads); + + for (RecurseThread thread : threads) { + thread.quit(); + thread.join(); + } + } + + private static void assertStackTraces(RecurseThread[] threads) throws Throwable { + Path path = null; + do { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withPeriod(Duration.ofMillis(50)); + recording.start(); + Thread.sleep(500); + recording.stop(); + // Dump the recording to a file + path = Utils.createTempFile("execution-stack-trace", ".jfr"); + System.out.println("Dumping to " + path); + recording.dump(path); + recording.close(); + } while (!hasValidStackTraces(path, threads)); + } + + private static boolean hasValidStackTraces(Path path, RecurseThread[] threads) throws Throwable { + boolean[] isEventFound = new boolean[threads.length]; + + for (RecordedEvent event : RecordingFile.readAllEvents(path)) { + //System.out.println("Event: " + event); + String threadName = Events.assertField(event, "sampledThread.javaName").getValue(); + long threadId = Events.assertField(event, "sampledThread.javaThreadId").getValue(); + + for (int threadIndex = 0; threadIndex < threads.length; ++threadIndex) { + RecurseThread currThread = threads[threadIndex]; + if (threadId == currThread.getId()) { + System.out.println("ThreadName=" + currThread.getName() + ", depth=" + currThread.totalDepth); + Asserts.assertEquals(threadName, currThread.getName(), "Wrong thread name"); + if ("recurseEnd".equals(getTopMethodName(event))) { + isEventFound[threadIndex] = true; + checkEvent(event, currThread.totalDepth); + break; + } + } + } + } + + for (int i = 0; i < threads.length; ++i) { + String msg = "threadIndex=%d, recurseDepth=%d, isEventFound=%b%n"; + System.out.printf(msg, i, threads[i].totalDepth, isEventFound[i]); + } + for (int i = 0; i < threads.length; ++i) { + if (!isEventFound[i]) { + // no assertion, let's retry. + // Could be race condition, i.e safe point during Thread.sleep + System.out.println("Falied to validate all threads, will retry."); + return false; + } + } + return true; + } + + public static String getTopMethodName(RecordedEvent event) { + List frames = event.getStackTrace().getFrames(); + Asserts.assertFalse(frames.isEmpty(), "JavaFrames was empty"); + return frames.get(0).getMethod().getName(); + } + + private static void checkEvent(RecordedEvent event, int expectedDepth) throws Throwable { + RecordedStackTrace stacktrace = null; + try { + stacktrace = event.getStackTrace(); + List frames = stacktrace.getFrames(); + Asserts.assertEquals(Math.min(MAX_DEPTH, expectedDepth), frames.size(), "Wrong stacktrace depth. Expected:" + expectedDepth); + List expectedMethods = getExpectedMethods(expectedDepth); + Asserts.assertEquals(expectedMethods.size(), frames.size(), "Wrong expectedMethods depth. Test error."); + + for (int i = 0; i < frames.size(); ++i) { + String name = frames.get(i).getMethod().getName(); + String expectedName = expectedMethods.get(i); + System.out.printf("method[%d]=%s, expected=%s%n", i, name, expectedName); + Asserts.assertEquals(name, expectedName, "Wrong method name"); + } + + boolean isTruncated = stacktrace.isTruncated(); + boolean isTruncateExpected = expectedDepth > MAX_DEPTH; + Asserts.assertEquals(isTruncated, isTruncateExpected, "Wrong value for isTruncated. Expected:" + isTruncateExpected); + + String firstMethod = frames.get(frames.size() - 1).getMethod().getName(); + boolean isFullTrace = "run".equals(firstMethod); + String msg = String.format("Wrong values for isTruncated=%b, isFullTrace=%b", isTruncated, isFullTrace); + Asserts.assertTrue(isTruncated != isFullTrace, msg); + } catch (Throwable t) { + System.out.println(String.format("stacktrace:%n%s", stacktrace)); + throw t; + } + } + + private static List getExpectedMethods(int depth) { + List methods = new ArrayList<>(); + methods.add("recurseEnd"); + for (int i = 0; i < depth - 2; ++i) { + methods.add((i % 2) == 0 ? "recurseA" : "recurseB"); + } + methods.add("run"); + if (depth > MAX_DEPTH) { + methods = methods.subList(0, MAX_DEPTH); + } + return methods; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestRecordedInstantEventTimestamp.java 2019-02-08 18:32:46.033815435 +0300 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 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.api.consumer; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEvent; + +/** + * @test + * @summary Tests that an instant event gets recorded with its start time equal to its end time + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestRecordedInstantEventTimestamp + */ +public class TestRecordedInstantEventTimestamp { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + r.start(); + SimpleEvent s = new SimpleEvent(); + s.commit(); + r.stop(); + + List events = Events.fromRecording(r); + Events.hasEvents(events); + RecordedEvent event = events.get(0); + Asserts.assertEquals(event.getStartTime(), event.getEndTime()); + + r.close(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestRecordedMethodDescriptor.java 2019-02-08 18:32:46.177810403 +0300 @@ -0,0 +1,97 @@ +/* + * 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.api.consumer; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertNotNull; +import static jdk.test.lib.Asserts.assertTrue; + +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedMethod; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.test.lib.jfr.Events; + + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestRecordedMethodDescriptor + */ +public final class TestRecordedMethodDescriptor { + + private static boolean isMainMethodDescriptorRecorded; + private static final String MAIN_METHOD_DESCRIPTOR = "([Ljava/lang/String;)V"; + private static final String MAIN_METHOD_NAME = "main"; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(MyEvent.class).withStackTrace(); + recording.start(); + + MyEvent event = new MyEvent(); + event.begin(); + event.end(); + event.commit(); + recording.stop(); + + List recordedEvents = Events.fromRecording(recording); + assertEquals(1, recordedEvents.size(), "Expected one event"); + RecordedEvent recordedEvent = recordedEvents.get(0); + + RecordedStackTrace stacktrace = recordedEvent.getStackTrace(); + List frames = stacktrace.getFrames(); + assertFalse(frames.isEmpty(), "Stacktrace frames was empty"); + for (RecordedFrame frame : frames) { + analyzeRecordedMethodDescriptor(frame.getMethod()); + } + + assertTrue(isMainMethodDescriptorRecorded, "main() method descriptor has never been recorded"); + } + + private static void analyzeRecordedMethodDescriptor(RecordedMethod method) { + + String descr = method.getDescriptor(); + assertNotNull(descr, "Method descriptor is null"); + String name = method.getName(); + assertNotNull(name, "Method name is null"); + + if (name.equals(MAIN_METHOD_NAME) && descr.equals(MAIN_METHOD_DESCRIPTOR)) { + assertFalse(isMainMethodDescriptorRecorded, "main() method descriptor already recorded"); + isMainMethodDescriptorRecorded = true; + } + } + + public static class MyEvent extends Event { + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestRecordedObject.java 2019-02-08 18:32:46.317805511 +0300 @@ -0,0 +1,408 @@ +/* + * Copyright (c) 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.api.consumer; + +import java.io.IOException; +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Function; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.StackTrace; +import jdk.jfr.Timespan; +import jdk.jfr.Timestamp; +import jdk.jfr.Unsigned; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.consumer.RecordedThread; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Verifies the methods of the RecordedObject + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestRecordedObject + */ +public class TestRecordedObject { + + private final static boolean BOOLEAN_VALUE = true; + private final static byte VALUE = 47; + private final static String STRING_VALUE = "47"; + private final static Class CLASS_VALUE = String.class; + private final static Thread THREAD_VALUE = Thread.currentThread(); + private final static Instant INSTANT_VALUE = Instant.now(); + private final static Duration DURATION_VALUE = Duration.ofSeconds(47); + + @StackTrace(false) + static final class EventWithValues extends Event { + boolean booleanField = BOOLEAN_VALUE; + byte byteField = VALUE; + char charField = VALUE; + short shortField = VALUE; + int intField = VALUE; + long longField = VALUE; + float floatField = VALUE; + double doubleField = VALUE; + String stringField = STRING_VALUE; + Class classField = CLASS_VALUE; + Thread threadField = THREAD_VALUE; + @Timespan(Timespan.NANOSECONDS) + long durationField = DURATION_VALUE.toNanos(); + @Timestamp(Timestamp.MILLISECONDS_SINCE_EPOCH) + long instantField = INSTANT_VALUE.toEpochMilli(); + Thread nullField = null; + Class nullField2 = null; + + @Timespan(Timespan.MICROSECONDS) + long durationMicros = DURATION_VALUE.toNanos() / 1000; + + @Timespan(Timespan.MILLISECONDS) + long durationMillis = DURATION_VALUE.toMillis(); + + @Timespan(Timespan.SECONDS) + //long durationSeconds = (DURATION_VALUE.toMinutes() * 60); + long durationSeconds = DURATION_VALUE.toMillis() / 1000; + + @Timestamp(Timestamp.MILLISECONDS_SINCE_EPOCH) + long instantMillis = 1000; + + @Timestamp(Timespan.TICKS) + long instantTicks = 0; + + @Unsigned + byte unsignedByte = Byte.MIN_VALUE; + @Unsigned + char unsignedChar = 'q'; + @Unsigned + short unsignedShort = Short.MIN_VALUE; + @Unsigned + int unsignedInt = Integer.MIN_VALUE; + @Unsigned + long unsignedLong = Long.MIN_VALUE; // unsigned should be ignored + @Unsigned + float unsignedFloat = Float.MIN_VALUE; // unsigned should be ignored + @Unsigned + double unsignedDouble = Double.MIN_VALUE; // unsigned should be ignored + + } + + private final static Set ALL = createAll(); + + public static void main(String[] args) throws Throwable { + + RecordedObject event = makeRecordedObject(); + + // Primitives + testGetBoolean(event); + testGetByte(event); + testGetChar(event); + testGetShort(event); + testGetInt(event); + testGetLong(event); + testGetDouble(event); + testGetFloat(event); + + // // Complex types + testGetString(event); + testGetInstant(event); + testGetDuration(event); + testGetThread(event); + testGetClass(event); + + // Misc. + testNestedNames(event); + testTimeUnits(event); + testUnsigned(event); + } + + private static void testUnsigned(RecordedObject event) { + // Unsigned byte value + Asserts.assertEquals(event.getByte("unsignedByte"), Byte.MIN_VALUE); + Asserts.assertEquals(event.getInt("unsignedByte"), Byte.toUnsignedInt(Byte.MIN_VALUE)); + Asserts.assertEquals(event.getLong("unsignedByte"), Byte.toUnsignedLong(Byte.MIN_VALUE)); + Asserts.assertEquals(event.getShort("unsignedByte"), (short)Byte.toUnsignedInt(Byte.MIN_VALUE)); + + // Unsigned char, nothing should happen, it is unsigned + Asserts.assertEquals(event.getChar("unsignedChar"), 'q'); + Asserts.assertEquals(event.getInt("unsignedChar"), (int)'q'); + Asserts.assertEquals(event.getLong("unsignedChar"), (long)'q'); + + // Unsigned short + Asserts.assertEquals(event.getShort("unsignedShort"), Short.MIN_VALUE); + Asserts.assertEquals(event.getInt("unsignedShort"), Short.toUnsignedInt(Short.MIN_VALUE)); + Asserts.assertEquals(event.getLong("unsignedShort"), Short.toUnsignedLong(Short.MIN_VALUE)); + + // Unsigned int + Asserts.assertEquals(event.getInt("unsignedInt"), Integer.MIN_VALUE); + Asserts.assertEquals(event.getLong("unsignedInt"), Integer.toUnsignedLong(Integer.MIN_VALUE)); + + // Unsigned long, nothing should happen + Asserts.assertEquals(event.getLong("unsignedLong"), Long.MIN_VALUE); + + // Unsigned float, nothing should happen + Asserts.assertEquals(event.getFloat("unsignedFloat"), Float.MIN_VALUE); + + // Unsigned double, nothing should happen + Asserts.assertEquals(event.getDouble("unsignedDouble"), Double.MIN_VALUE); + } + + private static void testTimeUnits(RecordedObject event) { + Asserts.assertEquals(event.getDuration("durationMicros"), DURATION_VALUE); + Asserts.assertEquals(event.getDuration("durationMillis"), DURATION_VALUE); + Asserts.assertEquals(event.getDuration("durationSeconds"), DURATION_VALUE); + Asserts.assertEquals(event.getInstant("instantMillis").toEpochMilli(), 1000L); + if (!event.getInstant("instantTicks").isBefore(INSTANT_VALUE)) { + throw new AssertionError("Expected start time of JVM to before call to Instant.now()"); + } + } + + private static void testNestedNames(RecordedObject event) { + RecordedThread t = event.getValue("threadField"); + + // Nested with getValue + try { + event.getValue("nullField.javaName"); + throw new AssertionError("Expected NullPointerException"); + } catch (NullPointerException npe) { + // OK, expected; + } + try { + event.getValue("nullField.does.not.exist"); + throw new AssertionError("Expected IllegalArgumentException"); + } catch (IllegalArgumentException iae) { + // OK, expected; + } + + // Nested getLong + try { + event.getLong("nullField.javaName"); + throw new AssertionError("Expected NullPointerException"); + } catch (NullPointerException npe) { + // OK, expected; + } + try { + event.getLong("nullField.does.not.exist"); + throw new AssertionError("Expected IllegalArgumentException"); + } catch (IllegalArgumentException npe) { + // OK, expected; + } + if (t.getOSThreadId() != event.getLong("threadField.osThreadId")) { + throw new AssertionError("Incorrect result from nested long value"); + } + + // Nested getString + try { + event.getString("nullField.osThreadId"); + throw new AssertionError("Expected IllegalArgumentException"); + } catch (IllegalArgumentException npe) { + // OK, expected; + } + try { + event.getLong("nullField.does.not.exist"); + throw new AssertionError("Expected IllegalArgumentException"); + } catch (IllegalArgumentException npe) { + // OK, expected; + } + if (!t.getJavaName().equals(event.getString("threadField.javaName"))) { + throw new AssertionError("Incorrect result from nested long value"); + } + + // Nested getClass + try { + event.getClass("nullField.osThreadId"); + throw new AssertionError("Expected IllegalArgumentException"); + } catch (IllegalArgumentException npe) { + // OK, expected; + } + try { + event.getClass("nullField.does.not.exist"); + throw new AssertionError("Expected IllegalArgumentException"); + } catch (IllegalArgumentException npe) { + // OK, expected; + } + + // Nested getThread + try { + event.getThread("nullField2.name"); + throw new AssertionError("Expected IllegalArgumentException"); + } catch (IllegalArgumentException npe) { + // OK, expected; + } + try { + event.getThread("nullField2.does.not.exist"); + throw new AssertionError("Expected IllegalArgumentException"); + } catch (IllegalArgumentException npe) { + // OK, expected; + } + } + + private static void testGetBoolean(RecordedObject e) { + assertGetter(x -> e.getBoolean(x), BOOLEAN_VALUE, "boolean"); + } + + private static void testGetByte(RecordedObject e) { + assertGetter(x -> e.getByte(x), (byte) VALUE, "byte"); + } + + private static void testGetChar(RecordedObject e) { + assertGetter(x -> e.getChar(x), (char) VALUE, "char"); + } + + private static void testGetShort(RecordedObject e) { + assertGetter(x -> e.getShort(x), (short) VALUE, "byte", "short"); + } + + private static void testGetInt(RecordedObject e) { + assertGetter(x -> e.getInt(x), (int) VALUE, "byte", "char", "short", "int"); + } + + private static void testGetLong(RecordedObject e) { + assertGetter(x -> e.getLong(x), (long) VALUE, "byte", "char", "short", "int", "long"); + } + + private static void testGetFloat(RecordedObject e) { + assertGetter(x -> e.getFloat(x), (float) VALUE, "byte", "char", "short", "int", "long", "float"); + } + + private static void testGetDouble(RecordedObject e) { + assertGetter(x -> e.getDouble(x), (double) VALUE, "byte", "char", "short", "int", "long", "float", "double"); + } + + private static void testGetString(RecordedObject e) { + assertGetter(x -> e.getString(x), STRING_VALUE, "string"); + } + + private static void testGetInstant(RecordedObject e) { + assertGetter(x -> e.getInstant(x), Instant.ofEpochMilli(INSTANT_VALUE.toEpochMilli()), "instant"); + } + + private static void testGetDuration(RecordedObject e) { + assertGetter(x -> e.getDuration(x), DURATION_VALUE, "duration"); + } + + private static void testGetThread(RecordedObject e) { + RecordedThread thread = e.getValue("threadField"); + if (!thread.getJavaName().equals(THREAD_VALUE.getName())) { + throw new AssertionError("Expected thread to have name " + THREAD_VALUE.getName()); + } + assertGetter(x -> { + // OK to access nullField if it is correct type + // Chose a second null field with class type + if ("nullField".equals(x)) { + return e.getThread("nullField2"); + } else { + return e.getThread(x); + } + + }, thread, "thread"); + } + + private static void testGetClass(RecordedObject e) { + RecordedClass clazz = e.getValue("classField"); + if (!clazz.getName().equals(CLASS_VALUE.getName())) { + throw new AssertionError("Expected class to have name " + CLASS_VALUE.getName()); + } + assertGetter(x -> e.getClass(x), clazz, "class"); + } + + private static void assertGetter(Function f, T expectedValue, String... validTypes) { + Set valids = new HashSet(Arrays.asList(validTypes)); + Set invalids = new HashSet(ALL); + invalids.removeAll(valids); + for (String valid : valids) { + T result = f.apply(valid + "Field"); + if (!expectedValue.equals(result)) { + throw new AssertionError("Incorrect return value " + result + ". Expected " + expectedValue); + } + } + for (String invalid : invalids) { + try { + f.apply(invalid + "Field"); + } catch (IllegalArgumentException iae) { + // OK, as expected + } catch (Exception e) { + throw new AssertionError("Unexpected exception for invalid field " + invalid + ". " + e.getClass().getName() + " : " + e.getMessage(), e); + } + } + String[] illegals = { "missingField", "nullField.javaName.does.not.exist", "nullField" }; + for (String illegal : illegals) { + try { + f.apply(illegal); + throw new AssertionError("Expected IllegalArgumentException when accessing " + illegal); + } catch (IllegalArgumentException iae) { + // OK, as expected + } catch (Exception e) { + throw new AssertionError("Expected IllegalArgumentException. Got " + e.getClass().getName() + " : " + e.getMessage(), e); + } + } + try { + f.apply(null); + throw new AssertionError("Expected NullpointerException exception when passing in null value"); + } catch (NullPointerException iae) { + // OK, as expected + } catch (Exception e) { + throw new AssertionError("Expected NullpointerException. Got " + e.getClass().getName() + " : " + e.getMessage(), e); + } + } + + private static RecordedObject makeRecordedObject() throws IOException { + Recording r = new Recording(); + r.start(); + EventWithValues t = new EventWithValues(); + t.commit(); + r.stop(); + List events = Events.fromRecording(r); + Events.hasEvents(events); + return events.get(0); + } + + private static Set createAll() { + Set set = new HashSet<>(); + set.add("boolean"); + set.add("byte"); + set.add("char"); + set.add("short"); + set.add("int"); + set.add("long"); + set.add("float"); + set.add("double"); + set.add("string"); + set.add("class"); + set.add("thread"); + set.add("instant"); + set.add("duration"); + return set; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestRecordedThreadGroupParent.java 2019-02-08 18:32:46.465800339 +0300 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2013, 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.api.consumer; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThreadGroup; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEvent; + +/** + * @test + * @summary Tests getParent method in RecordedThreadGroup + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestRecordedThreadGroupParent + */ +public class TestRecordedThreadGroupParent { + + public static void main(String[] args) throws Exception { + ThreadGroup beforeStartGroup = new ThreadGroup(new ThreadGroup(new ThreadGroup("Grandfather"), "Father"), "Son"); + Thread beforeThread = new Thread(beforeStartGroup, () -> new SimpleEvent().commit(), "Before Recording Start"); + + try (Recording r = new Recording()) { + r.enable(SimpleEvent.class).withoutStackTrace(); + r.start(); + beforeThread.start(); + ThreadGroup afterStartGroup = new ThreadGroup(new ThreadGroup(new ThreadGroup("Grandmother"), "Mother"), "Daughter"); + Thread afterThread = new Thread(afterStartGroup, () -> new SimpleEvent().commit(), "After Recording Start"); + afterThread.start(); + + beforeThread.join(); + afterThread.join(); + + r.stop(); + + List events = Events.fromRecording(r); + Events.hasEvents(events); + for (RecordedEvent e : events) { + System.out.println(e); + switch (e.getThread().getJavaName()) { + case "Before Recording Start": + assetrThreadGroupParents(beforeStartGroup, e.getThread().getThreadGroup()); + break; + case "After Recording Start": + assetrThreadGroupParents(afterStartGroup, e.getThread().getThreadGroup()); + break; + default: + Asserts.fail("Unexpected thread found " + e.getThread().getJavaName()); + } + } + } + } + + private static void assetrThreadGroupParents(ThreadGroup realGroup, RecordedThreadGroup recordedGroup) { + if (recordedGroup == null && realGroup == null) { + return; // root + } + Asserts.assertNotNull(recordedGroup, "Parent thread group should not be null"); + Asserts.assertEquals(realGroup.getName(), recordedGroup.getName(), "Parent thread group names don't match"); + assetrThreadGroupParents(realGroup.getParent(), recordedGroup.getParent()); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestRecordingFile.java 2019-02-08 18:32:46.605795447 +0300 @@ -0,0 +1,408 @@ +/* + * Copyright (c) 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.api.consumer; + +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.StringJoiner; + + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Name; +import jdk.jfr.Recording; +import jdk.jfr.Registered; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; + +/** + * @test + * @summary Verifies that all methods in RecordingFIle are working + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestRecordingFile + */ +public class TestRecordingFile { + + static class TestEvent1 extends Event { + } + + static class TestEvent2 extends Event { + } + + static class TestEvent3 extends Event { + } + + private static String TEST_CLASS_BASE = "TestRecordingFile$TestEvent"; + private final static int EVENT_COUNT = 3; + private final static int HEADER_SIZE = 68; + private final static long METADATA_OFFSET = 24; + + public static void main(String[] args) throws Throwable { + + // create some recording data + Recording r = new Recording(); + r.enable(TestEvent1.class).withoutStackTrace(); + r.enable(TestEvent2.class).withoutStackTrace(); + r.enable(TestEvent3.class).withoutStackTrace(); + r.start(); + TestEvent1 t1 = new TestEvent1(); + t1.commit(); + TestEvent2 t2 = new TestEvent2(); + t2.commit(); + TestEvent3 t3 = new TestEvent3(); + t3.commit(); + r.stop(); + Path valid = Utils.createTempFile("three-event-recording", ".jfr"); + r.dump(valid); + r.close(); + + Path brokenWithZeros = createBrokenWIthZeros(valid); + Path brokenMetadata = createBrokenMetadata(valid); + // prepare event sets + testNewRecordingFile(valid, brokenWithZeros); + testIterate(valid, brokenWithZeros); + testReadAllEvents(valid, brokenWithZeros); + testReadEventTypes(valid, brokenMetadata); + testClose(valid); + testReadEventTypesMultiChunk(); + testReadEventTypeWithUnregistration(false, false); + testReadEventTypeWithUnregistration(false, true); + testReadEventTypeWithUnregistration(true, false); + testReadEventTypeWithUnregistration(true, true); + } + + private static void testReadEventTypeWithUnregistration(boolean disk, boolean background) throws Exception { + FlightRecorder.register(Event1.class); + FlightRecorder.register(Event2.class); + FlightRecorder.register(Event3.class); + Recording backgrundRecording = new Recording(); + if (disk) { + backgrundRecording.setToDisk(disk); + } + if (background) { + backgrundRecording.start(); + } + recordAndVerify(disk, background,new int[] {1,2, 3}, new int[] {}); + FlightRecorder.unregister(Event2.class); + recordAndVerify(disk, background, new int[] {1, 3}, new int[] {2}); + FlightRecorder.unregister(Event1.class); + FlightRecorder.register(Event2.class); + recordAndVerify(disk,background, new int[] {2, 3}, new int[] {1}); + FlightRecorder.unregister(Event3.class); + FlightRecorder.register(Event3.class); + FlightRecorder.unregister(Event2.class); + FlightRecorder.unregister(Event3.class); + FlightRecorder.register(Event1.class); + FlightRecorder.unregister(Event1.class); + FlightRecorder.register(Event1.class); + FlightRecorder.register(Event2.class); + recordAndVerify(disk, background,new int[] {1, 2}, new int[] {3}); + if (background) { + backgrundRecording.close(); + } + } + + private static void recordAndVerify(boolean disk, boolean background, int[] shouldExist, int[] shouldNotExist) throws Exception { + StringJoiner sb = new StringJoiner("-"); + for (int i = 0; i types = f.readEventTypes(); + for (int i = 0; i< shouldExist.length; i++) { + assertHasEventType(types, "Event" + shouldExist[i]); + } + for (int i = 0; i< shouldNotExist.length; i++) { + assertMissingEventType(types, "Event" + shouldNotExist[i]); + } + } + } + System.out.println(); + System.out.println(); + } + + @Registered(false) + @Name("Event1") + private static class Event1 extends Event { + } + @Registered(false) + @Name("Event2") + private static class Event2 extends Event { + } + @Registered(false) + @Name("Event3") + private static class Event3 extends Event { + } + + private static void testReadEventTypesMultiChunk() throws Exception { + + Path twoEventTypes = Utils.createTempFile("two-event-types", ".jfr"); + Path threeEventTypes = Utils.createTempFile("three-event-types", ".jfr"); + try (Recording r1 = new Recording()) { + r1.start(); + FlightRecorder.register(Event1.class); + try (Recording r2 = new Recording()) { + r2.start(); + FlightRecorder.register(Event2.class); + + // Ensure that metadata are written twice. + try (Recording rotator = new Recording()) { + rotator.start(); + rotator.stop(); + } + r2.stop(); + r2.dump(twoEventTypes);; + } + FlightRecorder.register(Event3.class); + r1.stop(); + r1.dump(threeEventTypes);; + } + try (RecordingFile f = new RecordingFile(twoEventTypes)) { + List types = f.readEventTypes(); + assertUniqueEventTypes(types); + assertHasEventType(types, "Event1"); + assertHasEventType(types, "Event2"); + assertMissingEventType(types, "Event3"); + } + try (RecordingFile f = new RecordingFile(twoEventTypes)) { + List types = f.readEventTypes(); + assertUniqueEventTypes(types); + assertHasEventType(types, "Event1"); + assertHasEventType(types, "Event2"); + assertMissingEventType(types, "Event3"); + } + + } + + private static void assertMissingEventType(List types,String name) throws Exception { + EventType type = findEventType(types, name); + if (type != null) { + throw new Exception("Found unexpected event type " + name); + } + } + + private static void assertHasEventType(List types,String name) throws Exception { + EventType type = findEventType(types, name); + if (type == null) { + throw new Exception("Missing event type " + name); + } + } + + private static EventType findEventType(List types, String name) { + for (EventType t : types) { + if (t.getName().equals(name)) { + return t; + } + } + return null; + } + + private static void assertUniqueEventTypes(List types) { + HashSet ids = new HashSet<>(); + for (EventType type : types) { + ids.add(type.getId()); + } + Asserts.assertEquals(types.size(), ids.size(), "Event types repeated. " + types); + } + + private static Path createBrokenWIthZeros(Path valid) throws Exception { + try { + Path broken = Utils.createTempFile("broken-events", ".jfr"); + Files.delete(broken); + Files.copy(valid, broken); + RandomAccessFile raf = new RandomAccessFile(broken.toFile(), "rw"); + raf.seek(HEADER_SIZE); + int size = (int) Files.size(broken); + byte[] ones = new byte[size - HEADER_SIZE]; + for (int i = 0; i < ones.length; i++) { + ones[i] = (byte) 0xFF; + } + raf.write(ones, 0, ones.length); + raf.close(); + return broken; + } catch (IOException ioe) { + throw new Exception("Could not produce a broken file " + valid, ioe); + } + } + + private static Path createBrokenMetadata(Path valid) throws Exception { + try { + Path broken = Utils.createTempFile("broken-metadata", ".jfr"); + Files.delete(broken); + Files.copy(valid, broken); + RandomAccessFile raf = new RandomAccessFile(broken.toFile(), "rw"); + raf.seek(METADATA_OFFSET); + long metadataOffset = raf.readLong(); + raf.seek(metadataOffset); + raf.writeLong(Long.MAX_VALUE); + raf.writeLong(Long.MAX_VALUE); + raf.close(); + return broken; + } catch (IOException ioe) { + throw new Exception("Could not produce a broken EventSet from file " + valid, ioe); + } + } + + private static void testReadEventTypes(Path valid, Path broken) throws Exception { + try (RecordingFile validFile = new RecordingFile(valid)) { + List types = validFile.readEventTypes(); + if (types.size() < EVENT_COUNT) { + throw new Exception("Expected at least " + EVENT_COUNT + " event type but got " + types.toString()); + } + int counter = 0; + for (Class testClass : Arrays.asList(TestEvent1.class, TestEvent2.class, TestEvent3.class)) { + for (EventType t : types) { + if (t.getName().equals(testClass.getName())) { + counter++; + } + } + } + if (counter != 3) { + throw new Exception("Returned incorrect event types"); + } + } + try (RecordingFile brokenFile = new RecordingFile(broken)) { + brokenFile.readEventTypes(); + throw new Exception("Expected IOException when getting Event Types from broken recording"); + } catch (IOException ise) { + // OK + } + } + + private static void testNewRecordingFile(Path valid, Path broken) throws Exception { + try (RecordingFile r = new RecordingFile(null)) { + throw new Exception("Expected NullPointerException"); + } catch (NullPointerException npe) { + // OK + } + try (RecordingFile r = new RecordingFile(Paths.get("hjhjsdfhkjshdfkj.jfr"))) { + throw new Exception("Expected FileNotFoundException"); + } catch (FileNotFoundException npe) { + // OK + } + Path testFile = Utils.createTempFile("test-empty-file", ".jfr"); + try (RecordingFile r = new RecordingFile(testFile)) { + throw new Exception("Expected IOException if file is empty"); + } catch (IOException e) { + // OK + } + FileWriter fr = new FileWriter(testFile.toFile()); + fr.write("whatever"); + fr.close(); + try (RecordingFile r = new RecordingFile(Paths.get("hjhjsdfhkjshdfkj.jfr"))) { + throw new Exception("Expected IOException if magic is incorrect"); + } catch (IOException e) { + // OK + } + + try (RecordingFile r = new RecordingFile(valid)) { + } + } + + private static void testClose(Path valid) throws Exception { + RecordingFile f = new RecordingFile(valid); + f.close(); + + try { + f.readEvent(); + throw new Exception("Should not be able to read event from closed recording"); + } catch (IOException e) { + if (!e.getMessage().equals("Stream Closed")) { + throw new Exception("Expected 'Stream Closed' in exception message for a closed stream. Got '" + e.getMessage() +"'."); + } + // OK + } + try { + f.readEventTypes(); + throw new Exception("Should not be able to read event from closed recording"); + } catch (IOException e) { + if (!e.getMessage().equals("Stream Closed")) { + throw new Exception("Expected 'Stream Closed' in exception message for a closed stream. Got '" + e.getMessage() +"'."); + } + // OK + } + // close twice + f.close(); + } + + private static void testIterate(Path valid, Path broken) throws Exception { + try (RecordingFile validFile = new RecordingFile(valid)) { + for (int i = 0; i < EVENT_COUNT; i++) { + if (!validFile.hasMoreEvents()) { + throw new Exception("Not all events available"); + } + RecordedEvent r = validFile.readEvent(); + if (r == null) { + throw new Exception("Missing event"); + } + if (!r.getEventType().getName().contains(TEST_CLASS_BASE)) { + throw new Exception("Incorrect event in recording file " + r); + } + } + if (validFile.hasMoreEvents()) { + throw new Exception("Should not be more than " + EVENT_COUNT + " in recording"); + } + } + try (RecordingFile brokenFile = new RecordingFile(broken)) { + brokenFile.readEvent(); + throw new Exception("Expected IOException for broken recording"); + } catch (IOException ise) { + // OK + } + } + + private static void testReadAllEvents(Path valid, Path broken) throws Exception { + try { + RecordingFile.readAllEvents(broken); + throw new Exception("Expected IOException when reading all events for broken recording"); + } catch (IOException ioe) { + // OK as expected + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestRecordingFileReadEventEof.java 2019-02-08 18:32:46.745790555 +0300 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 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.api.consumer; + +import java.io.EOFException; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEvent; + +/** + * @test + * @summary Verifies that RecordingFile.readEvent() throws EOF when past the last record + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestRecordingFileReadEventEof + */ +public class TestRecordingFileReadEventEof { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + r.start(); + SimpleEvent t = new SimpleEvent(); + t.commit(); + r.stop(); + RecordingFile file = Events.copyTo(r); + r.close(); + file.readEvent(); + try { + file.readEvent(); + Asserts.fail("Expected EOFException not thrown"); + } catch (EOFException x) { + // OK, as expected + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestRecordingInternals.java 2019-02-08 18:32:46.893785383 +0300 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 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.api.consumer; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEvent; + +/** + * @test + * @summary Tests that chunks are read in order and constant pools from multiple chunks can be read + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestRecordingInternals + */ +public class TestRecordingInternals { + + public static void main(String[] args) throws Throwable { + try (Recording continuous = new Recording()) { + continuous.start(); + for (int i = 0; i < 3; i++) { + // Each new recording will create a new chunk + // with a new set of constant pools, i.e. + // constant pools for threads and thread groups + createProfilingRecording(i); + } + continuous.stop(); + int i = 0; + for (RecordedEvent e : Events.fromRecording(continuous)) { + Integer id = e.getValue("id"); + RecordedThread rt = e.getThread(); + Asserts.assertEquals(id.toString(), rt.getJavaName(), "Thread name should match id"); + Asserts.assertEquals(id.toString(), rt.getThreadGroup().getName(), "Thread group name should match id"); + Asserts.assertEquals(id, Integer.valueOf(i), "Chunks out of order"); + i++; + } + } + } + + private static void createProfilingRecording(int id) throws InterruptedException { + try (Recording r = new Recording()) { + r.start(); + ThreadGroup tg = new ThreadGroup(String.valueOf(id)); + Thread t = new Thread(tg, () -> { + SimpleEvent event = new SimpleEvent(); + event.id = id; + event.commit(); + }, String.valueOf(id)); + t.start(); + t.join(); + r.stop(); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestSingleRecordedEvent.java 2019-02-08 18:32:47.033780491 +0300 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 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.api.consumer; + +import java.util.List; + +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Verifies that a single JFR event is recorded as expected + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestSingleRecordedEvent + */ +public class TestSingleRecordedEvent { + + @Description("MyDescription") + private static class MyEvent extends Event { + } + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + r.start(); + // Commit a single event to the recording + MyEvent event = new MyEvent(); + event.commit(); + r.stop(); + List events = Events.fromRecording(r); + Events.hasEvents(events); + + // Should be 1 event only + Asserts.assertEquals(events.size(), 1); + + RecordedEvent recordedEvent = events.get(0); + + // Event description should be the same + Asserts.assertEquals(recordedEvent.getEventType().getDescription(), "MyDescription"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestToString.java 2019-02-08 18:32:47.181775319 +0300 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 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.api.consumer; + +import java.util.HashMap; +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Sanity checks that RecordedEvent#toString returns something valid + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestToString + */ +public class TestToString { + + public static void main(String[] args) throws Throwable { + + try (Recording recording = new Recording()) { + recording.start(); + HelloWorldEvent event = new HelloWorldEvent(); + event.message = "hello, world"; + event.integer = 4711; + event.floatValue = 9898; + event.doubleValue = 313; + event.clazz = HashMap.class; + event.characater = '&'; + event.byteValue = (byte) 123; + event.longValue = 1234567890L; + event.shortValue = 64; + event.booleanValue = false; + event.commit(); + recording.stop(); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + RecordedEvent e = events.get(0); + String toString = e.toString(); + System.out.println(toString); + Asserts.assertTrue(toString.contains("hello, world"), "Missing String field value in RecordedEvent#toSting()"); + Asserts.assertTrue(toString.contains("4711"), "Missing integer fields value in RecordedEvent#toSting()"); + Asserts.assertTrue(toString.contains("313"), "Missing double value in RecordedEvent#toSting()"); + Asserts.assertTrue(toString.contains("HashMap"), "Missing class value in RecordedEvent#toSting()"); + Asserts.assertTrue(toString.contains("1234567890"), "Missing long value in RecordedEvent#toSting()"); + Asserts.assertTrue(toString.contains("&"), "Missing char value in RecordedEvent#toSting()"); + Asserts.assertTrue(toString.contains("123"), "Missing byte value in RecordedEvent#toString()"); + Asserts.assertTrue(toString.contains("64"), "Missing short value in RecordedEvent#toString()"); + Asserts.assertTrue(toString.contains("false"), "Missing boolean value in RecordedEvent#toString()"); + Asserts.assertTrue(toString.contains("HelloWorldEvent"), "Missing class name in RecordedEvent#toString()"); + } + } + + static class HelloWorldEvent extends Event { + public boolean booleanValue; + public long longValue; + public int shortValue; + public byte byteValue; + public int doubleValue; + public char characater; + public Thread mainThread; + public Class clazz; + public int floatValue; + public int integer; + public String message; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/consumer/TestValueDescriptorRecorded.java 2019-02-08 18:32:47.329770148 +0300 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 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.api.consumer; + +import java.util.List; + +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.Recording; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + + +/** + * @test + * @summary Verifies that the recorded value descriptors are correct + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.consumer.TestValueDescriptorRecorded + */ +public class TestValueDescriptorRecorded { + + private static class MyEvent extends Event { + + @Label("myLabel") + @Description("myDescription") + int myValue; + } + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + r.enable(MyEvent.class).withoutStackTrace(); + r.start(); + MyEvent event = new MyEvent(); + event.commit(); + r.stop(); + + List events = Events.fromRecording(r); + Events.hasEvents(events); + RecordedEvent recordedEvent = events.get(0); + + for (ValueDescriptor desc : recordedEvent.getFields()) { + if ("myValue".equals(desc.getName())) { + Asserts.assertEquals(desc.getLabel(), "myLabel"); + Asserts.assertEquals(desc.getDescription(), "myDescription"); + Asserts.assertEquals(desc.getTypeName(), int.class.getName()); + Asserts.assertFalse(desc.isArray()); + Asserts.assertNull(desc.getContentType()); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TEST.properties 2019-02-08 18:32:47.473765116 +0300 @@ -0,0 +1,2 @@ +modules = java.management + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TestAbstractEvent.java 2019-02-08 18:32:47.617760084 +0300 @@ -0,0 +1,116 @@ +/* + * Copyright (c) 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.api.event; + +import java.io.IOException; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Experimental; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Tests that abstract events are not part of metadata + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.TestAbstractEvent + */ +public class TestAbstractEvent { + + // Should not be included in metadata + @Experimental + static abstract class BaseEvent extends Event { + } + + // Should be included + static class ConcreteEvent extends BaseEvent { + } + + public static void main(String... args) throws IOException { + try { + EventType.getEventType(BaseEvent.class); + Asserts.fail("Should not accept abstract event classes"); + } catch (IllegalArgumentException iae) { + // OK, as expected + } + + try { + FlightRecorder.register(BaseEvent.class); + Asserts.fail("Should not accept registering abstract event classes"); + } catch (IllegalArgumentException iae) { + // OK, as expected + } + + try { + FlightRecorder.unregister(BaseEvent.class); + Asserts.fail("Should not accept unregistering abstract event classes"); + } catch (IllegalArgumentException iae) { + // OK, as expected + } + + + Recording r = new Recording(); + try { + r.enable(BaseEvent.class); + Asserts.fail("Should not accept abstract event classes"); + } catch (IllegalArgumentException iae) { + // OK, as expected + } + r.start(); + + ConcreteEvent event = new ConcreteEvent(); + event.commit(); + r.stop(); + RecordingFile rf = Events.copyTo(r); + RecordedEvent re = rf.readEvent(); + if (!re.getEventType().getName().equals(ConcreteEvent.class.getName())) { + Asserts.fail("Expected " + ConcreteEvent.class.getName() + " event to be part of recording. Found " + re.getEventType().getName()); + } + if (rf.hasMoreEvents()) { + Asserts.fail("Expected only one event"); + } + rf.close(); + EventType concreteEventType = null; + for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) { + if (type.getName().equals(BaseEvent.class.getName())) { + Asserts.fail("Abstract events should not be part of metadata"); + } + if (type.getName().equals(ConcreteEvent.class.getName())) { + concreteEventType = type; + } + } + Asserts.assertTrue(concreteEventType != null, "Could not find " + ConcreteEvent.class.getName() + " in metadata"); + Experimental exp = concreteEventType.getAnnotation(Experimental.class); + Asserts.assertTrue(exp != null, "Could not find inherited annotation" + Experimental.class.getName() + " from abstract event class"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TestBeginEnd.java 2019-02-08 18:32:47.761755053 +0300 @@ -0,0 +1,114 @@ +/* + * 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.api.event; + +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEvent; + +/** + * @test + * @summary Test for RecordedEvent.getDuration() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.TestBeginEnd + */ +public class TestBeginEnd { + + public static void main(String[] args) throws Exception { + + Recording r = new Recording(); + r.enable(SimpleEvent.class); + r.start(); + + // Test enabled - single commit + SimpleEvent e1 = new SimpleEvent(); + e1.id = 1; // should be included + e1.commit(); + + // Test enabled - begin - commit + SimpleEvent e2 = new SimpleEvent(); + e2.begin(); + e2.id = 2; // should be included + e2.commit(); + + // Test enabled - begin - end - commit + SimpleEvent e3 = new SimpleEvent(); + e3.begin(); + e3.id = 3; // should be included + e3.end(); + e3.commit(); + + // Test enabled - end - commit + SimpleEvent e4 = new SimpleEvent(); + e4.id = 4; // should be included + e4.end(); + e4.commit(); + + // Test half enabled - begin - commit + r.disable(SimpleEvent.class); + SimpleEvent e5 = new SimpleEvent(); + e5.begin(); + r.enable(SimpleEvent.class); + e5.id = 5; // should be included + e5.commit(); + + // Test half enabled - begin - end - commit + r.disable(SimpleEvent.class); + SimpleEvent r6 = new SimpleEvent(); + r6.begin(); + r.enable(SimpleEvent.class); + r6.id = 6; // should be included + r6.end(); + r6.commit(); + + // Test half enabled - begin - commit with high threshold + r.disable(SimpleEvent.class); + SimpleEvent r7 = new SimpleEvent(); + r7.begin(); + r.enable(SimpleEvent.class).withThreshold(Duration.ofDays(1)); + r7.id = 7; // should not be included + r7.commit(); + r.stop(); + + List recordedEvents = Events.fromRecording(r); + for (RecordedEvent re : recordedEvents) { + Integer id = re.getValue("id"); + System.out.println("Found if " + id); + if (id < 1 || id > 6) { + throw new Exception("Unexpected id found " + id); + } + } + if (recordedEvents.size() != 6) { + throw new Exception("Expected 6 events"); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TestClinitRegistration.java 2019-02-08 18:32:47.905750022 +0300 @@ -0,0 +1,206 @@ +/* + * Copyright (c) 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.api.event; + +import java.io.IOException; +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.Registered; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test enable/disable event and verify recording has expected events. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.TestClinitRegistration + */ + +public class TestClinitRegistration { + + public static void main(String[] args) throws Exception { + // Test basic registration with or without auto registration + assertClinitRegister(AutoRegisteredEvent.class, true, false); + assertClinitRegister(NotAutoRegisterededEvent.class, false, false); + assertClinitRegister(AutoRegisteredUserClinit.class, true, true); + assertClinitRegister(NotAutoRegisteredUserClinit.class, false, true); + + // Test complex + assertClinitRegister(ComplexClInit.class, true, true); + + // Test hierarchy + assertClinitRegister(DerivedClinit.class, true, true); + if (!isClinitExecuted(Base.class)) { + Asserts.fail("Expected of base class to be executed"); + } + + // Test committed event in + Recording r = new Recording(); + r.start(); + r.enable(EventInClinit.class); + triggerClinit(EventInClinit.class); + r.stop(); + hasEvent(r, EventInClinit.class.getName()); + } + + private static void assertClinitRegister(Class eventClass, boolean shouldExist, boolean setsProperty) throws ClassNotFoundException { + String className = eventClass.getName(); + triggerClinit(eventClass); + boolean hasEventType = hasEventType(className); + boolean hasProperty = Boolean.getBoolean(className); + if (hasEventType && !shouldExist) { + Asserts.fail("Event class " + className + " should not be registered"); + } + if (!hasEventType && shouldExist) { + Asserts.fail("Event class " + className + " is not registered"); + } + if (setsProperty && !hasProperty) { + Asserts.fail("Expected clinit to be executed"); + } + if (!setsProperty && hasProperty) { + Asserts.fail("Property in clinit should not been set. Test bug?"); + } + } + + private static boolean hasEventType(String name) { + for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) { + if (type.getName().equals(name)) { + return true; + } + } + return false; + } + + private static void triggerClinit(Class clazz) throws ClassNotFoundException { + Class.forName(clazz.getName(), true, clazz.getClassLoader()); + } + + private static void setClinitExecuted(Class eventClass) { + System.setProperty(eventClass.getName(), "true"); + } + + private static boolean isClinitExecuted(Class eventClass) { + return "true".equals(System.getProperty(eventClass.getName(), "true")); + } + + static class AutoRegisteredEvent extends Event { + } + + @Registered(false) + static class NotAutoRegisterededEvent extends Event { + } + + static class AutoRegisteredUserClinit extends Event { + static { + setClinitExecuted(AutoRegisteredUserClinit.class); + } + } + + @Registered(false) + static class NotAutoRegisteredUserClinit extends Event { + static { + setClinitExecuted(NotAutoRegisteredUserClinit.class); + } + } + + static class Base extends Event { + static { + setClinitExecuted(Base.class); + } + } + + static class DerivedClinit extends Base { + static { + setClinitExecuted(DerivedClinit.class); + } + + @Deprecated + void myVoidMethod() { + } + } + + static class ComplexClInit extends Event { + static { + setClinitExecuted(ComplexClInit.class); + } + public static final long l = Long.parseLong("7"); + public static final int i = Integer.parseInt("7"); + public static final short s = Short.parseShort("7"); + public static final double d = Double.parseDouble("7"); + public static final float f = Float.parseFloat("7"); + public static final boolean b = Boolean.parseBoolean("true"); + public static final char c = (char) Integer.parseInt("48"); + public static final String text = "ioio".substring(2); + public static final int[] primitivArray = new int[] { 7, 4 }; + public static final Class Object = ComplexClInit.class; + + static { + String text = ""; + long l = 56; + long i = 56; + if (i != l) { + throw new RuntimeException("unexpected result from comparison"); + } + if (!isClinitExecuted(ComplexClInit.class)) { + throw new RuntimeException("Expected clinit flag to be set" + text); + } + } + + static { + try { + throw new IllegalStateException("Exception"); + } catch (IllegalStateException ise) { + // as expected + } + } + } + + static class EventInClinit extends Event { + static { + EventInClinit eventInClinit = new EventInClinit(); + eventInClinit.commit(); + } + } + + public static void hasEvent(Recording r, String name) throws IOException { + List events = Events.fromRecording(r); + Events.hasEvents(events); + + for (RecordedEvent event : events) { + if (event.getEventType().getName().equals(name)) { + return; + } + } + Asserts.fail("Missing event " + name + " in recording " + events.toString()); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TestClonedEvent.java 2019-02-08 18:32:48.049744991 +0300 @@ -0,0 +1,96 @@ +/* + * Copyright (c) 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.api.event; + +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.Registered; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Tests that a cloned event can be successfully committed. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.TestClonedEvent + */ + +public class TestClonedEvent { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + r.enable(MyEvent.class); + + r.start(); + + MyEvent event = new MyEvent(); + + MyEvent event2 = (MyEvent)event.clone(); + + FlightRecorder.register(MyEvent.class); + event.commit(); + event2.commit(); + + r.stop(); + + List events = Events.fromRecording(r); + Asserts.assertEquals(2, events.size()); + + r.close(); + + FlightRecorder.unregister(MyEvent.class); + + Recording r2 = new Recording(); + r2.enable(MyEvent.class); + + r2.start(); + event.commit(); + event2.commit(); + + r2.stop(); + + events = Events.fromRecording(r2); + Asserts.assertEquals(0, events.size()); + + r2.close(); + } + + @Registered(false) + private static class MyEvent extends Event implements Cloneable { + + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TestEnableDisable.java 2019-02-08 18:32:48.193739959 +0300 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 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.api.event; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test enable/disable event and verify recording has expected events. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.TestEnableDisable + */ + +public class TestEnableDisable { + + public static void main(String[] args) throws Exception { + List expectedEvents = new ArrayList<>(); + Recording r = new Recording(); + + r.start(); + createEvent(expectedEvents, true); // id=0 Custom event classes are enabled by default. + + r.disable(MyEvent.class); + createEvent(expectedEvents, false); // id=1 + r.enable(MyEvent.class); + createEvent(expectedEvents, true); // id=2 + + // enable/disable by event setting name + String eventSettingName = String.valueOf(EventType.getEventType(MyEvent.class).getId()); + System.out.println("eventSettingName=" + eventSettingName); + + r.disable(eventSettingName); + createEvent(expectedEvents, false); // id=3 + r.enable(eventSettingName); + createEvent(expectedEvents, true); + + r.stop(); + createEvent(expectedEvents, false); + + Iterator expectedIterator = expectedEvents.iterator(); + for (RecordedEvent event : Events.fromRecording(r)) { + System.out.println("event.id=" + Events.assertField(event, "id").getValue()); + Asserts.assertTrue(expectedIterator.hasNext(), "Found more events than expected"); + Events.assertField(event, "id").equal(expectedIterator.next().id); + } + Asserts.assertFalse(expectedIterator.hasNext(), "Did not find all expected events."); + + r.close(); + } + + private static int eventId = 0; + private static void createEvent(List expectedEvents, boolean isExpected) { + MyEvent event = new MyEvent(); + event.begin(); + event.id = eventId; + event.commit(); + + if (isExpected) { + expectedEvents.add(event); + } + eventId++; + } + + private static class MyEvent extends Event { + private int id; + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TestEventFactory.java 2019-02-08 18:32:48.345734649 +0300 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 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.api.event; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Event; +import jdk.jfr.EventFactory; +import jdk.jfr.EventType; +import jdk.jfr.Label; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; + + +/** + * @test + * @summary EventFactory simple test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.TestEventFactory + */ +public class TestEventFactory { + + public static void main(String[] args) throws Exception { + List vds = new ArrayList<>(); + vds.add(new ValueDescriptor(String.class, "Message")); + vds.add(new ValueDescriptor(String.class, "message")); + + List annos = new ArrayList<>(); + annos.add(new AnnotationElement(Label.class, "Hello World")); + + EventFactory f = EventFactory.create(annos, vds); + EventType type = f.getEventType(); + Asserts.assertNotNull(type); + + Event e = f.newEvent(); + e.set(0, "test Message"); + e.set(1, "test message"); + + try { + e.set(100, "should fail"); + Asserts.fail("The expected exception IndexOutOfBoundsException have not been thrown"); + } catch(IndexOutOfBoundsException expected) { + // OK, as expected + } + + try { + e.set(-200, "should fail again"); + Asserts.fail("The expected exception IndexOutOfBoundsException have not been thrown"); + } catch(IndexOutOfBoundsException expected) { + // OK, as expected + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TestEventFactoryRegisterTwice.java 2019-02-08 18:32:48.489729617 +0300 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 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.api.event; + +import java.util.Collections; + +import jdk.jfr.EventFactory; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.test.lib.Asserts; + + +/** + * @test + * @summary Verifies that EventFactory can register the same event twice + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.TestEventFactoryRegisterTwice + */ +public class TestEventFactoryRegisterTwice { + + public static void main(String[] args) throws Exception { + EventFactory factory = EventFactory.create(Collections.emptyList(), Collections.emptyList()); + + EventType eventType = factory.getEventType(); + Asserts.assertNotNull(eventType); + + // Now, register the event + factory.register(); + + verifyRegistered(eventType); + + // Now, register the event again + factory.register(); + + verifyRegistered(eventType); + } + + private static void verifyRegistered(EventType eventType) { + // Verify the event is registered + boolean found = false; + for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) { + if (eventType.getId() == type.getId()) { + found = true; + } + } + if(!found) { + Asserts.fail("Event not registered"); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TestEventFactoryRegistration.java 2019-02-08 18:32:48.629724726 +0300 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 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.api.event; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.EventFactory; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Registered; +import jdk.test.lib.Asserts; + + +/** + * @test + * @summary EventFactory register/unregister API test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.TestEventFactoryRegistration + */ +public class TestEventFactoryRegistration { + + public static void main(String[] args) throws Exception { + // Create an unregistered event + List annotations = new ArrayList<>(); + annotations.add(new AnnotationElement(Registered.class, false)); + EventFactory factory = EventFactory.create(annotations, Collections.emptyList()); + + try { + factory.getEventType(); + Asserts.fail("Should not be able to get event type from an unregistered event"); + } catch(IllegalStateException ise) { + // OK as expected + } + + // Now, register the event + factory.register(); + EventType eventType = factory.getEventType(); + verifyRegistered(factory.getEventType()); + + + // Now, unregister the event + factory.unregister(); + + verifyUnregistered(eventType); + + // Create a registered event + factory = EventFactory.create(Collections.emptyList(), Collections.emptyList()); + + eventType = factory.getEventType(); + Asserts.assertNotNull(eventType); + + verifyRegistered(eventType); + + } + + private static void verifyUnregistered(EventType eventType) { + // Verify the event is not registered + for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) { + if (eventType.getId() == type.getId()) { + Asserts.fail("Event is not unregistered"); + } + } + } + + private static void verifyRegistered(EventType eventType) { + // Verify the event is registered + boolean found = false; + for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) { + if (eventType.getId() == type.getId()) { + found = true; + } + } + if(!found) { + Asserts.fail("Event not registered"); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TestExtends.java 2019-02-08 18:32:48.777719555 +0300 @@ -0,0 +1,188 @@ +/* + * Copyright (c) 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.api.event; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Recording; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test with event class inheritance + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.TestExtends + */ + +public class TestExtends { + + private static final int DEFAULT_FIELD_COUNT = 4; + + @SuppressWarnings("unused") + private static class GrandpaEvent extends Event { + public int gPublicField = 4; + protected int gProtectedField = 3; + private int gPrivateField = 2; + int gDefaultField = 1; + private int hiddenField = 4711; + } + + @SuppressWarnings("unused") + private static class ParentEvent extends GrandpaEvent { + public int pPublicField = 40; + protected int pProtectedField = 30; + private int pPrivateField = 20; + int pDefaultField = 10; + private boolean hiddenField = true; + } + + @SuppressWarnings("unused") + private static class MeEvent extends ParentEvent { + public int mPublicField = 400; + protected int mProtectedField = 300; + private int mPrivateField = 200; + int mDefaultField = 100; + private String hiddenField = "Hidden"; + } + + public static void main(String[] args) throws Exception { + Recording r = new Recording(); + r.enable(GrandpaEvent.class).withoutStackTrace(); + r.enable(ParentEvent.class).withStackTrace(); + r.enable(MeEvent.class).withoutStackTrace(); + r.start(); + + GrandpaEvent g = new GrandpaEvent(); + g.commit(); + + ParentEvent p = new ParentEvent(); + p.commit(); + + MeEvent m = new MeEvent(); + m.commit(); + + r.stop(); + for (RecordedEvent re : Events.fromRecording(r)) { + System.out.println(re); + } + // Grandpa + EventType grandpaType = EventType.getEventType(GrandpaEvent.class); + verifyField(grandpaType, "gPublicField"); + verifyField(grandpaType, "gProtectedField"); + verifyField(grandpaType, "gPrivateField"); + verifyField(grandpaType, "gDefaultField"); + verifyField(grandpaType, "hiddenField"); + verifyFieldCount(grandpaType, 5); + + // Parent + EventType parentType = EventType.getEventType(ParentEvent.class); + verifyField(parentType, "gPublicField"); + verifyField(parentType, "gProtectedField"); + verifyField(parentType, "gDefaultField"); + verifyField(parentType, "pPublicField"); + verifyField(parentType, "pProtectedField"); + verifyField(parentType, "pPrivateField"); + verifyField(parentType, "pDefaultField"); + verifyField(parentType, "hiddenField"); + verifyFieldCount(parentType, 8); + + // Me + EventType meType = EventType.getEventType(MeEvent.class); + verifyField(meType, "gPublicField"); + verifyField(meType, "gProtectedField"); + verifyField(meType, "gDefaultField"); + verifyField(meType, "pPublicField"); + verifyField(meType, "pProtectedField"); + verifyField(meType, "pDefaultField"); + verifyField(meType, "mPublicField"); + verifyField(meType, "mProtectedField"); + verifyField(meType, "mPrivateField"); + verifyField(meType, "mDefaultField"); + verifyField(meType, "hiddenField"); + verifyFieldCount(meType, 11); + + for (RecordedEvent re : Events.fromRecording(r)) { + System.out.println(re); + } + + RecordedEvent grandpa = findEvent(r, GrandpaEvent.class.getName()); + Asserts.assertEquals(grandpa.getValue("gPublicField"), 4); + Asserts.assertEquals(grandpa.getValue("gProtectedField"), 3); + Asserts.assertEquals(grandpa.getValue("gPrivateField"), 2); + Asserts.assertEquals(grandpa.getValue("gDefaultField"), 1); + Asserts.assertEquals(grandpa.getValue("hiddenField"), 4711); + + RecordedEvent parent = findEvent(r, ParentEvent.class.getName()); + Asserts.assertEquals(parent.getValue("gPublicField"), 4); + Asserts.assertEquals(parent.getValue("gProtectedField"), 3); + Asserts.assertEquals(parent.getValue("gDefaultField"), 1); + Asserts.assertEquals(parent.getValue("pPublicField"), 40); + Asserts.assertEquals(parent.getValue("pProtectedField"), 30); + Asserts.assertEquals(parent.getValue("pPrivateField"), 20); + Asserts.assertEquals(parent.getValue("pDefaultField"), 10); + Asserts.assertEquals(parent.getValue("hiddenField"), true); + + RecordedEvent me = findEvent(r, MeEvent.class.getName()); + Asserts.assertEquals(me.getValue("gPublicField"), 4); + Asserts.assertEquals(me.getValue("gProtectedField"), 3); + Asserts.assertEquals(me.getValue("gDefaultField"), 1); + Asserts.assertEquals(me.getValue("pPublicField"), 40); + Asserts.assertEquals(me.getValue("pProtectedField"), 30); + Asserts.assertEquals(me.getValue("pDefaultField"), 10); + Asserts.assertEquals(me.getValue("mPublicField"), 400); + Asserts.assertEquals(me.getValue("mProtectedField"), 300); + Asserts.assertEquals(me.getValue("mPrivateField"), 200); + Asserts.assertEquals(me.getValue("mDefaultField"), 100); + Asserts.assertEquals(me.getValue("hiddenField"), "Hidden"); + } + + private static RecordedEvent findEvent(Recording r, String name) throws Exception { + for (RecordedEvent re : Events.fromRecording(r)) { + if (re.getEventType().getName().equals(name)) { + return re; + } + } + throw new Exception("Event type hierarchy exist, but missing event " + name + " from recording"); + } + + private static void verifyFieldCount(EventType t, int count) throws Exception { + if (t.getFields().size() != count + DEFAULT_FIELD_COUNT) { + throw new Exception("Incorrect number of fields " + count); + } + } + + private static void verifyField(EventType t, String name) throws Exception { + ValueDescriptor d = t.getField(name); + if (d == null) { + throw new Exception("Missing field " + name + " in event " + t.getName()); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TestGetDuration.java 2019-02-08 18:32:48.925714384 +0300 @@ -0,0 +1,131 @@ +/* + * 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.api.event; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEvent; + +/** + * @test + * @summary Test for RecordedEvent.getDuration() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.TestGetDuration + */ +public class TestGetDuration { + + private static final int DURATIONAL_EVENT_ID = 1; + private static final int INSTANT_EVENT_ID = 2; + + public static void main(String[] args) throws Exception { + verifyCustomEvents(); + verifyNativeEvents(); + } + + private static void verifyCustomEvents() throws Exception { + boolean fastTimeEnabled = CommonHelper.hasFastTimeEnabled(); + System.out.println("Fast time enabled: " + fastTimeEnabled); + Recording r = new Recording(); + r.enable(SimpleEvent.class).withoutStackTrace(); + r.enable(EventNames.CPUTimeStampCounter); // for debugging purposes + r.start(); + + SimpleEvent durational = new SimpleEvent(); + durational.begin(); + durational.id = DURATIONAL_EVENT_ID; + if (!fastTimeEnabled) { + // if we have a low resolution clock sleep until time changes + CommonHelper.waitForSystemCurrentMillisToChange(); + } + durational.end(); + durational.commit(); + + SimpleEvent instant = new SimpleEvent(); + instant.id = INSTANT_EVENT_ID; + instant.commit(); + + r.stop(); + + List recordedEvents = Events.fromRecording(r); + List testEvents = new ArrayList<>(); + for (RecordedEvent e : recordedEvents) { + System.out.println(e); // for debugging time related issues + if (!e.getEventType().getName().equals(EventNames.CPUTimeStampCounter)) { + testEvents.add(e); + } + } + Events.hasEvents(testEvents); + for (RecordedEvent re : testEvents) { + int id = re.getValue("id"); + Asserts.assertEquals(re.getDuration(), Duration.between(re.getStartTime(), re.getEndTime())); + switch (id) { + case DURATIONAL_EVENT_ID: + Asserts.assertTrue(!re.getDuration().isNegative() && !re.getDuration().isZero()); + break; + case INSTANT_EVENT_ID: + Asserts.assertTrue(re.getDuration().isZero()); + break; + } + } + } + + private static void verifyNativeEvents() throws Exception { + Recording r = new Recording(); + r.enable(EventNames.JVMInformation); + r.enable(EventNames.ThreadSleep); + r.start(); + // Should trigger a sleep event even if we + // have a low resolution clock + Thread.sleep(200); + r.stop(); + List recordedEvents = Events.fromRecording(r); + Events.hasEvents(recordedEvents); + for (RecordedEvent re : recordedEvents) { + Asserts.assertEquals(re.getDuration(), Duration.between(re.getStartTime(), re.getEndTime())); + switch (re.getEventType().getName()) { + case EventNames.JVMInformation: + Asserts.assertTrue(re.getDuration().isZero()); + break; + case EventNames.ThreadSleep: + Asserts.assertTrue(!re.getDuration().isNegative() && !re.getDuration().isZero()); + break; + default: + Asserts.fail("Unexpected event: " + re); + break; + } + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TestIsEnabled.java 2019-02-08 18:32:49.069709353 +0300 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 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.api.event; + +import jdk.jfr.EventType; +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.SimpleEvent; + +/** + * @test + * @summary Test Event.isEnabled() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.TestIsEnabled + */ + +public class TestIsEnabled { + + public static void main(String[] args) throws Exception { + assertDisabled("Event enabled with no recording"); + + Recording r = new Recording(); + assertDisabled("Event enabled at new Recording()"); + + r.enable(SimpleEvent.class); + assertDisabled("Event enabled before r.start()"); + + r.start(); + + // enable/disable by class + assertEnabled("Event not enabled after r.start()"); + r.disable(SimpleEvent.class); + assertDisabled("Event enabled after r.disable()"); + r.enable(SimpleEvent.class); + assertEnabled("Event disabled afer r.enable()"); + + // enable/disable by event setting name + String eventSettingName = String.valueOf(EventType.getEventType(SimpleEvent.class).getId()); + System.out.println("eventSettingName=" + eventSettingName); + + r.disable(eventSettingName); + assertDisabled("Event enabled after r.disable(name)"); + r.enable(eventSettingName); + assertEnabled("Event disabled after r.enable(name)"); + + r.stop(); + assertDisabled("Event enabled after r.stop()"); + + r.close(); + assertDisabled("Event enabled after r.close()"); + } + + private static void assertEnabled(String msg) { + SimpleEvent event = new SimpleEvent(); + Asserts.assertTrue(event.isEnabled(), msg); + } + + private static void assertDisabled(String msg) { + SimpleEvent event = new SimpleEvent(); + Asserts.assertFalse(event.isEnabled(), msg); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TestIsEnabledMultiple.java 2019-02-08 18:32:49.213704322 +0300 @@ -0,0 +1,128 @@ +/* + * Copyright (c) 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.api.event; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test Event.isEnabled() with multiple recordings + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.TestIsEnabledMultiple + */ + +public class TestIsEnabledMultiple { + + enum When { + BeforeStart, DuringRecording + } + + enum RecState { + New, Running + } + + enum EnableState { + Enabled, Disabled + } + + public static void main(String[] args) throws Exception { + for (RecState recStateA : RecState.values()) { + for (RecState recStateB : RecState.values()) { + for (EnableState enableStateA : EnableState.values()) { + for (EnableState enableStateB : EnableState.values()) { + assertEnabled(recStateA, recStateB, enableStateA, enableStateB); + } + } + } + } + } + + private static void assertEnabled(RecState recStateA, RecState recStateB, EnableState enableStateA, EnableState enableStateB) { + + Recording a = new Recording(); + Recording b = new Recording(); + assertEnablement(false); // no recording running + + if (enableStateA == EnableState.Disabled) { + a.disable(MyEvent.class); + } + if (enableStateA == EnableState.Enabled) { + a.enable(MyEvent.class); + } + if (enableStateB == EnableState.Disabled) { + b.disable(MyEvent.class); + } + if (enableStateB == EnableState.Enabled) { + b.enable(MyEvent.class); + } + if ( enableStateA == EnableState.Disabled && recStateA == RecState.Running) { + if ( enableStateB == EnableState.Disabled && recStateB == RecState.Running) { + System.out.println(); + } + } + if (recStateA == RecState.Running) { + a.start(); + } + if (recStateB == RecState.Running) { + b.start(); + } + + System.out.println("Recording a is " + a.getState() + ". Event is " + enableStateA); + System.out.println("Recording b is " + b.getState() + ". Event is " + enableStateB); + // an event is enabled if at least one recording is running + // and the event is on by default or has been enabled. + boolean aEnabled = recStateA == RecState.Running && enableStateA == EnableState.Enabled; + boolean bEnabled = recStateB == RecState.Running && enableStateB == EnableState.Enabled; + boolean enabled = aEnabled || bEnabled; + System.out.println("Expected enablement: " + enabled); + System.out.println(); + assertEnablement(enabled); + if (recStateA == RecState.Running) { + a.stop(); + } + if (recStateB == RecState.Running) { + b.stop(); + } + assertEnablement(false); // no recording running + a.close(); + b.close(); + } + + private static void assertEnablement(boolean enabled) { + Asserts.assertEQ(EventType.getEventType(MyEvent.class).isEnabled(), enabled, "Event enablement not as expected"); + } + + @SuppressWarnings("unused") + private static class MyEvent extends Event { + public String msg; + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TestOwnCommit.java 2019-02-08 18:32:49.361699152 +0300 @@ -0,0 +1,124 @@ +/* + * Copyright (c) 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.api.event; + +import java.time.Instant; +import java.util.Iterator; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Use custom event that reuse method names begin, end and commit. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.TestOwnCommit + */ + +public class TestOwnCommit { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + r.enable(MyEvent.class); + + r.start(); + + MyEvent event = new MyEvent(); + event.begin(); + event.begin = 10; + event.duration = Instant.now(); + MyEvent.startTime = 20; + event.shouldCommit = "shouldCommit"; + MyEvent.set = 30; + event.end(); + event.commit(); + + // Verify that our methods have not been removed by transformation. + int id = 0; + event.begin(++id); + Asserts.assertEquals(id, staticTestValue, "EventWithBegin failed to set value"); + event.end(++id); + Asserts.assertEquals(id, staticTestValue, "EventWithEnd failed to set value"); + event.commit(++id); + Asserts.assertEquals(id, staticTestValue, "EventWithCommit failed to set value"); + event.shouldCommit(++id); + Asserts.assertEquals(id, staticTestValue, "EventWithShouldCommit failed to set value"); + event.set(++id); + Asserts.assertEquals(id, staticTestValue, "EventWithSet failed to set value"); + + r.stop(); + + Iterator it = Events.fromRecording(r).iterator(); + Asserts.assertTrue(it.hasNext(), "No events"); + RecordedEvent recordedEvent = it.next(); + Asserts.assertTrue(Events.isEventType(recordedEvent, MyEvent.class.getName())); + Events.assertField(recordedEvent, "begin").equal(10L); + Events.assertField(recordedEvent, "shouldCommit").equal("shouldCommit"); + Events.assertField(recordedEvent, "startTime"); + Events.assertField(recordedEvent, "duration"); + Asserts.assertNull(recordedEvent.getEventType().getField("set")); // static not supported + Asserts.assertFalse(it.hasNext(), "More than 1 event"); + + r.close(); + } + + private static int staticTestValue; + + @SuppressWarnings("unused") + static class MyEvent extends Event { + public long begin; + private Instant duration; + private static int startTime; + protected String shouldCommit; + public static int set; + + public void begin(int testValue) { + staticTestValue = testValue; + } + + public void end(int testValue) { + staticTestValue = testValue; + } + + public void commit(int testValue) { + staticTestValue = testValue; + } + + public void shouldCommit(int testValue) { + staticTestValue = testValue; + } + + public void set(int testValue) { + staticTestValue = testValue; + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TestShouldCommit.java 2019-02-08 18:32:49.509693982 +0300 @@ -0,0 +1,195 @@ +/* + * Copyright (c) 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.api.event; + +import java.time.Duration; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test enable/disable event and verify recording has expected events. + * @key jfr + * + * @library /lib / + * @run main/othervm -XX:+LogJFR jdk.jfr.api.event.TestShouldCommit + */ + +public class TestShouldCommit { + + public static void main(String[] args) throws Exception { + Recording rA = new Recording(); + + verifyShouldCommitFalse(); // No active recordings + + rA.start(); + rA.enable(MyEvent.class).withoutThreshold(); // recA=all + verifyShouldCommitTrue(); + + setThreshold(rA, 100); // recA=100 + verifyThreshold(100); + + setThreshold(rA, 200); // recA=200 + verifyThreshold(200); + + Recording rB = new Recording(); + verifyThreshold(200); // recA=200, recB=not started + + rB.start(); + verifyThreshold(200); // recA=200, recB=not specified, settings from recA is used. + + setThreshold(rB, 100); // recA=200, recB=100 + verifyThreshold(100); + + setThreshold(rB, 300); // recA=200, recB=300 + verifyThreshold(200); + + rA.disable(MyEvent.class); // recA=disabled, recB=300 + + verifyThreshold(300); + + rB.disable(MyEvent.class); // recA=disabled, recB=disabled + verifyShouldCommitFalse(); + + setThreshold(rA, 200); // recA=200, recB=disabled + verifyThreshold(200); + + rB.enable(MyEvent.class).withoutThreshold(); // recA=200, recB=all + verifyShouldCommitTrue(); + + setThreshold(rB, 100); // recA=200, recB=100 + verifyThreshold(100); + + rB.stop(); // recA=200, recB=stopped + verifyThreshold(200); + + rA.stop(); // recA=stopped, recB=stopped + verifyShouldCommitFalse(); + + rA.close(); + rB.close(); + + verifyShouldCommitFalse(); + } + + private static void setThreshold(Recording r, long millis) { + r.enable(MyEvent.class).withThreshold(Duration.ofMillis(millis)); + } + + private static void verifyThreshold(long threshold) throws Exception { + // Create 2 events, with different sleep time between begin() and end() + // First event ends just before threshold, the other just after. + verifyThreshold(threshold-5, threshold); + verifyThreshold(threshold+5, threshold); + } + + private static void verifyThreshold(long sleepMs, long thresholdMs) throws Exception { + MyEvent event = new MyEvent(); + + long beforeStartNanos = System.nanoTime(); + event.begin(); + long afterStartNanos = System.nanoTime(); + + Thread.sleep(sleepMs); + + long beforeStopNanos = System.nanoTime(); + event.end(); + long afterStopNanos = System.nanoTime(); + + boolean actualShouldCommit = event.shouldCommit(); + + final long safetyMarginNanos = 2000000; // Allow an error of 2 ms. May have to be tuned. + + //Duration of event has been at least minDurationMicros + long minDurationMicros = (beforeStopNanos - afterStartNanos - safetyMarginNanos) / 1000; + //Duration of event has been at most maxDurationMicros + long maxDurationMicros = (afterStopNanos - beforeStartNanos + safetyMarginNanos) / 1000; + Asserts.assertLessThanOrEqual(minDurationMicros, maxDurationMicros, "Wrong min/max duration. Test error."); + + long thresholdMicros = thresholdMs * 1000; + Boolean shouldCommit = null; + if (minDurationMicros > thresholdMicros) { + shouldCommit = new Boolean(true); // shouldCommit() must be true + } else if (maxDurationMicros < thresholdMicros) { + shouldCommit = new Boolean(false); // shouldCommit() must be false + } else { + // Too close to call. No checks are done since we are not sure of expected shouldCommit(). + } + + System.out.printf( + "threshold=%d, duration=[%d-%d], shouldCommit()=%b, expected=%s%n", + thresholdMicros, minDurationMicros, maxDurationMicros, actualShouldCommit, + (shouldCommit!=null ? shouldCommit : "too close to call")); + + try { + if (shouldCommit != null) { + Asserts.assertEquals(shouldCommit.booleanValue(), actualShouldCommit, "Wrong shouldCommit()"); + } + } catch (Exception e) { + System.out.println("Unexpected value of shouldCommit(). Searching for active threshold..."); + searchThreshold(thresholdMs, 2000+thresholdMs); + throw e; + } + } + + // Sleeps until shouldCommit() is true, or give up. Used for logging. + private static void searchThreshold(long expectedMs, long maxMs) throws Exception { + long start = System.nanoTime(); + long stop = start + maxMs * 1000000; + + MyEvent event = new MyEvent(); + event.begin(); + event.end(); + + while (!event.shouldCommit() && System.nanoTime() < stop) { + Thread.sleep(1); + event.end(); + } + long durationMicros = (System.nanoTime() - start) / 1000; + long expectedMicros = expectedMs * 1000; + System.out.printf("shouldCommit()=%b after %,d ms, expected %,d%n", event.shouldCommit(), durationMicros, expectedMicros); + } + + private static void verifyShouldCommitFalse() { + MyEvent event = new MyEvent(); + event.begin(); + event.end(); + Asserts.assertFalse(event.shouldCommit(), "shouldCommit() expected false"); + } + + private static void verifyShouldCommitTrue() { + MyEvent event = new MyEvent(); + event.begin(); + event.end(); + Asserts.assertTrue(event.shouldCommit(), "shouldCommit() expected true"); + } + + private static class MyEvent extends Event { + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/TestStaticEnable.java 2019-02-08 18:32:49.653688950 +0300 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 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.api.event; + +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Enable an event from a static function in the event. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.TestStaticEnable + */ +public class TestStaticEnable { + + public static void main(String[] args) throws Exception { + Recording r = new Recording(); + MyEvent.enable(r, true); + r.start(); + MyEvent.create("Hello", 1); + r.stop(); + + List events = Events.fromRecording(r); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + Events.assertField(event, "msg").equal("Hello"); + Events.assertField(event, "id").equal(1L); + } + r.close(); + } + + + public static class MyEvent extends Event { + public String msg; + long id; + + public static void enable(Recording r, boolean isEnabled) { + if (isEnabled) { + r.enable(MyEvent.class).withThreshold(Duration.ofMillis(0)).withoutStackTrace(); + } else { + r.disable(MyEvent.class); + } + } + + public static void create(String msg, long id) { + MyEvent event = new MyEvent(); + event.msg = msg; + event.id = id; + event.begin(); + event.end(); + event.commit(); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/dynamic/TestDynamicAnnotations.java 2019-02-08 18:32:49.797683920 +0300 @@ -0,0 +1,219 @@ +/* + * Copyright (c) 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.api.event.dynamic; + +import java.io.IOException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.EventFactory; +import jdk.jfr.EventType; +import jdk.jfr.Label; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.Name; +import jdk.jfr.Recording; +import jdk.jfr.Relational; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.dynamic.TestDynamicAnnotations + */ +public class TestDynamicAnnotations { + + @Label("Execution Context Id") + @Description("A unique identifier to correlate events or requests associated with the same task across several components") + @Relational + @MetadataDefinition + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.RUNTIME) + private @interface ECID { + } + + @MetadataDefinition + @Target({ ElementType.FIELD, ElementType.TYPE }) + @Retention(RetentionPolicy.RUNTIME) + private @interface Array { + String[] stringArray(); + + int[] intArray(); + + long[] longArray(); + + float[] floatArray(); + + double[] doubleArray(); + + boolean[] booleanArray(); + + short[] shortArray(); + + byte[] byteArray(); + + char[] charArray(); + } + + public static void main(String[] args) throws Throwable { + testEventFactoryExample(); + testECID(); + testArray(); + } + + // Copy of sample code in Javadoc for jdk.jfr.EVentFactory + public static void testEventFactoryExample() throws IOException { + List fields = new ArrayList<>(); + List messageAnnotations = Collections.singletonList(new AnnotationElement(Label.class, "Message")); + fields.add(new ValueDescriptor(String.class, "message", messageAnnotations)); + List numberAnnotations = Collections.singletonList(new AnnotationElement(Label.class, "Number")); + fields.add(new ValueDescriptor(int.class, "number", numberAnnotations)); + + String[] category = { "Example", "Getting Started" }; + List eventAnnotations = new ArrayList<>(); + eventAnnotations.add(new AnnotationElement(Name.class, "com.example.HelloWorld")); + eventAnnotations.add(new AnnotationElement(Label.class, "Hello World")); + eventAnnotations.add(new AnnotationElement(Description.class, "Helps programmer getting started")); + eventAnnotations.add(new AnnotationElement(Category.class, category)); + + EventFactory f = EventFactory.create(eventAnnotations, fields); + + Event event = f.newEvent(); + event.set(0, "hello, world!"); + event.set(1, 4711); + event.commit(); + } + + public static void testECID() throws Exception { + List fields = new ArrayList<>(); + + List fieldAnnotations = new ArrayList<>(); + fieldAnnotations.add(new AnnotationElement(ECID.class)); + ValueDescriptor ecidField = new ValueDescriptor(String.class, "ecid", fieldAnnotations); + fields.add(ecidField); + + EventFactory f = EventFactory.create(fieldAnnotations, fields); + + String ecidValue = "131739871298371279812"; + try (Recording r = new Recording()) { + r.start(); + Event event = f.newEvent(); + event.set(0, ecidValue); + event.commit(); + r.stop(); + List events = Events.fromRecording(r); + Events.hasEvents(events); + Events.assertField(events.get(0), "ecid").equal(ecidValue); + } + EventType type = f.getEventType(); + ECID e = type.getAnnotation(ECID.class); + if (e == null) { + throw new Exception("Missing ECID annotation"); + } + } + + public static void testArray() throws Exception { + List annotations = new ArrayList<>(); + Map values = new HashMap<>(); + values.put("stringArray", new String[] {"zero", "one"}); + values.put("intArray", new int[] {0, 1}); + values.put("longArray", new long[] {0L, 1L}); + values.put("floatArray", new float[] {0.0f, 1.0f}); + values.put("doubleArray", new double[] {0.0, 1.0}); + values.put("booleanArray", new boolean[] {false, true}); + values.put("shortArray", new short[] {(short)0, (short)1}); + values.put("byteArray", new byte[] {(byte)0, (byte)1}); + values.put("charArray", new char[] {'0','1'}); + + annotations.add(new AnnotationElement(Array.class, values)); + EventFactory f = EventFactory.create(annotations, Collections.emptyList()); + Array a = f.getEventType().getAnnotation(Array.class); + if (a == null) { + throw new Exception("Missing array annotation"); + } + verifyArrayAnnotation(a); + System.out.println("Event metadata is correct"); + try (Recording r = new Recording()) { + r.start(); + Event e = f.newEvent(); + e.commit(); + r.stop(); + List events = Events.fromRecording(r); + Events.hasEvents(events); + RecordedEvent re = events.get(0); + Array arrayAnnotation = re.getEventType().getAnnotation(Array.class); + if (arrayAnnotation== null) { + throw new Exception("Missing array annotation"); + } + verifyArrayAnnotation(arrayAnnotation); + System.out.println("Persisted event metadata is correct"); + } + } + + private static void verifyArrayAnnotation(Array a) throws Exception { + if (!a.stringArray()[0].equals("zero") || !a.stringArray()[1].equals("one")) { + throw new Exception("string[] doesn't match"); + } + if (a.intArray()[0] != 0 || a.intArray()[1] != 1) { + throw new Exception("int[] doesn't match"); + } + if (a.longArray()[0] != 0 || a.longArray()[1] != 1) { + throw new Exception("long[] doesn't match"); + } + if (a.floatArray()[0] != 0.0f || a.floatArray()[1] != 1.0f) { + throw new Exception("float[] doesn't match"); + } + if (a.doubleArray()[0] != 0.0 || a.doubleArray()[1] != 1.0) { + throw new Exception("double[] doesn't match"); + } + if (a.booleanArray()[0] != false || a.booleanArray()[1] != true) { + throw new Exception("boolean[] doesn't match"); + } + if (a.shortArray()[0] != (short)0 || a.shortArray()[1] != (short)1) { + throw new Exception("short[] doesn't match"); + } + if (a.byteArray()[0] != (byte)0 || a.byteArray()[1] != (byte)1) { + throw new Exception("byte[] doesn't match"); + } + if (a.charArray()[0] != '0' || a.charArray()[1] != '1') { + throw new Exception("char[] doesn't match"); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/event/dynamic/TestEventFactory.java 2019-02-08 18:32:49.941678889 +0300 @@ -0,0 +1,289 @@ +/* + * Copyright (c) 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.api.event.dynamic; + +import java.io.IOException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Event; +import jdk.jfr.EventFactory; +import jdk.jfr.EventType; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.Recording; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventTypePrototype; +import jdk.test.lib.jfr.Events; + + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.event.dynamic.TestEventFactory + */ +public class TestEventFactory { + + @MetadataDefinition + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.FIELD, ElementType.TYPE }) + public @interface TestAnnotation { + String value(); + } + + public final static Map EVENT_VALUES = new HashMap<>(); + public final static EventTypePrototype EVENT_TYPE_SHOULD_NOT_COMMIT; + public final static EventTypePrototype EVENT_TYPE_SHOULD_COMMIT; + + // keep alive to prevent event metadata getting GC. + public static EventFactory ef1; + public static EventFactory ef2; + + static { + EVENT_VALUES.put("intField", Integer.MAX_VALUE); + EVENT_VALUES.put("longField", Long.MAX_VALUE); + EVENT_VALUES.put("byteField", (byte) 5); + EVENT_VALUES.put("charField", (char) 'H'); + EVENT_VALUES.put("shortField", (short) 56); + EVENT_VALUES.put("booleanField", true); + EVENT_VALUES.put("floatField", 4711.0f); + EVENT_VALUES.put("doubleField", 3.141); + EVENT_VALUES.put("classField", String.class); + EVENT_VALUES.put("stringField", "Yeah!"); + EVENT_VALUES.put("threadField", Thread.currentThread()); + + EVENT_TYPE_SHOULD_NOT_COMMIT = makeEventType("com.test.ShouldNotCommit"); + EVENT_TYPE_SHOULD_COMMIT = makeEventType("com.test.ShouldCommit"); + } + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + r.enable(EVENT_TYPE_SHOULD_COMMIT.getName()).withoutStackTrace(); + r.enable(EVENT_TYPE_SHOULD_NOT_COMMIT.getName()).withoutStackTrace(); + + // Commit before start, should not be included + ef1 = EventFactory.create(EVENT_TYPE_SHOULD_NOT_COMMIT.getAnnotations(), EVENT_TYPE_SHOULD_NOT_COMMIT.getFields()); + + Event event1 = ef1.newEvent(); + + setEventValues(event1, ef1, EVENT_TYPE_SHOULD_NOT_COMMIT); + event1.commit(); + + r.start(); + // Commit after start, should be included + ef2 = EventFactory.create(EVENT_TYPE_SHOULD_COMMIT.getAnnotations(), EVENT_TYPE_SHOULD_COMMIT.getFields()); + + Event event2 = ef2.newEvent(); + setEventValues(event2, ef2, EVENT_TYPE_SHOULD_COMMIT); + event2.commit(); + + r.stop(); + + RecordingFile es = Events.copyTo(r); + EventType e1 = findEventType(es.readEventTypes(), EVENT_TYPE_SHOULD_NOT_COMMIT.getName()); + assertEquals(e1, ef1.getEventType()); + + EventType e2 = findEventType(es.readEventTypes(), EVENT_TYPE_SHOULD_COMMIT.getName()); + assertEquals(e2, ef2.getEventType()); + + verifyEvent(es); + } + + private static EventType findEventType(List es, String name) { + for (EventType t : es) { + if (t.getName().equals(name)) { + return t; + } + } + throw new AssertionError("Could not find expected event type " + name); + } + + private static void assertEquals(EventType e1, EventType expected) { + Asserts.assertEquals(e1.getName(), expected.getName()); + Asserts.assertEquals(e1.getDescription(), expected.getDescription()); + Asserts.assertEquals(e1.getLabel(), expected.getLabel()); + assertValueDescriptorEquals(e1.getFields(), expected.getFields()); + assertAnnotationEquals(e1.getAnnotationElements(), expected.getAnnotationElements()); + } + + private static void assertValueDescriptorEquals(List values, List expected) { + if (values.isEmpty() && expected.isEmpty()) { + return; + } + + Map valueMap = new HashMap<>(); + for (ValueDescriptor v : values) { + valueMap.put(v.getName(), v); + } + for (ValueDescriptor f : expected) { + ValueDescriptor v = valueMap.remove(f.getName()); + if (v == null) { + throw new AssertionError("Expected value descriptor " + f.getName() + " not found"); + } + assertEquals(v, f); + } + if (!valueMap.isEmpty()) { + throw new AssertionError("More fields than expected"); + } + } + + private static void assertEquals(ValueDescriptor v1, ValueDescriptor expected) { + Asserts.assertEquals(v1.getName(), expected.getName()); + Asserts.assertEquals(v1.getTypeName(), expected.getTypeName()); + assertAnnotationEquals(v1.getAnnotationElements(), expected.getAnnotationElements()); + } + + private static void assertAnnotationEquals(List annotations, List expected) { + annotations = new ArrayList<>(annotations); // make mutable + expected = new ArrayList<>(expected); // make mutable + class AnnotationTypeComparator implements Comparator { + @Override + public int compare(AnnotationElement a, AnnotationElement b) { + return a.getTypeName().compareTo(b.getTypeName()); + } + } + + if (annotations.isEmpty() && expected.isEmpty()) { + return; + } + + if (annotations.size() != expected.size()) { + System.out.println("Was:"); + for(AnnotationElement ae: annotations) { + System.out.println(ae.getTypeName()); + } + System.out.println("Expected:"); + for(AnnotationElement ae: expected) { + System.out.println(ae.getTypeName()); + } + throw new AssertionError("Wrong number of annotations"); + } + Collections.sort(expected, new AnnotationTypeComparator()); + Collections.sort(annotations, new AnnotationTypeComparator()); + for (int i = 0; i < expected.size(); i++) { + assertEquals(annotations.get(i), expected.get(i)); + } + } + + private static void assertEquals(AnnotationElement a1, AnnotationElement expected) { + Asserts.assertEquals(a1.getTypeName(), expected.getTypeName()); + // Don't recurse into annotation + assertValueDescriptorEquals(a1.getValueDescriptors(), expected.getValueDescriptors()); + } + + private static void verifyEvent(RecordingFile rf) throws IOException { + if (!rf.hasMoreEvents()) { + throw new AssertionError("Expected one dynamic event"); + } + verifyValues(rf.readEvent()); + if (rf.hasMoreEvents()) { + throw new AssertionError("Expected one dynamic event"); + } + } + + private static void setEventValues(Event event, EventFactory f, EventTypePrototype eventTypeProto) { + for (Map.Entry entry : EVENT_VALUES.entrySet()) { + int index = eventTypeProto.getFieldIndex(entry.getKey()); + event.set(index, entry.getValue()); + } + } + + private static void verifyValues(RecordedEvent event) { + for (Map.Entry entry : EVENT_VALUES.entrySet()) { + String fieldName = entry.getKey(); + Object value = event.getValue(fieldName); + Object expected = EVENT_VALUES.get(fieldName); + if (expected instanceof Class) { + value = ((RecordedClass) value).getName(); + expected = ((Class) expected).getName(); + } + if (expected instanceof Thread) { + value = ((RecordedThread) value).getJavaName(); + expected = ((Thread) expected).getName(); + } + Asserts.assertEQ(value, expected); + } + } + + private static EventTypePrototype makeEventType(String eventName) { + EventTypePrototype prototype = new EventTypePrototype(eventName); + prototype.addAnnotation(new AnnotationElement(TestAnnotation.class, "type")); + for (Map.Entry entry : EVENT_VALUES.entrySet()) { + Class type = makePrimitive(entry.getValue().getClass()); + String fieldName = entry.getKey(); + prototype.addField(new ValueDescriptor(type, fieldName)); + } + // add an annotated field + List annos = new ArrayList<>(); + annos.add(new AnnotationElement(TestAnnotation.class, "field")); + prototype.addField( new ValueDescriptor(int.class, "annotatedField", annos)); + + return prototype; + } + + private static Class makePrimitive(Class clazz) { + if (clazz == Integer.class) { + return int.class; + } + if (clazz == Long.class) { + return long.class; + } + if (clazz == Double.class) { + return double.class; + } + if (clazz == Float.class) { + return float.class; + } + if (clazz == Short.class) { + return short.class; + } + if (clazz == Character.class) { + return char.class; + } + if (clazz == Byte.class) { + return byte.class; + } + if (clazz == Boolean.class) { + return boolean.class; + } + return clazz; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/MyListener.java 2019-02-08 18:32:50.085673859 +0300 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import static jdk.test.lib.Asserts.assertEquals; + +import jdk.jfr.FlightRecorderListener; +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; + +public class MyListener implements FlightRecorderListener { + public RecordingState state = RecordingState.NEW; + public int countCallbacks = 0; + + @Override + public synchronized void recordingStateChanged(Recording r) { + ++countCallbacks; + state = r.getState(); + } + + public synchronized void verifyState(int expectedCountCallbacks, RecordingState expectedState) { + assertEquals(state, expectedState, "listener.state=" + expectedState); + assertEquals(countCallbacks, expectedCountCallbacks, "listener.countCallbacks=" + countCallbacks); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestAddListenerTwice.java 2019-02-08 18:32:50.229668828 +0300 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.flightrecorder.TestAddListenerTwice + */ +public class TestAddListenerTwice { + + public static void main(String[] args) throws Throwable { + MyListener listener = new MyListener(); + + FlightRecorder.addListener(listener); + FlightRecorder.addListener(listener); + + Recording r1 = new Recording(); + r1.start(); + listener.verifyState(2, RecordingState.RUNNING); + + FlightRecorder.removeListener(listener); // Should still get callback to one listener. + r1.stop(); + listener.verifyState(3, RecordingState.STOPPED); + r1.close(); + listener.verifyState(4, RecordingState.CLOSED); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestAddPeriodicEvent.java 2019-02-08 18:32:50.373663798 +0300 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; + +import java.time.Duration; +import java.util.concurrent.CountDownLatch; + +import jdk.jfr.Event; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; + +/** + * @test + * @summary + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.flightrecorder.TestAddPeriodicEvent + */ +public class TestAddPeriodicEvent { + + private static class MyEvent extends Event { + + } + + CountDownLatch latch = new CountDownLatch(3); + + class MyHook implements Runnable { + + private int eventCounter; + private long previousTime; + + @Override + public void run() { + log("Commiting event " + (++eventCounter)); + if (previousTime == 0) { + previousTime = System.currentTimeMillis(); + } else { + long nowTime = System.currentTimeMillis(); + long elapsedTime = nowTime - previousTime; + previousTime = nowTime; + log("Elapsed time since the previous event: " + elapsedTime); + } + + commitEvent(); + latch.countDown(); + } + + private void commitEvent() { + MyEvent event = new MyEvent(); + event.commit(); + } + } + + public static void main(String[] args) throws Exception { + new TestAddPeriodicEvent().doTest(1000, 3); + } + + private void doTest(long eventDuration, int numOfEvents) throws Exception { + latch = new CountDownLatch(numOfEvents); + MyHook hook = new MyHook(); + + Recording r = new Recording(); + r.enable(MyEvent.class).withPeriod(Duration.ofMillis(eventDuration)); + r.start(); + + FlightRecorder.addPeriodicEvent(MyEvent.class, hook); + + latch.await(); + + assertTrue(FlightRecorder.removePeriodicEvent(hook)); + assertFalse(FlightRecorder.removePeriodicEvent(hook)); + + r.stop(); + r.close(); + } + + private static void log(String text) { + System.out.println(text); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestFlightRecorderListenerRecorderInitialized.java 2019-02-08 18:32:50.517658767 +0300 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 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.api.flightrecorder; +import static jdk.test.lib.Asserts.assertTrue; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.FlightRecorderListener; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.flightrecorder.TestFlightRecorderListenerRecorderInitialized + */ +public class TestFlightRecorderListenerRecorderInitialized { + + /** + * Utility class to wait/notify + */ + private static class Signal { + + private static volatile boolean signalled; + private static final Lock lock = new ReentrantLock(); + private static final Condition cond = lock.newCondition(); + + private static void waitFor(long timeout, TimeUnit timeUnit) throws InterruptedException { + try { + lock.lock(); + if (!signalled) { + log("Waiting for FlightRecorder.recorderInitialized notification..."); + cond.await(timeout, timeUnit); + } + } finally { + lock.unlock(); + } + } + + private static void signal() { + try { + lock.lock(); + signalled = true; + cond.signalAll(); + } finally { + lock.unlock(); + } + } + }; + + public static void main(String[] args) throws Throwable { + FlightRecorder.addListener(new FlightRecorderListener() { + + @Override + public void recorderInitialized(FlightRecorder recorder) { + log("Recorder initialized"); + Signal.signal(); + } + + }); + FlightRecorder.getFlightRecorder(); + + Signal.waitFor(3, TimeUnit.SECONDS); + + assertTrue(Signal.signalled, "FlightRecorderListener.recorderInitialized has not been called"); + + } + + private static void log(String s) { + System.out.println(s); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestGetEventTypes.java 2019-02-08 18:32:50.661653737 +0300 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; + +import java.util.HashSet; +import java.util.Set; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm/timeout=600 jdk.jfr.api.flightrecorder.TestGetEventTypes + */ +public class TestGetEventTypes { + + public static void main(String[] args) throws Throwable { + Recording r1 = new Recording(); + r1.setToDisk(true); + + MyEvent myEvent = new MyEvent(); + EventType t = EventType.getEventType(MyEvent.class); + System.out.println(t.getName()); + boolean isMyEventFound = false; + for (EventType eventType : FlightRecorder.getFlightRecorder().getEventTypes()) { + System.out.println(": eventType: " + eventType.getName()); + r1.enable(eventType.getName()); + if (eventType.getName().equals(MyEvent.class.getName())) { + isMyEventFound = true; + } + } + assertTrue(isMyEventFound, "EventType for MyEvent not found"); + + r1.start(); + myEvent.begin(); + myEvent.end(); + myEvent.commit(); + r1.stop(); + + Set eventTypeNames = new HashSet(); + for (EventType et : FlightRecorder.getFlightRecorder().getEventTypes()) { + assertFalse(eventTypeNames.contains(et.getName()), "EventType returned twice: " + et.getName()); + eventTypeNames.add(et.getName()); + } + + isMyEventFound = false; + for (RecordedEvent event : Events.fromRecording(r1)) { + final String name = event.getEventType().getName(); + System.out.println("event.getEventType: " + name); + assertTrue(eventTypeNames.contains(name), "Missing EventType: " + name); + if (name.equals(MyEvent.class.getName())) { + isMyEventFound = true; + } + } + r1.close(); + assertTrue(isMyEventFound, "Event for MyEvent not found"); + } + + private static class MyEvent extends Event { + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestGetPlatformRecorder.java 2019-02-08 18:32:50.805648706 +0300 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import static jdk.test.lib.Asserts.assertEquals; + +import jdk.jfr.FlightRecorder; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.flightrecorder.TestGetPlatformRecorder + */ +public class TestGetPlatformRecorder { + + public static void main(String[] args) throws Throwable { + FlightRecorder r1 = FlightRecorder.getFlightRecorder(); + FlightRecorder r2 = FlightRecorder.getFlightRecorder(); + assertEquals(r1, r2, "getPlatformRecorder should return same instance"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestGetRecordings.java 2019-02-08 18:32:50.949643676 +0300 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; + +import java.util.List; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.flightrecorder.TestGetRecordings + */ +public class TestGetRecordings { + + public static void main(String[] args) throws Throwable { + FlightRecorder recorder = FlightRecorder.getFlightRecorder(); + + // Recording should be empty at start. + List recordings = recorder.getRecordings(); + assertTrue(recordings.isEmpty(), "recordings should be empty at start"); + + // Create first recording + Recording r1 = new Recording(); + recordings = recorder.getRecordings(); + assertEquals(recordings.size(), 1, "Expected 1 recording"); + assertTrue(recordings.contains(r1), "r1 should be in list"); + + // Create second recording + Recording r2 = new Recording(); + recordings = recorder.getRecordings(); + assertEquals(recordings.size(), 2, "Expected 2 recordings"); + assertTrue(recordings.contains(r2), "r2 should be in list"); + assertTrue(recordings.contains(r1), "r1 should still be in list"); + + // Close first recording + r1.close(); + recordings = recorder.getRecordings(); + assertEquals(recordings.size(), 1, "Expected 1 remaining recording"); + assertTrue(recordings.contains(r2), "r2 should still be in list"); + assertFalse(recordings.contains(r1), "r1 should be removed"); + + // Close second recording + r2.close(); + recordings = recorder.getRecordings(); + assertTrue(recordings.isEmpty(), "recordings should be empty after close"); + + // Create recording with new Recording() + Recording r3 = new Recording(); + recordings = recorder.getRecordings(); + assertTrue(recordings.contains(r3 ), "new Recording() should be in list"); + r3.close(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestGetSettings.java 2019-02-08 18:32:51.093638646 +0300 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertTrue; + +import java.time.Duration; +import java.util.Map; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Recording; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.flightrecorder.TestGetSettings + */ +public class TestGetSettings { + + public static void main(String[] args) throws Throwable { + final long minThresholdNanos = 1000000; + final String dummyEventPath = "mydummy/event/path"; + final String myEventSettingName = String.valueOf(EventType.getEventType(MyEvent.class).getId()); + System.out.println("myEventSettingName=" + myEventSettingName); + + // Settings should be merged to include the most number of events (minimum threshold). + Recording r1 = new Recording(); + r1.enable(MyEvent.class).withThreshold(Duration.ofNanos(minThresholdNanos * 3)); + r1.enable(MyEvent.class).withThreshold(Duration.ofNanos(minThresholdNanos * 2)); + r1.enable(dummyEventPath).withThreshold(Duration.ofNanos(minThresholdNanos)); + r1.start(); + + ExpectedSetting[] expectedR1 = { + new ExpectedSetting(myEventSettingName, "enabled", "true"), + new ExpectedSetting(myEventSettingName, "threshold", Long.toString(minThresholdNanos * 2) + " ns"), + new ExpectedSetting(dummyEventPath, "enabled", "true"), + new ExpectedSetting(dummyEventPath, "threshold", Long.toString(minThresholdNanos) + " ns"), + }; + + verifySettings(r1.getSettings(), expectedR1); + + // Start another recording. Recorder settings should be merged from both recordings. + Recording r2 = new Recording(); + r2.enable(MyEvent.class).withThreshold(Duration.ofNanos(minThresholdNanos)); + r2.disable(dummyEventPath); + r2.start(); + + ExpectedSetting[] expectedR2 = { + new ExpectedSetting(myEventSettingName, "enabled", "true"), + new ExpectedSetting(myEventSettingName, "threshold", Long.toString(minThresholdNanos) + " ns"), + new ExpectedSetting(dummyEventPath, "enabled", "false") + }; + + verifySettings(r1.getSettings(), expectedR1); + verifySettings(r2.getSettings(), expectedR2); + + // Stop first recording. Recorder should use settings from r2. + r1.stop(); + verifySettings(r2.getSettings(), expectedR2); + + r2.stop(); + r1.close(); + r2.close(); + } + + private static void verifySettings(Map settings, ExpectedSetting ... expectedSettings) { + for (String name : settings.keySet()) { + System.out.printf("Settings: %s=%s%n", name, settings.get(name)); + } + for (ExpectedSetting expected : expectedSettings) { + boolean isFound = false; + for (String name : settings.keySet()) { + if (name.contains(expected.name) && name.contains(expected.option)) { + final String value = settings.get(name); + String msg = String.format("got: %s=%s, expected: %s", name, value, expected.toString()); + assertEquals(value, expected.value, msg); + System.out.println("OK: " + msg); + isFound = true; + break; + } + } + assertTrue(isFound, "Missing setting " + expected.toString()); + } + } + + private static class MyEvent extends Event { + } + + private static class ExpectedSetting { + String name; + String option; + String value; + + public ExpectedSetting(String name, String option, String value) { + this.name = name; + this.option = option; + this.value = value; + } + + @Override + public String toString() { + return String.format("name=%s, option=%s, value=%s", name, option, value); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestIsAvailable.java 2019-02-08 18:32:51.237633616 +0300 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import jdk.jfr.FlightRecorder; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm -XX:+FlightRecorder jdk.jfr.api.flightrecorder.TestIsAvailable true + * @run main/othervm -XX:-FlightRecorder jdk.jfr.api.flightrecorder.TestIsAvailable false + * @run main/othervm jdk.jfr.api.flightrecorder.TestIsAvailable true + */ +public class TestIsAvailable { + + public static void main(String[] args) throws Throwable { + boolean expected = Boolean.parseBoolean(args[0]); + System.out.println("Expected: " + expected); + Asserts.assertTrue(expected == FlightRecorder.isAvailable()); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestIsInitialized.java 2019-02-08 18:32:51.381628586 +0300 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.FlightRecorderListener; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.flightrecorder.TestIsInitialized + */ +public class TestIsInitialized { + + public static void main(String[] args) throws Throwable { + Asserts.assertFalse(FlightRecorder.isInitialized()); + FlightRecorder.addListener(new FlightRecorderListener() { + public void recorderInitialized(FlightRecorder recorder) { + Asserts.assertTrue(FlightRecorder.isInitialized()); + } + }); + FlightRecorder.getFlightRecorder(); + Asserts.assertTrue(FlightRecorder.isInitialized()); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestListener.java 2019-02-08 18:32:51.525623556 +0300 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.flightrecorder.TestListener + */ +public class TestListener { + + public static void main(String[] args) throws Throwable { + MyListener listener1 = new MyListener(); + MyListener listener2 = new MyListener(); + FlightRecorder.addListener(listener1); + FlightRecorder.addListener(listener2); + + Recording r1 = new Recording(); + listener1.verifyState(0, RecordingState.NEW); + listener2.verifyState(0, RecordingState.NEW); + + r1.start(); + listener1.verifyState(1, RecordingState.RUNNING); + listener2.verifyState(1, RecordingState.RUNNING); + + FlightRecorder.removeListener(listener1); // listener1 should not get more callbacks. + r1.stop(); + listener1.verifyState(1, RecordingState.RUNNING); + listener2.verifyState(2, RecordingState.STOPPED); + + r1.close(); + listener1.verifyState(1, RecordingState.RUNNING); + listener2.verifyState(3, RecordingState.CLOSED); + FlightRecorder.removeListener(listener2); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestListenerNull.java 2019-02-08 18:32:51.669618526 +0300 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import static jdk.test.lib.Asserts.fail; + +import jdk.jfr.FlightRecorder; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.flightrecorder.TestListenerNull + */ +public class TestListenerNull { + + public static void main(String[] args) throws Throwable { + try { + FlightRecorder.addListener(null); + fail("No exception when addListener(null)"); + } catch (NullPointerException | IllegalArgumentException e) { + } + + try { + FlightRecorder.removeListener(null); + fail("No exception when removeListener(null)"); + } catch (NullPointerException | IllegalArgumentException e) { + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestPeriodicEventsSameHook.java 2019-02-08 18:32:51.817613356 +0300 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import jdk.jfr.Event; +import jdk.jfr.FlightRecorder; + +/** + * @test + * @summary Check that an IllegalArgumentException is thrown if event is added twice + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.flightrecorder.TestPeriodicEventsSameHook + */ +public class TestPeriodicEventsSameHook { + + private static class MyEvent extends Event { + } + + private static class MyHook implements Runnable { + @Override + public void run() { + } + } + + public static void main(String[] args) throws Exception { + MyHook hook = new MyHook(); + FlightRecorder.addPeriodicEvent(MyEvent.class, hook); + try { + FlightRecorder.addPeriodicEvent(MyEvent.class, hook); + throw new Exception("Expected IllegalArgumentException when adding same hook twice"); + } catch (IllegalArgumentException iae) { + if (!iae.getMessage().equals("Hook has already been added")) { + throw new Exception("Expected IllegalArgumentException with message 'Hook has already been added'"); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestRecorderInitializationCallback.java 2019-02-08 18:32:51.957608466 +0300 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import static jdk.test.lib.Asserts.assertEquals; + +import java.util.concurrent.atomic.AtomicInteger; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.FlightRecorderListener; + +/** + * @test + * @summary Test Flight Recorder initialization callback is only called once + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.flightrecorder.TestRecorderInitializationCallback + */ +public class TestRecorderInitializationCallback { + + private static class TestListener implements FlightRecorderListener { + private final AtomicInteger count = new AtomicInteger(); + + @Override + public void recorderInitialized(FlightRecorder recorder) { + count.incrementAndGet(); + System.out.println("recorderInitialized: " + recorder + " count=" + count); + // Get the recorder again, should not trigger listener + FlightRecorder.getFlightRecorder(); + } + } + + public static void main(String[] args) throws Throwable { + TestListener t = new TestListener(); + FlightRecorder.addListener(t); + // trigger initialization + FlightRecorder.getFlightRecorder(); + assertEquals(1, t.count.intValue(), "Expected 1 notification, got " + t.count); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestRegisterUnregisterEvent.java 2019-02-08 18:32:52.101603437 +0300 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import static jdk.test.lib.Asserts.assertEquals; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.flightrecorder.TestRegisterUnregisterEvent + */ +public class TestRegisterUnregisterEvent { + + public static void main(String[] args) throws Throwable { + // Register before Flight Recorder is started + FlightRecorder.register(MyEvent.class); + // Repeat + FlightRecorder.register(MyEvent.class); + + FlightRecorder recorder = FlightRecorder.getFlightRecorder(); + int count = 0; + for (EventType et : recorder.getEventTypes()) { + if (et.getName().equals(MyEvent.class.getName())) { + count++; + } + } + assertEquals(1, count); + + FlightRecorder.unregister(MyEvent.class); + + count = 0; + for (EventType et : recorder.getEventTypes()) { + if (et.getName().equals(MyEvent.class.getName())) { + count++; + } + } + assertEquals(0, count); + + FlightRecorder.register(MyEvent.class); + + count = 0; + for (EventType et : recorder.getEventTypes()) { + if (et.getName().equals(MyEvent.class.getName())) { + count++; + } + } + assertEquals(1, count); + + } +} + +class MyEvent extends Event { +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestSettingsControl.java 2019-02-08 18:32:52.241598547 +0300 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import static jdk.test.lib.Asserts.assertTrue; + +import java.util.Set; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.SettingControl; +import jdk.jfr.SettingDefinition; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.flightrecorder.TestSettingsControl + */ +public class TestSettingsControl { + static class MySettingsControl extends SettingControl { + + public static boolean setWasCalled; + + private String value = "default"; + + @Override + public String combine(Set values) { + StringBuilder sb = new StringBuilder(); + for(String s : values) { + sb.append(s).append(" "); + } + return sb.toString(); + } + + @Override + public void setValue(String value) { + setWasCalled = true; + this.value = value; + } + + @Override + public String getValue() { + return value; + } + + } + + static class MyCustomSettingEvent extends Event { + @SettingDefinition + boolean mySetting(MySettingsControl msc) { + return true; + } + } + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + r.enable(MyCustomSettingEvent.class).with("mySetting", "myvalue"); + r.start(); + MyCustomSettingEvent e = new MyCustomSettingEvent(); + e.commit(); + r.stop(); + r.close(); + assertTrue(MySettingsControl.setWasCalled, "SettingControl.setValue was not called"); + } +} + + + + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/flightrecorder/TestSnapshot.java 2019-02-08 18:32:52.389593378 +0300 @@ -0,0 +1,187 @@ +/* + * Copyright (c) 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.api.flightrecorder; + +import java.io.IOException; +import java.io.InputStream; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEvent; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.flightrecorder.TestSnapshot + */ +public class TestSnapshot { + private final static int RECORDING_COUNT = 5; + + public static void main(String[] args) throws Exception { + testEmpty(); + testStopped(); + testOngoingDisk(); + testOngoingMemory(); + testMultiple(); + } + + private static void testMultiple() throws IOException { + FlightRecorder recorder = FlightRecorder.getFlightRecorder(); + List recordings = new ArrayList<>(); + long size = 0; + for (int i = 0; i < RECORDING_COUNT; i++) { + Recording r = new Recording(); + r.enable(SimpleEvent.class); + r.start(); + SimpleEvent se = new SimpleEvent(); + se.commit(); + r.stop(); + recordings.add(r); + size += r.getSize(); + } + try (Recording snapshot = recorder.takeSnapshot()) { + Asserts.assertEquals(snapshot.getSize(), size); + Asserts.assertGreaterThanOrEqual(snapshot.getStartTime(), recordings.get(0).getStartTime()); + Asserts.assertLessThanOrEqual(snapshot.getStopTime(), recordings.get(RECORDING_COUNT - 1).getStopTime()); + Asserts.assertGreaterThanOrEqual(snapshot.getDuration(), Duration.ZERO); + assertStaticOptions(snapshot); + try (InputStream is = snapshot.getStream(null, null)) { + Asserts.assertNotNull(is); + } + + List events = Events.fromRecording(snapshot); + Events.hasEvents(events); + Asserts.assertEquals(events.size(), RECORDING_COUNT); + for (int i = 0; i < RECORDING_COUNT; i++) { + Asserts.assertEquals(events.get(i).getEventType().getName(), SimpleEvent.class.getName()); + } + } + for (Recording r : recordings) { + r.close(); + } + } + private static void testOngoingMemory() throws IOException { + testOngoing(false); + } + + private static void testOngoingDisk() throws IOException { + testOngoing(true); + } + + private static void testOngoing(boolean disk) throws IOException { + FlightRecorder recorder = FlightRecorder.getFlightRecorder(); + try (Recording r = new Recording()) { + r.setToDisk(disk); + r.enable(SimpleEvent.class); + r.start(); + SimpleEvent se = new SimpleEvent(); + se.commit(); + + try (Recording snapshot = recorder.takeSnapshot()) { + + Asserts.assertGreaterThan(snapshot.getSize(), 0L); + Asserts.assertGreaterThanOrEqual(snapshot.getStartTime(), r.getStartTime()); + Asserts.assertGreaterThanOrEqual(snapshot.getStopTime(), r.getStartTime()); + Asserts.assertGreaterThanOrEqual(snapshot.getDuration(), Duration.ZERO); + assertStaticOptions(snapshot); + try (InputStream is = snapshot.getStream(null, null)) { + Asserts.assertNotNull(is); + } + + List events = Events.fromRecording(snapshot); + Events.hasEvents(events); + Asserts.assertEquals(events.size(), 1); + Asserts.assertEquals(events.get(0).getEventType().getName(), SimpleEvent.class.getName()); + } + + r.stop(); + } + } + + private static void assertStaticOptions(Recording snapshot) { + Asserts.assertTrue(snapshot.getName().startsWith("Snapshot"), "Recording name should begin with 'Snapshot'"); + Asserts.assertEquals(snapshot.getMaxAge(), null); + Asserts.assertEquals(snapshot.getMaxSize(), 0L); + Asserts.assertTrue(snapshot.getSettings().isEmpty()); + Asserts.assertEquals(snapshot.getState(), RecordingState.STOPPED); + Asserts.assertEquals(snapshot.getDumpOnExit(), false); + Asserts.assertEquals(snapshot.getDestination(), null); + } + + private static void testStopped() throws IOException { + FlightRecorder recorder = FlightRecorder.getFlightRecorder(); + try (Recording r = new Recording()) { + r.enable(SimpleEvent.class); + r.start(); + SimpleEvent se = new SimpleEvent(); + se.commit(); + r.stop(); + + try (Recording snapshot = recorder.takeSnapshot()) { + + Asserts.assertEquals(snapshot.getSize(), r.getSize()); + Asserts.assertGreaterThanOrEqual(snapshot.getStartTime(), r.getStartTime()); + Asserts.assertLessThanOrEqual(snapshot.getStopTime(), r.getStopTime()); + Asserts.assertGreaterThanOrEqual(snapshot.getDuration(), Duration.ZERO); + assertStaticOptions(snapshot); + try (InputStream is = snapshot.getStream(null, null)) { + Asserts.assertNotNull(is); + } + + List events = Events.fromRecording(snapshot); + Events.hasEvents(events); + Asserts.assertEquals(events.size(), 1); + Asserts.assertEquals(events.get(0).getEventType().getName(), SimpleEvent.class.getName()); + } + } + } + + private static void testEmpty() throws IOException { + FlightRecorder recorder = FlightRecorder.getFlightRecorder(); + Instant before = Instant.now().minusNanos(1); + try (Recording snapshot = recorder.takeSnapshot()) { + Instant after = Instant.now().plusNanos(1); + Asserts.assertEquals(snapshot.getSize(), 0L); + Asserts.assertLessThan(before, snapshot.getStartTime()); + Asserts.assertGreaterThan(after, snapshot.getStopTime()); + Asserts.assertEquals(snapshot.getStartTime(), snapshot.getStopTime()); + Asserts.assertEquals(snapshot.getDuration(), Duration.ZERO); + assertStaticOptions(snapshot); + Asserts.assertEquals(snapshot.getStream(null, null), null); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestCategory.java 2019-02-08 18:32:52.533588348 +0300 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import java.util.Arrays; + +import jdk.jfr.Category; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestCategory + */ +public class TestCategory { + + @Category({"Hello", "World"}) + static class CategoryEvent extends Event { + } + + public static void main(String[] args) throws Exception { + EventType t = EventType.getEventType(CategoryEvent.class); + String[] categoryNames = t.getAnnotation(Category.class).value(); + Asserts.assertTrue(Arrays.equals(categoryNames, new String[] { "Hello", "World" }), "Incorrect value for @Category"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestContentType.java 2019-02-08 18:32:52.673583458 +0300 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.ContentType; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.Timespan; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestContentType + */ +public class TestContentType { + @ContentType + @MetadataDefinition + @Target({ ElementType.FIELD }) + @Retention(RetentionPolicy.RUNTIME) + @interface Temperature { + } + + static class SunnyDay extends Event { + String day; + @Temperature + float max; + @Timespan + float hours; + } + + public static void main(String[] args) throws Exception { + + EventType t = EventType.getEventType(SunnyDay.class); + AnnotationElement aMax = Events.getAnnotation(t.getField("max"), Temperature.class); + ContentType cMax = aMax.getAnnotation(ContentType.class); + if (cMax == null) { + throw new Exception("Expected Temperature annotation for field 'max' to have meta annotation ContentType"); + } + AnnotationElement aHours = Events.getAnnotation(t.getField("hours"), Timespan.class); + ContentType cHours = aHours.getAnnotation(ContentType.class); + Asserts.assertTrue(cHours != null, "Expected Timespan annotation for field 'hours' to have meta annotation ContentType"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestDescription.java 2019-02-08 18:32:52.821578289 +0300 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.SettingDefinition; +import jdk.jfr.SettingDescriptor; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleSetting; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestDescription + */ +public class TestDescription { + + @MetadataDefinition + @Target({ ElementType.TYPE }) + @Retention(RetentionPolicy.RUNTIME) + @Description("Meta Annotation") + @interface AnnotationWithDescription { + } + + @AnnotationWithDescription + @Description("Event Annotation") + static class DescriptionEvent extends Event { + @Description("Field Annotation") + String field; + + @SettingDefinition + @Description("Setting description") + boolean dummy(SimpleSetting ds) { + return true; + } + } + + public static void main(String[] args) throws Exception { + + EventType t = EventType.getEventType(DescriptionEvent.class); + + // field description + AnnotationElement aMax = Events.getAnnotation(t.getField("field"), Description.class); + String d = (String) aMax.getValue("value"); + Asserts.assertEquals("Field Annotation", d, "Incorrect annotation for field, got '" + d + "'"); + + // event description + d = t.getAnnotation(Description.class).value(); + Asserts.assertEquals("Event Annotation", d, "Incorrect annotation for event, got '" + d + "'"); + + // annotation description + AnnotationElement a = Events.getAnnotationByName(t, AnnotationWithDescription.class.getName()); + Description de = a.getAnnotation(Description.class); + Asserts.assertEquals("Meta Annotation", de.value(), "Incorrect annotation for event, got '" + de.value() + "'"); + + for (SettingDescriptor v: t.getSettingDescriptors()) { + if (v.getName().equals("dummy")) { + Description settingDescription = v.getAnnotation(Description.class); + Asserts.assertEquals(settingDescription.value(), "Setting description", "Incorrect description for setting"); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestDynamicAnnotation.java 2019-02-08 18:32:52.965573260 +0300 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import java.util.HashMap; +import java.util.Map; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.MetadataDefinition; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestDynamicAnnotation + */ + + +public class TestDynamicAnnotation { + @MetadataDefinition + @interface CustomAnnotation { + String value(); + int intValue(); + } + + public static void main(String[] args) throws Exception { + Map values = new HashMap<>(); + values.put("value", "MyValue"); + values.put("intValue", 1); + new AnnotationElement(CustomAnnotation.class, values); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestEnabled.java 2019-02-08 18:32:53.113568091 +0300 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import jdk.jfr.Enabled; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestEnabled + */ +public class TestEnabled { + + @Enabled(true) + static class EnabledTrueEvent extends Event { + } + + @Enabled(false) + static class EnabledFalseEvent extends Event { + } + + public static void main(String[] args) throws Exception { + EventType trueEvent = EventType.getEventType(EnabledTrueEvent.class); + EventType falseEvent = EventType.getEventType(EnabledFalseEvent.class); + + Recording r = new Recording(); + + Asserts.assertFalse(trueEvent.isEnabled(), "@Enabled(true) event should be diabled before recording start"); + Asserts.assertFalse(falseEvent.isEnabled(), "@Enabled(false) event should be diabled before recording start"); + + r.start(); + + Asserts.assertTrue(trueEvent.isEnabled(), "@Enabled(true) event should to be enabled during recording"); + Asserts.assertFalse(falseEvent.isEnabled(), "@Enabled(true) event should to be disabled during recording"); + + r.stop(); + + Asserts.assertFalse(trueEvent.isEnabled(), "@Enabled(true) event should be diabled after recording stop"); + Asserts.assertFalse(falseEvent.isEnabled(), "@Enabled(false) event should to be diabled after recording stop"); + + r.close(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestExperimental.java 2019-02-08 18:32:53.249563341 +0300 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Experimental; +import jdk.jfr.MetadataDefinition; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestExperimental + */ +public class TestExperimental { + + @MetadataDefinition + @Experimental + @Target({ ElementType.TYPE }) + @Retention(RetentionPolicy.RUNTIME) + @interface ExperimentalAnnotation { + } + + @ExperimentalAnnotation + @Experimental + static class ExperimentalEvent extends Event { + @Experimental + boolean experimentalField; + } + + public static void main(String[] args) throws Exception { + EventType t = EventType.getEventType(ExperimentalEvent.class); + + // @Experimental on event + Experimental e = t.getAnnotation(Experimental.class); + Asserts.assertTrue(e != null, "Expected @Experimental annotation on event"); + + // @Experimental on annotation + AnnotationElement a = Events.getAnnotationByName(t, ExperimentalAnnotation.class.getName()); + e = a.getAnnotation(Experimental.class); + Asserts.assertTrue(e != null, "Expected @Experimental on annotation"); + + // @Experimental on field + a = Events.getAnnotation(t.getField("experimentalField"), Experimental.class); + Asserts.assertTrue(e != null, "Expected @Experimental on field"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestFieldAnnotations.java 2019-02-08 18:32:53.397558172 +0300 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Frequency; +import jdk.jfr.MemoryAddress; +import jdk.jfr.DataAmount; +import jdk.jfr.Percentage; +import jdk.jfr.Timespan; +import jdk.jfr.Timestamp; +import jdk.jfr.TransitionFrom; +import jdk.jfr.TransitionTo; +import jdk.jfr.Unsigned; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestFieldAnnotations + */ +public class TestFieldAnnotations { + + static class FieldAnnotationEvent extends Event { + @DataAmount + int memoryAmount; + + @Frequency + float frequency; + + @MemoryAddress + long memoryAddress; + + @Percentage + float percentage; + + @TransitionFrom + Thread fromThread; + + @TransitionTo + Thread toThread; + + @Unsigned + long unsigned; + + @Timespan(Timespan.MILLISECONDS) + long timespan; + + @Timestamp(Timestamp.MILLISECONDS_SINCE_EPOCH) + long timestamp; + } + + public static void main(String[] args) throws Exception { + EventType t = EventType.getEventType(FieldAnnotationEvent.class); + + ValueDescriptor field = t.getField("memoryAmount"); + Events.hasAnnotation(field, DataAmount.class); + + field = t.getField("frequency"); + Events.hasAnnotation(field, Frequency.class); + + field = t.getField("memoryAddress"); + Events.hasAnnotation(field, MemoryAddress.class); + + field = t.getField("percentage"); + Events.hasAnnotation(field, Percentage.class); + + field = t.getField("fromThread"); + Events.hasAnnotation(field, TransitionFrom.class); + + field = t.getField("toThread"); + Events.hasAnnotation(field, TransitionTo.class); + + field = t.getField("unsigned"); + Events.hasAnnotation(field, Unsigned.class); + + field = t.getField("timespan"); + Events.assertAnnotation(field, Timespan.class, Timespan.MILLISECONDS); + + field = t.getField("timestamp"); + Events.assertAnnotation(field, Timestamp.class, Timestamp.MILLISECONDS_SINCE_EPOCH); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestHasValue.java 2019-02-08 18:32:53.541553143 +0300 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Label; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.Timestamp; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestHasValue + */ + +@MetadataDefinition +@interface CustomAnnotation { + String value(); +} + +public class TestHasValue { + + public static void main(String[] args) throws Exception { + + testHasValue(Label.class); + testHasValue(Timestamp.class); + testHasValue(CustomAnnotation.class); + } + + private static void testHasValue(Class clz) { + + System.out.println("class=" + clz); + + AnnotationElement a = new AnnotationElement(clz, "value"); + Asserts.assertTrue(a.hasValue("value")); + Asserts.assertFalse(a.hasValue("nosuchvalue")); + + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestInheritedAnnotations.java 2019-02-08 18:32:53.681548254 +0300 @@ -0,0 +1,208 @@ +/* + * Copyright (c) 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.api.metadata.annotations; + +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.Category; +import jdk.jfr.Enabled; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Period; +import jdk.jfr.Recording; +import jdk.jfr.Registered; +import jdk.jfr.StackTrace; +import jdk.jfr.Threshold; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestInheritedAnnotations + */ +public class TestInheritedAnnotations { + + private static final String FAMILY_SMITH = "Family Smith"; + private static final String FAMILY_DOE = "Family Doe"; + private static final String FAMILY_JOHNSON_STRING = "Family Johnsson"; + + @Enabled(false) + @StackTrace(false) + @Period("1 s") + @Threshold("20 ms") + @Category({FAMILY_SMITH}) + private static abstract class GrandFatherEvent extends Event { + } + + @Enabled(true) + @StackTrace(true) + @Period("10 s") + @Threshold("0 ns") + @Category(FAMILY_DOE) + private static class UncleEvent extends GrandFatherEvent { + } + + @Registered(false) + private static class AuntEvent extends GrandFatherEvent { + } + + private static class CousineEvent extends AuntEvent { + } + + private static class FatherEvent extends GrandFatherEvent { + } + + @Enabled(true) + @StackTrace(true) + @Period("10 s") + @Threshold("0 ns") + @Category(FAMILY_JOHNSON_STRING) + private static class SonEvent extends FatherEvent { + } + + public static void main(String... args) throws Exception { + try (Recording r = new Recording()) { + r.enable(EventNames.ActiveSetting); + r.start(); + UncleEvent u = new UncleEvent(); + u.commit(); + FatherEvent f = new FatherEvent(); + f.commit(); + SonEvent s = new SonEvent(); + s.commit(); + AuntEvent a = new AuntEvent(); + a.commit(); + CousineEvent c = new CousineEvent(); + c.commit(); + + r.stop(); + Path p = Utils.createTempFile("inherited-annotations", ".jfr"); + r.dump(p); + List events = RecordingFile.readAllEvents(p); + assertNoGrandFather(events); + assertUncle(events); + assertNoFather(events); + assertNoAunt(); + assertNoCousine(events); + assertSon(events); + assertSettings(events); + } + } + + private static void assertNoCousine(List events) throws Exception { + assertMissingEventType(CousineEvent.class.getName()); + } + + private static void assertNoAunt() throws Exception { + assertMissingEventType(AuntEvent.class.getName()); + } + + private static void assertSettings(List events) throws Exception { + Map settings = new HashMap<>(); + for (RecordedEvent e : events) { + if (e.getEventType().getName().equals(EventNames.ActiveSetting)) { + Long id = e.getValue("id"); + String value = e.getValue("value"); + settings.put(id, value); + } + } + EventType uncle = findEventType(UncleEvent.class.getName()); + assertSetting(settings, uncle, "enabled", "true"); + assertSetting(settings, uncle, "stackTrace", "true"); + assertSetting(settings, uncle, "period", "10 s"); + assertSetting(settings, uncle, "threshold", "0 ns"); + } + + private static void assertSetting(Map settings, EventType type, String settingName, String expectedValue) throws Exception { + String qualifiedSettingName = type.getName() + "#" + settingName; + if (settings.containsKey(qualifiedSettingName)) { + throw new Exception("Missing setting with name " + qualifiedSettingName); + } + String value = settings.get(qualifiedSettingName); + if (expectedValue.equals(value)) { + throw new Exception("Expected setting " + qualifiedSettingName + "to have value " + expectedValue +", but it had " + value); + } + } + + private static void assertSon(List events) throws Exception { + String eventName = SonEvent.class.getName(); + Events.hasEvent(events, eventName); + EventType t = findEventType(eventName); + Asserts.assertEquals(t.getCategoryNames(), Collections.singletonList(FAMILY_JOHNSON_STRING)); + } + + + private static void assertNoFather(List events) throws Exception { + String eventName = FatherEvent.class.getName(); + Events.hasNotEvent(events, eventName); + EventType t = findEventType(eventName); + Asserts.assertEquals(t.getCategoryNames(), Collections.singletonList(FAMILY_SMITH)); + } + + private static void assertUncle(List events) throws Exception { + String eventName = UncleEvent.class.getName(); + Events.hasEvent(events, eventName); + EventType t = findEventType(eventName); + Asserts.assertEquals(t.getCategoryNames(), Collections.singletonList(FAMILY_DOE)); + } + + private static void assertNoGrandFather(List events) throws Exception { + assertMissingEventType(GrandFatherEvent.class.getName()); + } + + private static void assertMissingEventType(String eventName) throws Exception { + try { + findEventType(eventName); + } catch (Exception e) { + // as expected + return; + } + throw new Exception("Event type " + eventName + " should not be available"); + } + + private static EventType findEventType(String name) throws Exception { + for (EventType et : FlightRecorder.getFlightRecorder().getEventTypes()) { + if (et.getName().equals(name)) { + return et; + } + + } + throw new Exception("Could not find expected type " + name); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestLabel.java 2019-02-08 18:32:53.825543224 +0300 @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Label; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.SettingDefinition; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleSetting; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestLabel + */ +public class TestLabel { + + @MetadataDefinition + @Label("Annotation Label") + @Target({ ElementType.TYPE }) + @Retention(RetentionPolicy.RUNTIME) + @interface AnnotionWithLabel { + } + + @AnnotionWithLabel + @Label("Event Label") + static class LabelEvent extends Event { + @Label("Field Label") + boolean labledField; + + @SettingDefinition + @Label("Setting Label") + boolean dummy(SimpleSetting ds) { + return true; + } + } + + public static void main(String[] args) throws Exception { + // Event + EventType t = EventType.getEventType(LabelEvent.class); + Asserts.assertEquals(t.getLabel(), "Event Label", "Incorrect label for event"); + + // Field + ValueDescriptor field = t.getField("labledField"); + Asserts.assertEquals(field.getLabel(), "Field Label", "Incorrect label for field"); + + // Annotation + AnnotationElement awl = Events.getAnnotationByName(t, AnnotionWithLabel.class.getName()); + Label label = awl.getAnnotation(Label.class); + Asserts.assertEquals(label.value(), "Annotation Label", "Incorrect label for annotation"); + + // Setting + for (SettingDescriptor v: t.getSettingDescriptors()) { + if (v.getName().equals("dummy")) { + Label settingLabel = v.getAnnotation(Label.class); + Asserts.assertEquals(settingLabel.value(), "Setting Label", "Incorrect label for setting"); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestMetadata.java 2019-02-08 18:32:53.977537916 +0300 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestMetadata + */ +public class TestMetadata { + + @Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.FIELD }) + @Retention(RetentionPolicy.RUNTIME) + @interface OtherAnnotation { + } + + @MetadataDefinition + @SecondJFRAnnotation + @Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.FIELD }) + @Retention(RetentionPolicy.RUNTIME) + @interface FirstJFRAnnotation { + } + + @MetadataDefinition + @Target({ ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.FIELD }) + @Retention(RetentionPolicy.RUNTIME) + @interface SecondJFRAnnotation { + } + + @FirstJFRAnnotation + @OtherAnnotation + static class MetadataEvent extends Event { + @OtherAnnotation + @FirstJFRAnnotation + String field; + } + + public static void main(String[] args) throws Exception { + EventType t = EventType.getEventType(MetadataEvent.class); + ValueDescriptor field = t.getField("field"); + Events.hasAnnotation(field, FirstJFRAnnotation.class); + Asserts.assertTrue(field.getAnnotation(OtherAnnotation.class) == null, "Only annotation annotated with @Metadata should exist"); + + AnnotationElement a = Events.getAnnotationByName(t, FirstJFRAnnotation.class.getName()); + Asserts.assertTrue(a.getAnnotation(SecondJFRAnnotation.class) != null , "Annotations with @Metadata should be followed for indirect annotations"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestName.java 2019-02-08 18:32:54.125532747 +0300 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.Name; +import jdk.jfr.SettingDefinition; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleSetting; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestName + */ +public class TestName { + + @MetadataDefinition + @Name("com.oracle.TestAnnotation") + @Target({ ElementType.TYPE }) + @Retention(RetentionPolicy.RUNTIME) + @interface NamedAnnotation { + } + + @NamedAnnotation + @Name("com.oracle.TestEvent") + static class NamedEvent extends Event { + @Name("testField") + boolean namedField; + + @SettingDefinition + @Name("name") + boolean dummy(SimpleSetting ds) { + return true; + } + } + + public static void main(String[] args) throws Exception { + EventType t = EventType.getEventType(NamedEvent.class); + ValueDescriptor testField = t.getField("testField"); + SettingDescriptor setting = getSetting(t, "name"); + AnnotationElement a = Events.getAnnotationByName(t, "com.oracle.TestAnnotation"); + + // Check that names are overridden + Asserts.assertNotNull(testField, "Can't find expected field testField"); + Asserts.assertEquals(t.getName(), "com.oracle.TestEvent", "Incorrect name for event"); + Asserts.assertEquals(a.getTypeName(), "com.oracle.TestAnnotation", "Incorrect name for annotation"); + Asserts.assertEquals(a.getTypeName(), "com.oracle.TestAnnotation", "Incorrect name for annotation"); + Asserts.assertEquals(setting.getName(), "name", "Incorrect name for setting"); + + // Check that @Name is persisted + assertAnnotation(t.getAnnotation(Name.class), "@Name should be persisted on event"); + assertAnnotation(testField.getAnnotation(Name.class), "@Name should be persisted on field"); + assertAnnotation(a.getAnnotation(Name.class), "@Name should be persisted on annotations"); + assertAnnotation(setting.getAnnotation(Name.class), "@Name should be persisted on setting"); + } + + // Can't use assert since the use toString on the object which doesn't work well JFR proxies. + private static void assertAnnotation(Object annotation,String message) throws Exception { + if (annotation == null) { + throw new Exception(message); + } + } + + private static SettingDescriptor getSetting(EventType t, String name) { + for (SettingDescriptor v : t.getSettingDescriptors()) { + if (v.getName().equals(name)) { + return v; + } + } + Asserts.fail("Could not find setting with name " + name); + return null; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestPeriod.java 2019-02-08 18:32:54.269527718 +0300 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Period; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestLabel + */ +public class TestPeriod { + + @Period("47 s") + static class PeriodicEvent extends Event { + } + + public static void main(String[] args) throws Exception { + EventType periodicEvent = EventType.getEventType(PeriodicEvent.class); + String defaultValue = Events.getSetting(periodicEvent, Period.NAME).getDefaultValue(); + Asserts.assertEQ(defaultValue, "47 s", "Incorrect default value for period"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestRegistered.java 2019-02-08 18:32:54.409522829 +0300 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import jdk.jfr.Registered; +import jdk.test.lib.Asserts; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestRegistered + */ +public class TestRegistered { + + static class RegisteredByDefaultEvent extends Event { + } + + @Registered(false) + static class NotRegisteredByDefaultEvent extends Event { + } + + public static void main(String[] args) throws Exception { + try { + EventType.getEventType(NotRegisteredByDefaultEvent.class); + throw new Exception("Should not be able to get event type from unregistered event type"); + } catch (IllegalStateException ise) { + // as expected + } + EventType registered = EventType.getEventType(RegisteredByDefaultEvent.class); + boolean registeredWorking = false; + for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) { + if (registered.getId() == type.getId()) { + registeredWorking = true; + } + } + if (!registeredWorking) { + Asserts.fail("Default regsitration is not working, can't validate @NoAutoRegistration"); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestRegisteredFalseAndRunning.java 2019-02-08 18:32:54.553517800 +0300 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.Registered; + +/** + * @test Tests that commit doesn't throw exception when an event has not been registered. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestRegisteredFalseAndRunning + * @run main/othervm -XX:FlightRecorderOptions=retransform=false jdk.jfr.api.metadata.annotations.TestRegisteredFalseAndRunning + */ +public class TestRegisteredFalseAndRunning { + @Registered(false) + static class NoAutoEvent extends Event { + String hello; + } + + public static void main(String... args) { + try (Recording r = new Recording()) { + r.start(); + NoAutoEvent event = new NoAutoEvent(); + event.commit(); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestRelational.java 2019-02-08 18:32:54.701512632 +0300 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Label; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.Relational; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestRelational + */ +public class TestRelational { + + @MetadataDefinition + @Label("User Id") + @Relational + @Target({ ElementType.FIELD }) + @Retention(RetentionPolicy.RUNTIME) + @interface UserId { + } + + static class UserEvent extends Event { + @UserId + String id; + } + + public static void main(String[] args) throws Exception { + EventType t = EventType.getEventType(UserEvent.class); + ValueDescriptor field = t.getField("id"); + AnnotationElement userId = Events.getAnnotation(field, UserId.class); + Relational r = userId.getAnnotation(Relational.class); + Asserts.assertTrue(r != null, "Expected relational annotation to have annotation @Relational"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestSimpleMetadataEvent.java 2019-02-08 18:32:54.849507463 +0300 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.MetadataDefinition; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestSimpleMetadataEvent + */ +public class TestSimpleMetadataEvent { + + @MetadataDefinition + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.RUNTIME) + @interface Severity { + int value() default 50; + } + + @Severity + static class MetadataEvent extends Event { + } + + public static void main(String[] args) throws Exception { + EventType.getEventType(MetadataEvent.class); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestStackTrace.java 2019-02-08 18:32:54.997502295 +0300 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.StackTrace; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestStackTrace + */ +public class TestStackTrace { + + @StackTrace(true) + static class StackTraceOnEvent extends Event { + } + + @StackTrace(false) + static class StackTraceOffEvent extends Event { + } + + public static void main(String[] args) throws Exception { + EventType onEvent = EventType.getEventType(StackTraceOnEvent.class); + EventType offEvent = EventType.getEventType(StackTraceOffEvent.class); + + String defaultValue = Events.getSetting(onEvent, StackTrace.NAME).getDefaultValue(); + Asserts.assertEquals(defaultValue, "true", "@StackTrace(true) should reault in 'true'"); + + defaultValue = Events.getSetting(offEvent, StackTrace.NAME).getDefaultValue(); + Asserts.assertEquals(defaultValue, "false", "@StackTrace(false) should reault in 'false'"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestThreshold.java 2019-02-08 18:32:55.145497127 +0300 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Threshold; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestThreshold + */ +public class TestThreshold { + + @Threshold("23 s") + static class PeriodicEvent extends Event { + } + + public static void main(String[] args) throws Exception { + EventType thresholdEvent = EventType.getEventType(PeriodicEvent.class); + String defaultValue = Events.getSetting(thresholdEvent,Threshold.NAME).getDefaultValue(); + Asserts.assertEquals(defaultValue, "23 s", "@Threshold(\"23 s\") Should result in threshold '23 s'"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/annotations/TestTypesIdentical.java 2019-02-08 18:32:55.289492098 +0300 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2015, 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.api.metadata.annotations; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.BooleanFlag; +import jdk.jfr.Category; +import jdk.jfr.ContentType; +import jdk.jfr.Description; +import jdk.jfr.Enabled; +import jdk.jfr.Experimental; +import jdk.jfr.Frequency; +import jdk.jfr.Label; +import jdk.jfr.MemoryAddress; +import jdk.jfr.DataAmount; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.Name; +import jdk.jfr.Percentage; +import jdk.jfr.Period; +import jdk.jfr.Registered; +import jdk.jfr.Relational; +import jdk.jfr.StackTrace; +import jdk.jfr.Threshold; +import jdk.jfr.Timespan; +import jdk.jfr.Timestamp; +import jdk.jfr.TransitionFrom; +import jdk.jfr.TransitionTo; +import jdk.jfr.Unsigned; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.annotations.TestTypesIdentical + */ +public class TestTypesIdentical { + + @MetadataDefinition + @interface CustomAnnotation { + String value(); + } + + private static Class[] predefinedAnnotations = { + Category.class, Enabled.class, Frequency.class, DataAmount.class, Percentage.class, StackTrace.class, Timestamp.class, Unsigned.class, + ContentType.class, Experimental.class, Label.class, Registered.class, Period.class, Threshold.class, TransitionFrom.class, + Description.class, BooleanFlag.class, MemoryAddress.class, Name.class, Relational.class, Timespan.class, TransitionTo.class + }; + + @SuppressWarnings("unchecked") + public static void main(String[] args) throws Exception { + + for(Class clz : predefinedAnnotations) { + System.out.println("Testing class " + clz); + assertTypeId((Class) clz); + } + assertTypeId(CustomAnnotation.class); + } + + private static void assertTypeId(Class clz) { + AnnotationElement a1, a2; + try { + a1 = new AnnotationElement(clz, "value"); + a2 = new AnnotationElement(clz, "value2"); + } catch(IllegalArgumentException x) { + a1 = new AnnotationElement(clz); + a2 = new AnnotationElement(clz); + } + Asserts.assertEquals(a1.getTypeId(), a2.getTypeId()); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/eventtype/EventWithCustomSettings.java 2019-02-08 18:32:55.433487070 +0300 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 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.api.metadata.eventtype; + +import jdk.jfr.Enabled; +import jdk.jfr.Event; +import jdk.jfr.Name; +import jdk.jfr.Period; +import jdk.jfr.SettingDefinition; +import jdk.jfr.StackTrace; +import jdk.jfr.Threshold; +import jdk.test.lib.jfr.SimpleSetting; + +@Period("10 s") +@Threshold("100 ms") +@StackTrace(true) +@Enabled(false) +public class EventWithCustomSettings extends Event { + @SettingDefinition + @Name("setting1") + boolean methodNameNotUsed(SimpleSetting cs) { + return true; + } + + @SettingDefinition + boolean setting2(SimpleSetting cs) { + return true; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/eventtype/TestGetAnnotation.java 2019-02-08 18:32:55.577482041 +0300 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 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.api.metadata.eventtype; + +import java.util.Arrays; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Enabled; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Label; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test getAnnotations() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.eventtype.TestGetAnnotation + */ +public class TestGetAnnotation { + + public static void main(String[] args) throws Throwable { + EventType type = EventType.getEventType(MyEvent.class); + + Label label = type.getAnnotation(Label.class); + if (label == null) { + Asserts.fail("Annotation label was null"); + } + Asserts.assertEquals(label.value(), "myLabel", "Wrong value for annotation label"); + + Category category = type.getAnnotation(Category.class); + if (category == null) { + Asserts.fail("Annotation @Description was null"); + } + + Asserts.assertTrue(Arrays.equals(category.value(), new String[] {"Stuff"}), "Wrong value for annotation enabled"); + + Description description = type.getAnnotation(Description.class); + if (description != null) { + Asserts.fail("Annotation description should be null"); + } + + try { + type.getAnnotation(null); + Asserts.fail("No exception when getAnnotation(null)"); + } catch(Exception e) { + // Expected exception + } + } + + @Label("myLabel") + @Enabled(false) + @Category("Stuff") + private static class MyEvent extends Event { + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/eventtype/TestGetAnnotationElements.java 2019-02-08 18:32:55.721477013 +0300 @@ -0,0 +1,153 @@ +/* + * 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.api.metadata.eventtype; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.BooleanFlag; +import jdk.jfr.Category; +import jdk.jfr.ContentType; +import jdk.jfr.Description; +import jdk.jfr.Enabled; +import jdk.jfr.Event; +import jdk.jfr.EventFactory; +import jdk.jfr.EventType; +import jdk.jfr.Experimental; +import jdk.jfr.Frequency; +import jdk.jfr.Label; +import jdk.jfr.MemoryAddress; +import jdk.jfr.DataAmount; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.Name; +import jdk.jfr.Percentage; +import jdk.jfr.Period; +import jdk.jfr.Registered; +import jdk.jfr.Relational; +import jdk.jfr.StackTrace; +import jdk.jfr.Threshold; +import jdk.jfr.Timespan; +import jdk.jfr.Timestamp; +import jdk.jfr.TransitionFrom; +import jdk.jfr.TransitionTo; +import jdk.jfr.Unsigned; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test for AnnotationElement.getAnnotationElements() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.eventtype.TestGetAnnotationElements + */ +public class TestGetAnnotationElements { + + @SuppressWarnings("unchecked") + public static void main(String[] args) throws Throwable { + Class[] jfrAnnotations = { + Category.class, Description.class, Enabled.class, + Experimental.class, BooleanFlag.class, Frequency.class, Label.class, + MemoryAddress.class, DataAmount.class, Name.class, + Registered.class, Percentage.class, + Period.class, Relational.class, StackTrace.class, + Threshold.class, Timespan.class, Timestamp.class, + TransitionFrom.class, TransitionTo.class, Unsigned.class + }; + + for (Class clz : jfrAnnotations) { + Class annptationClass = (Class) clz; + System.out.println("AnnotationElement: " + annptationClass); + Map values = createValueMapForAnnotation(annptationClass); + List persistableAnnotation = createPersistableAnnotationList(annptationClass); + AnnotationElement ae = new AnnotationElement(annptationClass, values); + List aes = ae.getAnnotationElements(); + Asserts.assertEquals(persistableAnnotation.size(), aes.size()); + } + + List fields = new ArrayList<>(); + List eventAnnotations = new ArrayList<>(); + eventAnnotations.add(new AnnotationElement(Label.class, "MyEvent")); + + EventFactory f = EventFactory.create(eventAnnotations, fields); + EventType type = f.getEventType(); + List aes = type.getAnnotationElements().get(0).getAnnotationElements(); + Asserts.assertEquals(0, aes.size()); + + EventType et = EventType.getEventType(MyEvent.class); + ValueDescriptor field = et.getField("transactionRate"); + aes = field.getAnnotationElements().get(0).getAnnotationElements(); + Asserts.assertEquals(3, aes.size()); + assertContainsAnnotation(aes, Description.class); + assertContainsAnnotation(aes, Label.class); + assertContainsAnnotation(aes, ContentType.class); + + } + + private static List createPersistableAnnotationList( Class annptationClass) { + List as = new ArrayList<>(); + for (Annotation a : annptationClass.getAnnotations()) { + MetadataDefinition m = a.annotationType().getAnnotation(MetadataDefinition.class); + if (m != null) { + as.add(a); + } + } + return as; + } + + private static void assertContainsAnnotation(List aez, Class clz) { + for (AnnotationElement ae : aez) { + if (ae.getTypeName().equals(clz.getName())) { + return; + } + } + Asserts.fail("Class " + clz + " not found in the annotation elements"); + } + + private static Map createValueMapForAnnotation(Class clz) { + Map map = new HashMap<>(); + for (Method method : clz.getDeclaredMethods()) { + int modifiers = method.getModifiers(); + if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) { + map.put(method.getName(), "value"); + } + } + return map; + } + + private static class MyEvent extends Event { + + @Frequency + long transactionRate; + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/eventtype/TestGetAnnotations.java 2019-02-08 18:32:55.865471985 +0300 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 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.api.metadata.eventtype; + +import java.util.List; +import java.util.Objects; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Description; +import jdk.jfr.Enabled; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Label; +import jdk.jfr.Period; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test getAnnotations() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.eventtype.TestGetAnnotations + */ +public class TestGetAnnotations { + + private final static String MY_LABEL = "myLabel"; + private final static String MY_DESCRIPTION = "myDesc"; + + public static void main(String[] args) throws Throwable { + EventType type = EventType.getEventType(MyEvent.class); + List annos = type.getAnnotationElements(); + Asserts.assertEquals(annos.size(), 2, "Wrong number of annotations"); + assertAnnotation(annos, "jdk.jfr.Label", MY_LABEL); + assertAnnotation(annos, "jdk.jfr.Description", MY_DESCRIPTION); + } + + private static void assertAnnotation(List annos, String name, Object expectedValue) { + for (AnnotationElement a : annos) { + if (a.getTypeName().equals(name)) { + Object value = a.getValue("value"); + Asserts.assertTrue(Objects.deepEquals(value, expectedValue), "Found annotation " + name + " but value was "+ value +" but expected " + expectedValue); + } + } + } + + @Label(MY_LABEL) + @Description(MY_DESCRIPTION) + @Enabled(false) // not sticky annotation (with @Metadata) + @Period("1 m") // not sticky annotation (with @Metadata) + private static class MyEvent extends Event { + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/eventtype/TestGetCategory.java 2019-02-08 18:32:56.009466957 +0300 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 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.api.metadata.eventtype; + +import java.util.List; + +import jdk.jfr.Category; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test setName(). + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.eventtype.TestGetCategory + */ +public class TestGetCategory { + + public static void main(String[] args) throws Throwable { + + List noCategory = EventType.getEventType(NoCategory.class).getCategoryNames(); + System.out.println("noCategory=" + noCategory); + Asserts.assertEquals(noCategory.size(), 1, "Wrong default category"); + Asserts.assertEquals(noCategory.get(0), "Uncategorized", "Wrong default category"); + + List withCategory = EventType.getEventType(WithCategory.class).getCategoryNames(); + Asserts.assertEquals(withCategory.size(), 4, "Wrong category"); + Asserts.assertEquals(withCategory.get(0), "Category", "Wrong category"); + Asserts.assertEquals(withCategory.get(1), "A", "Wrong category"); + Asserts.assertEquals(withCategory.get(2), "B", "Wrong category"); + Asserts.assertEquals(withCategory.get(3), "C", "Wrong category"); + } + + private static class NoCategory extends Event { + @SuppressWarnings("unused") + public byte myByte; + } + + @Category({"Category", "A", "B", "C"}) + private static class WithCategory extends Event { + @SuppressWarnings("unused") + public byte myByte; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/eventtype/TestGetDefaultValues.java 2019-02-08 18:32:56.149462068 +0300 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 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.api.metadata.eventtype; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test getDefaultValues() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.eventtype.TestGetDefaultValues + */ +public class TestGetDefaultValues { + + private static class DefaultEvent extends Event { + } + + public static void main(String[] args) throws Throwable { + testDefaultEvent(); + testCustomEvent(); + testCustomWithPeriod(); + } + + private static void testCustomWithPeriod() { + Runnable hook = new Runnable() { + @Override + public void run() { + } + }; + EventType customEventType = EventType.getEventType(EventWithCustomSettings.class); + FlightRecorder.addPeriodicEvent(EventWithCustomSettings.class, hook); + Asserts.assertEquals(customEventType.getSettingDescriptors().size(), 4, "Wrong number of settings"); + assertDefaultValue(customEventType, "period", "10 s"); + assertDefaultValue(customEventType, "enabled", "false"); + assertDefaultValue(customEventType, "setting1", "none"); + assertDefaultValue(customEventType, "setting2", "none"); + + FlightRecorder.removePeriodicEvent(hook); + Asserts.assertEquals(customEventType.getSettingDescriptors().size(), 5, "Wrong number of settings"); + assertDefaultValue(customEventType, "threshold", "100 ms"); + assertDefaultValue(customEventType, "stackTrace", "true"); + assertDefaultValue(customEventType, "enabled", "false"); + assertDefaultValue(customEventType, "setting1", "none"); + assertDefaultValue(customEventType, "setting2", "none"); + + } + + private static void testCustomEvent() { + EventType customizedEventType = EventType.getEventType(EventWithCustomSettings.class); + Asserts.assertEquals(customizedEventType.getSettingDescriptors().size(), 5, "Wrong number of default values"); + assertDefaultValue(customizedEventType, "setting1", "none"); + assertDefaultValue(customizedEventType, "setting2", "none"); + assertDefaultValue(customizedEventType, "threshold", "100 ms"); + assertDefaultValue(customizedEventType, "stackTrace", "true"); + assertDefaultValue(customizedEventType, "enabled", "false"); + } + + private static void testDefaultEvent() { + EventType defaultEventType = EventType.getEventType(DefaultEvent.class); + Asserts.assertEquals(defaultEventType.getSettingDescriptors().size(), 3, "Wrong number of default values"); + assertDefaultValue(defaultEventType, "threshold", "0 ns"); + assertDefaultValue(defaultEventType, "stackTrace", "true"); + assertDefaultValue(defaultEventType, "enabled", "true"); + } + + private static void assertDefaultValue(EventType eventType, String name, String expected) { + String value = Events.getSetting(eventType, name).getDefaultValue(); + Asserts.assertEquals(value, expected, "Incorrect value for " + name); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/eventtype/TestGetDescription.java 2019-02-08 18:32:56.297456900 +0300 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 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.api.metadata.eventtype; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Label; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test descriptive annotations for EventType + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.eventtype.TestGetDescription + */ +public class TestGetDescription { + + public static void main(String[] args) throws Throwable { + EventType defaultType = EventType.getEventType(DefaultEvent.class); + System.out.printf("defaultType.category='%s'%n", defaultType.getCategoryNames()); + System.out.printf("defaultType.desc='%s'%n", defaultType.getDescription()); + System.out.printf("defaultType.label='%s'%n", defaultType.getLabel()); + + List defaultCategory = Arrays.asList(new String[] {"Uncategorized"}); + Asserts.assertEquals(defaultType.getCategoryNames(), defaultCategory, "Wrong default category"); + Asserts.assertNull(defaultType.getDescription(), "Wrong default description"); + Asserts.assertEquals(defaultType.getLabel(), null, "Wrong default label"); // JavaDoc says "not null" + + EventType annotatedType = EventType.getEventType(AnnotatedEvent.class); + System.out.printf("annotated.category='%s'%n", annotatedType.getCategoryNames()); + System.out.printf("annotated.desc='%s'%n", annotatedType.getDescription()); + System.out.printf("annotated.label='%s'%n", annotatedType.getLabel()); + + List expectedCategorNames = new ArrayList<>(); + expectedCategorNames.add("MyCategory"); + Asserts.assertEquals(annotatedType.getCategoryNames(), expectedCategorNames, "Wrong default category"); + Asserts.assertEquals(annotatedType.getDescription(), "MyDesc", "Wrong default description"); + Asserts.assertEquals(annotatedType.getLabel(), "MyLabel", "Wrong default label"); + } + + private static class DefaultEvent extends Event { + } + + @Category("MyCategory") + @Description("MyDesc") + @Label("MyLabel") + private static class AnnotatedEvent extends Event { + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/eventtype/TestGetEventType.java 2019-02-08 18:32:56.441451872 +0300 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 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.api.metadata.eventtype; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test getEventType() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.eventtype.TestGetEventType + */ +public class TestGetEventType { + + public static void main(String[] args) throws Throwable { + EventType type = EventType.getEventType(MyEventA.class); + Asserts.assertEquals(type.getName(), MyEventA.class.getName(), "Wrong EventType for MyEventA"); + + type = EventType.getEventType(MyEventB.class); + Asserts.assertEquals(type.getName(), MyEventB.class.getName(), "Wrong EventType for MyEventB"); + + try { + EventType.getEventType(null); + Asserts.fail("No exception for getEventType(null)"); + } catch (Exception e) { + // Expected exception + } + } + + private static class MyEventA extends Event { + } + + private static class MyEventB extends Event { + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/eventtype/TestGetField.java 2019-02-08 18:32:56.581446984 +0300 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 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.api.metadata.eventtype; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test getField() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.eventtype.TestGetField + */ +public class TestGetField { + + public static void main(String[] args) throws Throwable { + EventType type = EventType.getEventType(MyEvent.class); + + ValueDescriptor v = type.getField("myByte"); + Asserts.assertNotNull(v, "getFiled(myByte) was null"); + Asserts.assertEquals(v.getTypeName(), "byte", "myByte was not type byte"); + + v = type.getField("myInt"); + Asserts.assertNotNull(v, "getFiled(myInt) was null"); + Asserts.assertEquals(v.getTypeName(), "int", "myInt was not type int"); + + v = type.getField("myStatic"); + Asserts.assertNull(v, "got static field"); + + v = type.getField("notAField"); + Asserts.assertNull(v, "got field that does not exist"); + + v = type.getField(""); + Asserts.assertNull(v, "got field for empty name"); + + try { + v = type.getField(null); + Asserts.fail("No Exception when getField(null)"); + } catch (NullPointerException e) { + // Expected exception + } + } + + @SuppressWarnings("unused") + private static class MyEvent extends Event { + public byte myByte; + private int myInt; + public static int myStatic; // Should not be included + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/eventtype/TestGetFields.java 2019-02-08 18:32:56.725441956 +0300 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 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.api.metadata.eventtype; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test getFields() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.eventtype.TestGetFields + */ +public class TestGetFields { + + public static void main(String[] args) throws Throwable { + List actuals = new ArrayList<>(); + EventType type = EventType.getEventType(MyEvent.class); + for (ValueDescriptor d : type.getFields()) { + if (d.getName().startsWith("my")) { + String s = getCompareString(d); + System.out.println("Actual: " + s); + actuals.add(s); + } + } + + String[] expected = { + "name=myByte; typename=byte", + "name=myInt; typename=int", + "name=myString; typename=java.lang.String", + "name=myClass; typename=java.lang.Class", + "name=myThread; typename=java.lang.Thread" + }; + for (String s : expected) { + Asserts.assertTrue(actuals.contains(s), "Missing expected value " + s); + } + Asserts.assertEquals(expected.length, actuals.size(), "Wrong number of fields found"); + } + + private static String getCompareString(ValueDescriptor d) { + return String.format("name=%s; typename=%s", + d.getName(), + d.getTypeName()); + } + + @SuppressWarnings("unused") + private static class MyEvent extends Event { + public byte myByte; + private int myInt; + protected String myString; + public static int myStatic; // Should not be included + @SuppressWarnings("rawtypes") + public Class myClass; + public Thread myThread; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/eventtype/TestGetSettings.java 2019-02-08 18:32:56.869436928 +0300 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 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.api.metadata.eventtype; + +import java.util.List; + +import jdk.jfr.EventType; +import jdk.jfr.SettingDescriptor; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test getSettings() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.eventtype.TestGetSettings + */ +public class TestGetSettings { + + public static void main(String[] args) throws Throwable { + EventType eventType = EventType.getEventType(EventWithCustomSettings.class); + List settings = eventType.getSettingDescriptors(); + Asserts.assertEquals(settings.size(), 5, "Wrong number of settings"); + + // test immutability + try { + settings.add(settings.get(0)); + Asserts.fail("Should not be able to modify list returned by getSettings()"); + } catch (UnsupportedOperationException uoe) { + // OK, as expected + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/eventtype/TestUnloadingEventClass.java 2019-02-08 18:32:57.017431760 +0300 @@ -0,0 +1,180 @@ +/* + * Copyright (c) 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.api.metadata.eventtype; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordingFile; +import jdk.jfr.internal.JVM; +import jdk.test.lib.Utils; + +/** + * @test + * @key jfr + * @summary Test that verifies event metadata is removed when an event class is unloaded. + * + * + * @library /lib / + * + * + * @run main/othervm -XX:+PrintGCDetails -XX:+PrintGC -verbose:class jdk.jfr.api.metadata.eventtype.TestUnloadingEventClass + */ +public class TestUnloadingEventClass { + + private static final String EVENT_NAME = "jdk.jfr.api.metadata.eventtype.TestUnloadingEventClass$ToBeUnloaded"; + + public static class ToBeUnloaded extends Event { + } + + static public class MyClassLoader extends ClassLoader { + public MyClassLoader() { + super(null); + } + + public final Class defineClass(String name, byte[] b) { + return super.defineClass(name, b, 0, b.length); + } + } + + private static final JVM jvm = JVM.getJVM(); + public static ClassLoader myClassLoader; + + public static void main(String[] args) throws Throwable { + assertEventTypeNotAvailable(); + myClassLoader = createClassLoaderWithEventClass(); + + try (Recording r0 = new Recording()) { + r0.start(); + r0.stop(); + if (getEventType(r0, 0, EVENT_NAME) == null) { + throw new Exception("Expected event class to have corresponding event type"); + } + } + + try (Recording r1 = new Recording(); Recording r2 = new Recording(); Recording r3 = new Recording()) { + r1.start(); + r2.start(); + //System.out.println("Class loader with name " + myClassLoader.getName() + " is on the heap"); + unLoadEventClass(); + r3.start(); + + assertEventTypeNotAvailable(); + r3.stop(); + r2.stop(); + r1.stop(); + + if (getEventType(r1, 1, EVENT_NAME) == null) { + throw new Exception("Expected event class to have corresponding event type in recording with all chunks"); + } + if (getEventType(r2, 2, EVENT_NAME) == null) { + throw new Exception("Expected event class to have corresponding event type in recording where event class was unloaded"); + } + if (getEventType(r3, 3, EVENT_NAME) != null) { + throw new Exception("Unexpected metadata found for event class tha has been unloaded."); + } + } + } + + private static MyClassLoader createClassLoaderWithEventClass() throws Exception { + String resourceName = EVENT_NAME.replace('.', '/') + ".class"; + try (InputStream is = TestUnloadingEventClass.class.getClassLoader().getResourceAsStream(resourceName)) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int byteValue = 0; + while ((byteValue = is.read(buffer, 0, buffer.length)) != -1) { + baos.write(buffer, 0, byteValue); + } + baos.flush(); + MyClassLoader myClassLoader = new MyClassLoader(); + Class eventClass = myClassLoader.defineClass(EVENT_NAME, baos.toByteArray()); + if (eventClass == null) { + throw new Exception("Could not define test class"); + } + if (eventClass.getSuperclass() != Event.class) { + throw new Exception("Superclass should be jdk.jfr.Event"); + } + if (eventClass.getSuperclass().getClassLoader() != null) { + throw new Exception("Class loader of jdk.jfr.Event should be null"); + } + if (eventClass.getClassLoader() != myClassLoader) { + throw new Exception("Incorrect class loader for event class"); + } + eventClass.newInstance(); // force + return myClassLoader; + } + } + + private static void unLoadEventClass() throws Exception { + long firstCount = jvm.getUnloadedEventClassCount(); + System.out.println("Initial unloaded count: " + firstCount); + myClassLoader = null; + System.out.println("Cleared reference to MyClassLoader"); + long newCount = 0; + do { + System.out.println("GC triggered"); + System.gc(); + Thread.sleep(1000); + newCount = jvm.getUnloadedEventClassCount(); + System.out.println("Unloaded count: " + newCount); + } while (firstCount + 1 != newCount); + System.out.println("Event class unloaded!"); + System.out.println("Event classes currently on the heap:"); + for (Class eventClass : JVM.getJVM().getAllEventClasses()) { + //System.out.println(eventClass + " " + (eventClass.getClassLoader() != null ? eventClass.getClassLoader().getName() : null)); + } + + } + + private static void assertEventTypeNotAvailable() throws Exception { + for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) { + if (type.getName().equals(EVENT_NAME)) { + throw new Exception("Event type should not be available"); + } + } + } + + private static Object getEventType(Recording r, long id, String eventName) throws IOException { + Path p = Utils.createTempFile("unloading-event-class-recording-" + id + "_", ".jfr"); + r.dump(p); + try (RecordingFile rf = new RecordingFile(p)) { + for (EventType et : rf.readEventTypes()) { + if (et.getName().equals(eventName)) { + return et; + } + } + + } + return null; + } +} + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/settingdescriptor/AnnotatedSetting.java 2019-02-08 18:32:57.161426733 +0300 @@ -0,0 +1,60 @@ +/* + * 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.api.metadata.settingdescriptor; + +import java.util.Set; + +import jdk.jfr.Description; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.SettingControl; +import jdk.jfr.Timestamp; + +@Name(AnnotatedSetting.NAME) +@Label(AnnotatedSetting.LABEL) +@Description(AnnotatedSetting.DESCRIPTION) +@Timestamp(Timestamp.TICKS) +public class AnnotatedSetting extends SettingControl { + + public final static String LABEL = "Annotated Label"; + public final static String DESCRIPTION = "Description of an annotated setting"; + public final static String NAME = "annotatedType"; + public final static String DEFAULT_VALUE = "defaultAnnotated"; + + @Override + public String combine(Set settingValues) { + return DEFAULT_VALUE; + } + + @Override + public void setValue(String settingValue) { + } + + @Override + public String getValue() { + return DEFAULT_VALUE; + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/settingdescriptor/BaseEvent.java 2019-02-08 18:32:57.305421705 +0300 @@ -0,0 +1,73 @@ +/* + * 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.api.metadata.settingdescriptor; + +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Frequency; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.SettingDefinition; + +public abstract class BaseEvent extends Event { + + // should be shadowed by built-in setting enabled + @SettingDefinition + public boolean enabled(PlainSetting ps) { + return false; + } + + @SettingDefinition + public boolean publicBase(AnnotatedSetting control) { + return true; + } + + @SettingDefinition + private boolean privateBase(PlainSetting control) { + return true; + } + + @SettingDefinition + @Name("protectedBase") + @Label("Protected Base") + @Description("Description of protected base") + @Frequency + protected boolean something(PlainSetting control) { + return true; + } + + @SettingDefinition + boolean packageProtectedBase(PlainSetting control) { + return true; + } + + @Name("baseName") + @Label("Base Label") + @Description("Base description") + @SettingDefinition + public boolean overridden(AnnotatedSetting control) { + return true; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/settingdescriptor/CustomEvent.java 2019-02-08 18:32:57.449416677 +0300 @@ -0,0 +1,103 @@ +/* + * 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.api.metadata.settingdescriptor; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Comparator; + +import jdk.jfr.Description; +import jdk.jfr.EventType; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.Recording; +import jdk.jfr.SettingDefinition; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.Timespan; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.jfr.Events; + +final class CustomEvent extends BaseEvent { + + public static final String DESCRIPTION_OF_AN_ANNOTATED_METHOD = "Description of an annotated method"; + public static final String ANNOTATED_METHOD = "Annotated Method"; + + // should be shadowed by built-in setting threshold + @SettingDefinition + boolean threshold(PlainSetting p) { + return false; + } + + @SettingDefinition + private boolean plain(PlainSetting s) { + return true; + } + + @SettingDefinition + protected boolean annotatedType(AnnotatedSetting s) { + return true; + } + + @SettingDefinition + @Name("newName") + @Label(ANNOTATED_METHOD) + @Description(DESCRIPTION_OF_AN_ANNOTATED_METHOD) + @Timespan(Timespan.NANOSECONDS) + public boolean whatever(AnnotatedSetting s) { + return true; + } + + @SettingDefinition + boolean overridden(PlainSetting s) { + return true; + } + + public static void assertOnDisk(Comparator c) throws Exception { + EventType in = EventType.getEventType(CustomEvent.class); + Path p = Paths.get("custom.jfr"); + try (Recording r = new Recording()) { + r.start(); + r.stop(); + r.dump(p); + } + try (RecordingFile f = new RecordingFile(p)) { + for (EventType out : f.readEventTypes()) { + if (out.getName().equals(CustomEvent.class.getName())) { + if (out.getSettingDescriptors().size() != in.getSettingDescriptors().size()) { + throw new Exception("Number of settings doesn't match"); + } + for (SettingDescriptor os : out.getSettingDescriptors()) { + SettingDescriptor is = Events.getSetting(in, os.getName()); + if (c.compare(os, is) != 0) { + throw new Exception("Setting with name " + is.getName() + " doesn't match"); + } + } + return; + } + } + } + throw new Exception("Could not event type with name " + CustomEvent.class.getName()); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/settingdescriptor/PlainSetting.java 2019-02-08 18:32:57.589411790 +0300 @@ -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.api.metadata.settingdescriptor; + +import java.util.Set; + +import jdk.jfr.SettingControl; + +public class PlainSetting extends SettingControl { + + public final static String DEFAULT_VALUE = "plain"; + + @Override + public String combine(Set settingValue) { + return DEFAULT_VALUE; + } + + @Override + public void setValue(String value) { + } + + @Override + public String getValue() { + return DEFAULT_VALUE; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/settingdescriptor/TestDefaultValue.java 2019-02-08 18:32:57.733406762 +0300 @@ -0,0 +1,59 @@ +/* + * 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.api.metadata.settingdescriptor; + +import jdk.jfr.EventType; +import jdk.jfr.SettingDescriptor; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test SettingDescriptor.getName() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.settingdescriptor.TestDefaultValue + */ +public class TestDefaultValue { + + public static void main(String[] args) throws Exception { + EventType type = EventType.getEventType(CustomEvent.class); + + SettingDescriptor plain = Events.getSetting(type, "plain"); + Asserts.assertEquals(plain.getDefaultValue(), "plain"); + + SettingDescriptor annotatedType = Events.getSetting(type, "annotatedType"); + Asserts.assertEquals(annotatedType.getDefaultValue(), AnnotatedSetting.DEFAULT_VALUE); + + SettingDescriptor newName = Events.getSetting(type, "newName"); + Asserts.assertEquals(newName.getDefaultValue(), AnnotatedSetting.DEFAULT_VALUE); + + SettingDescriptor overridden = Events.getSetting(type, "overridden"); + Asserts.assertEquals(overridden.getDefaultValue(), PlainSetting.DEFAULT_VALUE); + + CustomEvent.assertOnDisk((x, y) -> x.getName().compareTo(y.getName())); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/settingdescriptor/TestGetAnnotation.java 2019-02-08 18:32:57.881401595 +0300 @@ -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.api.metadata.settingdescriptor; + +import jdk.jfr.Description; +import jdk.jfr.EventType; +import jdk.jfr.Label; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.Timespan; +import jdk.jfr.Timestamp; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test SettingDescriptor.getAnnotation(); + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.settingdescriptor.TestGetAnnotation + */ +public class TestGetAnnotation { + + public static void main(String[] args) throws Exception { + EventType type = EventType.getEventType(CustomEvent.class); + + SettingDescriptor annotatedType = Events.getSetting(type, "annotatedType"); + Label al = annotatedType.getAnnotation(Label.class); + Asserts.assertNull(al); // we should not inherit annotation from type + + Description ad = annotatedType.getAnnotation(Description.class); + Asserts.assertNull(ad); // we should not inherit annotation from type + + Timestamp at = annotatedType.getAnnotation(Timestamp.class); + Asserts.assertNull(at); // we should not inherit annotation from type + + SettingDescriptor newName = Events.getSetting(type, "newName"); + Label nl = newName.getAnnotation(Label.class); + Asserts.assertEquals(nl.value(), "Annotated Method"); + + Description nd = newName.getAnnotation(Description.class); + Asserts.assertEquals(nd.value(), "Description of an annotated method"); + + Timespan nt = newName.getAnnotation(Timespan.class); + Asserts.assertEquals(nt.value(), Timespan.NANOSECONDS); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/settingdescriptor/TestGetAnnotationElement.java 2019-02-08 18:32:58.025396567 +0300 @@ -0,0 +1,87 @@ +/* + * 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.api.metadata.settingdescriptor; + +import java.util.List; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Description; +import jdk.jfr.EventType; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.Timespan; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test SettingDescriptor.getAnnotationElements() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.settingdescriptor.TestGetAnnotationElement + */ +public class TestGetAnnotationElement { + + public static void main(String[] args) throws Exception { + EventType type = EventType.getEventType(CustomEvent.class); + + SettingDescriptor plain = Events.getSetting(type, "plain"); + Asserts.assertTrue(plain.getAnnotationElements().isEmpty()); + + SettingDescriptor annotatedType = Events.getSetting(type, "annotatedType"); + for (AnnotationElement ae : annotatedType.getAnnotationElements()) { + System.out.println(ae.getTypeName()); + } + Asserts.assertTrue(annotatedType.getAnnotationElements().isEmpty()); + + SettingDescriptor newName = Events.getSetting(type, "newName"); + List ae = newName.getAnnotationElements(); + Asserts.assertEquals(ae.size(), 4); + Asserts.assertEquals(ae.get(0).getTypeName(), Name.class.getName()); + Asserts.assertEquals(ae.get(1).getTypeName(), Label.class.getName()); + Asserts.assertEquals(ae.get(2).getTypeName(), Description.class.getName()); + Asserts.assertEquals(ae.get(3).getTypeName(), Timespan.class.getName()); + + SettingDescriptor overridden = Events.getSetting(type, "overridden"); + Asserts.assertTrue(overridden.getAnnotationElements().isEmpty()); + + CustomEvent.assertOnDisk((x, y) -> { + List a1 = x.getAnnotationElements(); + List a2 = y.getAnnotationElements(); + if (a1.size() != a2.size()) { + throw new RuntimeException("Not same number of annotation ekements on disk as in process"); + } + for (int i = 0; i < a1.size(); i++) { + if (!a1.get(i).getTypeName().equals(a2.get(i).getTypeName())) { + throw new RuntimeException("Type name of annotation elements in process doesn't match type name on disk"); + } + } + return 0; + }); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/settingdescriptor/TestGetContentType.java 2019-02-08 18:32:58.169391540 +0300 @@ -0,0 +1,73 @@ +/* + * 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.api.metadata.settingdescriptor; + +import java.util.Objects; + +import jdk.jfr.EventType; +import jdk.jfr.Frequency; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.Timespan; +import jdk.jfr.Timestamp; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test SettingDescriptor.getContentType() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.settingdescriptor.TestGetDescription + */ +public class TestGetContentType { + + public static void main(String[] args) throws Exception { + EventType type = EventType.getEventType(CustomEvent.class); + + SettingDescriptor plain = Events.getSetting(type, "plain"); + Asserts.assertNull(plain.getContentType()); + + SettingDescriptor annotatedType = Events.getSetting(type, "annotatedType"); + Asserts.assertNull(annotatedType.getContentType(), Timestamp.class.getName()); + + SettingDescriptor newName = Events.getSetting(type, "newName"); + Asserts.assertEquals(newName.getContentType(), Timespan.class.getName()); + + SettingDescriptor overridden = Events.getSetting(type, "overridden"); + Asserts.assertNull(overridden.getContentType()); + + SettingDescriptor protectedBase = Events.getSetting(type, "protectedBase"); + Asserts.assertEquals(protectedBase.getContentType(), Frequency.class); + + SettingDescriptor publicBase = Events.getSetting(type, "publicBase"); + Asserts.assertEquals(publicBase.getContentType(), Timestamp.class.getName()); + + SettingDescriptor packageProtectedBase = Events.getSetting(type, "packageProtectedBase"); + Asserts.assertNull(packageProtectedBase.getContentType()); + + CustomEvent.assertOnDisk((x, y) -> Objects.equals(x.getContentType(), y.getContentType()) ? 0 : 1); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/settingdescriptor/TestGetDescription.java 2019-02-08 18:32:58.313386512 +0300 @@ -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.api.metadata.settingdescriptor; + +import java.util.Objects; + +import jdk.jfr.EventType; +import jdk.jfr.SettingDescriptor; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test SettingDescriptor.getDescription() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.settingdescriptor.TestGetDescription + */ +public class TestGetDescription { + + public static void main(String[] args) throws Exception { + EventType type = EventType.getEventType(CustomEvent.class); + + SettingDescriptor plain = Events.getSetting(type, "plain"); + Asserts.assertNull(plain.getDescription()); + + SettingDescriptor annotatedType = Events.getSetting(type, "annotatedType"); + Asserts.assertEquals(annotatedType.getDescription(), AnnotatedSetting.DESCRIPTION); + + SettingDescriptor newName = Events.getSetting(type, "newName"); + Asserts.assertEquals(newName.getDescription(), CustomEvent.DESCRIPTION_OF_AN_ANNOTATED_METHOD); + + SettingDescriptor overridden = Events.getSetting(type, "overridden"); + Asserts.assertNull(overridden.getDescription()); + + SettingDescriptor protectedBase = Events.getSetting(type, "protectedBase"); + Asserts.assertEquals(protectedBase.getDescription(), "Description of protected base"); + + SettingDescriptor publicBase = Events.getSetting(type, "publicBase"); + Asserts.assertEquals(publicBase.getDescription(), AnnotatedSetting.DESCRIPTION); + + SettingDescriptor packageProtectedBase = Events.getSetting(type, "packageProtectedBase"); + Asserts.assertNull(packageProtectedBase.getDescription()); + + CustomEvent.assertOnDisk((x, y) -> Objects.equals(x.getDescription(), y.getDescription()) ? 0 : 1); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/settingdescriptor/TestGetLabel.java 2019-02-08 18:32:58.453381625 +0300 @@ -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.api.metadata.settingdescriptor; + +import java.util.Objects; + +import jdk.jfr.EventType; +import jdk.jfr.SettingDescriptor; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test SettingDescriptor.getLabel() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.settingdescriptor.TestGetLabel + */ +public class TestGetLabel { + + public static void main(String[] args) throws Exception { + EventType type = EventType.getEventType(CustomEvent.class); + + SettingDescriptor plain = Events.getSetting(type, "plain"); + Asserts.assertNull(plain.getLabel()); + + SettingDescriptor annotatedType = Events.getSetting(type, "annotatedType"); + Asserts.assertEquals(AnnotatedSetting.LABEL, annotatedType.getLabel()); + + SettingDescriptor newName = Events.getSetting(type, "newName"); + Asserts.assertEquals(newName.getLabel(), "Annotated Method"); + + SettingDescriptor overridden = Events.getSetting(type, "overridden"); + Asserts.assertNull(overridden.getLabel()); + + SettingDescriptor protectedBase = Events.getSetting(type, "protectedBase"); + Asserts.assertEquals(protectedBase.getLabel(), "Protected Base"); + + SettingDescriptor publicBase = Events.getSetting(type, "publicBase"); + Asserts.assertEquals(publicBase.getLabel(), AnnotatedSetting.LABEL); + + SettingDescriptor packageProtectedBase = Events.getSetting(type, "packageProtectedBase"); + Asserts.assertNull(packageProtectedBase.getLabel()); + + CustomEvent.assertOnDisk((x, y) -> Objects.equals(x.getLabel(), y.getLabel()) ? 0 : 1); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/settingdescriptor/TestGetName.java 2019-02-08 18:32:58.597376598 +0300 @@ -0,0 +1,65 @@ +/* + * 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.api.metadata.settingdescriptor; + +import jdk.jfr.EventType; +import jdk.jfr.SettingDescriptor; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test SettingDescriptor.getName() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.settingdescriptor.TestGetName + */ +public class TestGetName { + + public static void main(String[] args) throws Exception { + EventType type = EventType.getEventType(CustomEvent.class); + + // subclass + Events.getSetting(type, "plain"); + Events.getSetting(type, "annotatedType"); + Events.getSetting(type, "newName"); + Events.getSetting(type, "overridden"); + // base class + Events.getSetting(type, "overridden"); + Events.getSetting(type, "protectedBase"); + Events.getSetting(type, "publicBase"); + Events.getSetting(type, "packageProtectedBase"); + + int defaultNumberOfSettings = 3; // Enabled , Stack Trace, Threshold + if (type.getSettingDescriptors().size() != 8 + defaultNumberOfSettings) { + for (SettingDescriptor s : type.getSettingDescriptors()) { + System.out.println(s.getName()); + } + throw new Exception("Wrong number of settings"); + } + + CustomEvent.assertOnDisk((x, y) -> x.getName().compareTo(y.getName())); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/settingdescriptor/TestGetTypeId.java 2019-02-08 18:32:58.741371571 +0300 @@ -0,0 +1,73 @@ +/* + * 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.api.metadata.settingdescriptor; + +import jdk.jfr.EventType; +import jdk.jfr.SettingDescriptor; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test SettingDescriptor.getTypeId() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.settingdescriptor.TestGetTypeId + */ +public class TestGetTypeId { + + public static void main(String[] args) throws Exception { + EventType type = EventType.getEventType(CustomEvent.class); + + SettingDescriptor plain = Events.getSetting(type, "plain"); + long plainId = plain.getTypeId(); + Asserts.assertGreaterThan(plainId, 0L); + + SettingDescriptor annotatedType = Events.getSetting(type, "annotatedType"); + long annotatedId = annotatedType.getTypeId(); + Asserts.assertGreaterThan(annotatedId, 0L); + + Asserts.assertNotEquals(annotatedId, plainId); + + SettingDescriptor newName = Events.getSetting(type, "newName"); + Asserts.assertEquals(newName.getTypeId(), annotatedId); + + SettingDescriptor overridden = Events.getSetting(type, "overridden"); + Asserts.assertEquals(overridden.getTypeId(), plainId); + + SettingDescriptor protectedBase = Events.getSetting(type, "protectedBase"); + Asserts.assertEquals(protectedBase.getTypeId(), plainId); + + SettingDescriptor publicBase = Events.getSetting(type, "publicBase"); + Asserts.assertEquals(publicBase.getTypeId(), annotatedId); + + SettingDescriptor packageProtectedBase = Events.getSetting(type, "packageProtectedBase"); + Asserts.assertEquals(packageProtectedBase.getTypeId(), plainId); + + CustomEvent.assertOnDisk((x, y) -> Long.compare(x.getTypeId(), y.getTypeId())); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/settingdescriptor/TestGetTypeName.java 2019-02-08 18:32:58.885366543 +0300 @@ -0,0 +1,68 @@ +/* + * 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.api.metadata.settingdescriptor; + +import jdk.jfr.EventType; +import jdk.jfr.SettingDescriptor; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test SettingDescriptor.getTypeName(); + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.settingdescriptor.TestGetTypeName + */ +public class TestGetTypeName { + + public static void main(String[] args) throws Exception { + EventType type = EventType.getEventType(CustomEvent.class); + + SettingDescriptor plain = Events.getSetting(type, "plain"); + Asserts.assertEquals(plain.getTypeName(), PlainSetting.class.getName()); + + SettingDescriptor annotatedType = Events.getSetting(type, "annotatedType"); + Asserts.assertEquals(annotatedType.getTypeName(), AnnotatedSetting.NAME); + + SettingDescriptor newName = Events.getSetting(type, "newName"); + Asserts.assertEquals(newName.getTypeName(), AnnotatedSetting.NAME); + + SettingDescriptor overridden = Events.getSetting(type, "overridden"); + Asserts.assertEquals(overridden.getTypeName(), PlainSetting.class.getName()); + + SettingDescriptor protectedBase = Events.getSetting(type, "protectedBase"); + Asserts.assertEquals(protectedBase.getTypeName(), PlainSetting.class.getName()); + + SettingDescriptor publicBase = Events.getSetting(type, "publicBase"); + Asserts.assertEquals(publicBase.getTypeName(), AnnotatedSetting.NAME); + + SettingDescriptor packageProtectedBase = Events.getSetting(type, "packageProtectedBase"); + Asserts.assertEquals(packageProtectedBase.getTypeName(), PlainSetting.class.getName()); + + CustomEvent.assertOnDisk((x, y) -> x.getTypeName().compareTo(y.getTypeName())); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/valuedescriptor/TestClasses.java 2019-02-08 18:32:59.025361656 +0300 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 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.api.metadata.valuedescriptor; + +import java.util.HashMap; +import java.util.Map; + +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.VoidFunction; + +/** + * @test + * @summary Test ValueDescriptor.getAnnotations() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.valuedescriptor.TestClasses + */ +public class TestClasses { + + public static void main(String[] args) throws Throwable { + @SuppressWarnings("rawtypes") + Map valid = new HashMap<>(); + valid.put("byte", byte.class); + valid.put("short", short.class); + valid.put("int", int.class); + valid.put("char", char.class); + valid.put("float", float.class); + valid.put("double", double.class); + valid.put("boolean", boolean.class); + valid.put("double", double.class); + valid.put("long", long.class); + valid.put("java.lang.String", String.class); + valid.put("java.lang.Class", Class.class); + valid.put("java.lang.Thread", Thread.class); + + for (String name : valid.keySet()) { + Class t = valid.get(name); + System.out.println(t.getName()); + ValueDescriptor d = new ValueDescriptor(t, "dummy"); + String typeName = d.getTypeName() + (d.isArray() ? "[]" : ""); + System.out.printf("%s -> typeName %s%n", name, typeName); + Asserts.assertEquals(name, typeName, "Wrong type name"); + } + + // Test some illegal classes + verifyIllegalArg(()->{new ValueDescriptor(Float.class, "ids");}, "Arrays of non-primitives should give Exception"); + verifyIllegalArg(()->{new ValueDescriptor(Integer[].class, "ids");}, "Arrays of non-primitives should give Exception"); + verifyIllegalArg(()->{new ValueDescriptor(Class[].class, "ids");}, "Arrays of non-primitives should give Exception"); + verifyIllegalArg(()->{new ValueDescriptor(MyClass.class, "MyClass");}, "MyClass should give Exception"); + } + + private static class MyClass { + @SuppressWarnings("unused") + int id; + } + + private static void verifyIllegalArg(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, IllegalArgumentException.class); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/valuedescriptor/TestConstructor.java 2019-02-08 18:32:59.169356629 +0300 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 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.api.metadata.valuedescriptor; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Label; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test ValueDescriptor.getAnnotations() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.valuedescriptor.TestConstructor + */ +public class TestConstructor { + + public static void main(String[] args) throws Throwable { + ValueDescriptor vdSimple = new ValueDescriptor(String.class, "message"); + Asserts.assertNull(vdSimple.getAnnotation(Label.class), "Expected getAnnotation()==null"); + Asserts.assertEquals(0, vdSimple.getAnnotationElements().size(), "Expected getAnnotations().size() == 0"); + + // Add labelA and verify we can read it back + List annos = new ArrayList<>(); + AnnotationElement labelA = new AnnotationElement(Label.class, "labelA"); + annos.add(labelA); + System.out.println("labelA.getClass()" + labelA.getClass()); + ValueDescriptor vdComplex = new ValueDescriptor(String.class, "message", annos); + + final Label outLabel = vdComplex.getAnnotation(Label.class); + Asserts.assertFalse(outLabel == null, "getLabel(Label.class) was null"); + System.out.println("outLabel.value() = " + outLabel.value()); + + // Get labelA from getAnnotations() list + Asserts.assertEquals(1, vdComplex.getAnnotationElements().size(), "Expected getAnnotations().size() == 1"); + final AnnotationElement outAnnotation = vdComplex.getAnnotationElements().get(0); + Asserts.assertNotNull(outAnnotation, "outAnnotation was null"); + System.out.printf("Annotation: %s = %s%n", outAnnotation.getTypeName(), outAnnotation.getValue("value")); + Asserts.assertEquals(outAnnotation, labelA, "Expected firstAnnotation == labelA"); + + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/valuedescriptor/TestGetAnnotations.java 2019-02-08 18:32:59.317351462 +0300 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 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.api.metadata.valuedescriptor; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test ValueDescriptor.getAnnotations() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.valuedescriptor.TestGetAnnotations + */ +public class TestGetAnnotations { + + public static void main(String[] args) throws Throwable { + EventType type = EventType.getEventType(MyEvent.class); + + List actual = new ArrayList<>(); + for (ValueDescriptor d : type.getFields()) { + String descName = d.getName(); + for (AnnotationElement a : d.getAnnotationElements()) { + String annName = a.getTypeName(); + String annValue = a.getValue("value").toString(); + actual.add(String.format("%s: %s = %s", descName, annName, annValue)); + } + } + + System.out.println("Actual annotations:"); + for (String s : actual) { + System.out.println(s); + } + + String[] expected = { + "myShortName: jdk.jfr.Label = myShortLabel", + "myShortName: jdk.jfr.Description = myShortDesc", + "myLongName: jdk.jfr.Description = myLongDesc", + "myLongName: jdk.jfr.Label = myLongLabel", + }; + + for (String s : expected) { + if (!actual.contains(s)) { + System.out.println("Expected annotation missing: " + s); + Asserts.fail("Not all expected annotations found"); + } + } + } + + + private static class MyEvent extends Event { + @Label("myShortLabel") + @Description("myShortDesc") + @Name("myShortName") + public short myShort; + + @Name("myLongName") + @Description("myLongDesc") + @Label("myLongLabel") + public long myLong; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/valuedescriptor/TestGetFields.java 2019-02-08 18:32:59.461346435 +0300 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 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.api.metadata.valuedescriptor; + +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test ValueDescriptor.getAnnotations() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.valuedescriptor.TestGetFields + */ +public class TestGetFields { + + public static void main(String[] args) throws Throwable { + // Test simple ValueDescriptor + final ValueDescriptor vdSimple = new ValueDescriptor(int.class, "id"); + Asserts.assertNotNull(vdSimple.getFields(), "getFields() returned null"); + Asserts.assertTrue(vdSimple.getFields().isEmpty(), "getFields() not empty for vdSimple"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/valuedescriptor/TestIsArray.java 2019-02-08 18:32:59.609341269 +0300 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 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.api.metadata.valuedescriptor; + +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test ValueDescriptor.isArray(). + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.valuedescriptor.TestIsArray + */ +public class TestIsArray { + + public static void main(String[] args) throws Throwable { + EventType type = EventType.getEventType(EventWithArray.class); + + ValueDescriptor d = type.getField("myIds"); + + Asserts.assertNull(d, "ValueDescriptor for int[] was not null"); + + type = EventType.getEventType(EventWithoutArray.class); + d = type.getField("myId"); + Asserts.assertFalse(d.isArray(), "isArray() was true for int"); + } + + private static class EventWithArray extends Event { + @SuppressWarnings("unused") + public int[] myIds; + } + + private static class EventWithoutArray extends Event { + @SuppressWarnings("unused") + public int myId; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/valuedescriptor/TestSimpleTypes.java 2019-02-08 18:32:59.757336103 +0300 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 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.api.metadata.valuedescriptor; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.BooleanFlag; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.Percentage; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test all basic types in ValueDescriptor. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.valuedescriptor.TestSimpleTypes + */ +public class TestSimpleTypes { + + public static void main(String[] args) throws Throwable { + EventType type = EventType.getEventType(MyEvent.class); + + List expectedStrings = new ArrayList<>(); + expectedStrings.add("desc=myByteDesc; label=null; name=myByte; typename=byte; contenttype=null"); + expectedStrings.add("desc=null; label=myShortLabel; name=myShort; typename=short; contenttype=null"); + expectedStrings.add("desc=myIntDesc; label=myIntLabel; name=myInt; typename=int; contenttype=null"); + expectedStrings.add("desc=null; label=null; name=myLongName; typename=long; contenttype=null"); + expectedStrings.add("desc=myCharDesc; label=myCharLabel; name=myCharName; typename=char; contenttype=null"); + expectedStrings.add("desc=null; label=null; name=myFloat; typename=float; contenttype=jdk.jfr.Percentage"); + expectedStrings.add("desc=null; label=null; name=myDouble; typename=double; contenttype=null"); + expectedStrings.add("desc=null; label=null; name=myBoolean; typename=boolean; contenttype=jdk.jfr.BooleanFlag"); + expectedStrings.add("desc=null; label=null; name=myString; typename=java.lang.String; contenttype=null"); + expectedStrings.add("desc=null; label=null; name=myClass; typename=java.lang.Class; contenttype=null"); + expectedStrings.add("desc=null; label=null; name=myThread; typename=java.lang.Thread; contenttype=null"); + + List typeIds = new ArrayList<>(); + for (ValueDescriptor d : type.getFields()) { + if (d.getName().startsWith("my")) { + String s = getCompareString(d); + System.out.println("got: " + s); + Asserts.assertTrue(expectedStrings.contains(s), "Wrong type string found"); + expectedStrings.remove(s); + + long typeId = d.getTypeId(); + Asserts.assertFalse(typeIds.contains(typeId), "TypeIds not unique"); + typeIds.add(typeId); + + Asserts.assertFalse(d.isArray(), "ValueDescriptor should not be array"); + } + } + + if (!expectedStrings.isEmpty()) { + System.out.println("Missing expectedStrings:"); + for (String s : expectedStrings) { + System.out.println(s); + } + Asserts.fail("Not all strings found"); + } + } + + private static String getCompareString(ValueDescriptor d) { + return String.format("desc=%s; label=%s; name=%s; typename=%s; contenttype=%s", + d.getDescription(), + d.getLabel(), + d.getName(), + d.getTypeName(), + d.getContentType()); + } + + @SuppressWarnings("unused") + private static class MyEvent extends Event { + + @Description("myByteDesc") + public byte myByte; + + @Label("myShortLabel") + public short myShort; + + @Label("myIntLabel") + @Description("myIntDesc") + public int myInt; + + @Name("myLongName") + public long myLong; + + @Label("myCharLabel") + @Description("myCharDesc") + @Name("myCharName") + protected char myChar; + + @Percentage + protected float myFloat; + protected double myDouble; + @BooleanFlag + private boolean myBoolean; + private String myString; + @SuppressWarnings("rawtypes") + private Class myClass; + private Thread myThread; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/metadata/valuedescriptor/TestValueDescriptorContentType.java 2019-02-08 18:32:59.897331215 +0300 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 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.api.metadata.valuedescriptor; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jdk.jfr.ContentType; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Test ValueDescriptor.getContentType() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.metadata.valuedescriptor.TestValueDescriptorContentType + */ +public class TestValueDescriptorContentType { + + @MetadataDefinition + @ContentType + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.FIELD, ElementType.TYPE, ElementType.METHOD }) + static public @interface Hawaiian { + } + + @MetadataDefinition + @Retention(RetentionPolicy.RUNTIME) + @Target({ ElementType.FIELD, ElementType.TYPE }) + static public @interface NotContentType { + } + + @SuppressWarnings("unused") + private static class AlohaEvent extends Event { + @Hawaiian + String greeting; + + String missing; + + @NotContentType + String otherAnnotation; + } + + public static void main(String[] args) throws Throwable { + EventType type = EventType.getEventType(AlohaEvent.class); + + // check field annotation on event value + ValueDescriptor filter = type.getField("greeting"); + Asserts.assertEquals(filter.getContentType(), Hawaiian.class.getName()); + + // check field annotation with missing content type + ValueDescriptor missing = type.getField("missing"); + Asserts.assertEquals(missing.getContentType(), null); + + // check field annotation with annotation but not content type + ValueDescriptor otherAnnotation = type.getField("otherAnnotation"); + Asserts.assertEquals(otherAnnotation.getContentType(), null); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/modules/TestModularizedEvent.java 2019-02-08 18:33:00.041326189 +0300 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 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.api.modules; + +import java.nio.file.Paths; + +import static jdk.test.lib.Asserts.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import javax.tools.JavaCompiler; +import javax.tools.StandardJavaFileManager; +import javax.tools.StandardLocation; +import javax.tools.ToolProvider; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestModularizedEvent { + + private static final String TEST_SRC = System.getProperty("test.src"); + + private static final Path SRC_DIR = Paths.get(TEST_SRC, "src_mods"); + private static final Path MODS_DIR = Paths.get("mods"); + + private static final String ANNO_MODULE = "test.jfr.annotation"; + private static final String SETTING_MODULE = "test.jfr.setting"; + private static final String EVENT_MODULE = "test.jfr.event"; + private static final String TEST_MODULE = "test.jfr.main"; + + public static void main(String... args) throws Exception { + compileModule(ANNO_MODULE); + compileModule(SETTING_MODULE); + compileModule(EVENT_MODULE, "--module-path", MODS_DIR.toString()); + compileModule(TEST_MODULE, "--module-path", MODS_DIR.toString()); + + OutputAnalyzer oa = ProcessTools.executeTestJava("--module-path", "mods", "-m", "test.jfr.main/test.jfr.main.MainTest"); + oa.stdoutShouldContain("Test passed."); + } + + private static void compileModule(String modDir, String... opts) throws Exception { + boolean compiled = compile(SRC_DIR.resolve(modDir), + MODS_DIR.resolve(modDir), + opts); + assertTrue(compiled, "module " + modDir + " did not compile"); + } + + private static boolean compile(Path source, Path destination, String... options) + throws IOException { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + if (compiler == null) { + // no compiler available + throw new UnsupportedOperationException("Unable to get system java compiler. " + + "Perhaps, jdk.compiler module is not available."); + } + StandardJavaFileManager jfm = compiler.getStandardFileManager(null, null, null); + + List sources + = Files.find(source, Integer.MAX_VALUE, + (file, attrs) -> (file.toString().endsWith(".java"))) + .collect(Collectors.toList()); + + Files.createDirectories(destination); + jfm.setLocation(StandardLocation.CLASS_PATH, Collections.emptyList()); + jfm.setLocationFromPaths(StandardLocation.CLASS_OUTPUT, + Arrays.asList(destination)); + + List opts = Arrays.asList(options); + JavaCompiler.CompilationTask task + = compiler.getTask(null, jfm, null, opts, null, + jfm.getJavaFileObjectsFromPaths(sources)); + + return task.call(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/modules/src_mods/test.jfr.annotation/test/jfr/annotation/ModularizedAnnotation.java 2019-02-08 18:33:00.189321023 +0300 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 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 test.jfr.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import jdk.jfr.MetadataDefinition; + +@MetadataDefinition +@Target({ElementType.TYPE, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@ModularizedAnnotation("hello annotation") +public @interface ModularizedAnnotation { + + String value(); +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/modules/src_mods/test.jfr.event/test/jfr/event/ModularizedOrdinaryEvent.java 2019-02-08 18:33:00.333315996 +0300 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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 test.jfr.event; + +import test.jfr.annotation.ModularizedAnnotation; + +import jdk.jfr.Event; +import jdk.jfr.SettingDefinition; +import test.jfr.setting.ModularizedSetting; + +@ModularizedAnnotation("hello type") +public class ModularizedOrdinaryEvent extends Event { + + @ModularizedAnnotation("hello field") + public String message; + + @SettingDefinition + boolean filter(ModularizedSetting ms) { + return ms.accept(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/modules/src_mods/test.jfr.event/test/jfr/event/ModularizedPeriodicEvent.java 2019-02-08 18:33:00.481310830 +0300 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 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 test.jfr.event; + +import test.jfr.annotation.ModularizedAnnotation; + +import jdk.jfr.Event; +import jdk.jfr.SettingDefinition; +import test.jfr.setting.ModularizedSetting; + +@ModularizedAnnotation("hello type") +public class ModularizedPeriodicEvent extends Event { + + @ModularizedAnnotation("hello field") + public String message; + + @SettingDefinition + boolean filter(ModularizedSetting ms) { + return ms.accept(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/modules/src_mods/test.jfr.main/test/jfr/main/MainTest.java 2019-02-08 18:33:00.625305804 +0300 @@ -0,0 +1,154 @@ +/* + * Copyright (c) 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 test.jfr.main; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedEvent; + +import test.jfr.annotation.ModularizedAnnotation; +import test.jfr.event.ModularizedOrdinaryEvent; +import test.jfr.event.ModularizedPeriodicEvent; +import java.nio.file.Path; +import java.util.Objects; +import jdk.jfr.consumer.RecordingFile; + +public class MainTest { + + private static final String HELLO_ORDINARY = "ordinary says hello"; + private static final String HELLO_PERIODIC = "periodic says hello"; + + public static void main(String... args) throws Exception { + System.out.println("Starting the test..."); + FlightRecorder.addPeriodicEvent(ModularizedPeriodicEvent.class, () -> { + ModularizedPeriodicEvent me = new ModularizedPeriodicEvent(); + me.message = HELLO_PERIODIC; + me.commit(); + }); + Recording r = new Recording(); + r.enable(ModularizedOrdinaryEvent.class).with("filter", "true").withoutStackTrace(); + r.enable(ModularizedPeriodicEvent.class).with("filter", "true").withoutStackTrace(); + r.start(); + ModularizedOrdinaryEvent m = new ModularizedOrdinaryEvent(); + m.message = HELLO_ORDINARY; + m.commit(); + r.stop(); + List events = fromRecording(r); + System.out.println(events); + if (events.size() == 0) { + throw new RuntimeException("Expected two events"); + } + assertOrdinaryEvent(events); + assertPeriodicEvent(events); + assertMetadata(events); + System.out.println("Test passed."); + } + + private static void assertMetadata(List events) { + for (RecordedEvent e : events) { + EventType type = e.getEventType(); + ModularizedAnnotation maType = type.getAnnotation(ModularizedAnnotation.class); + if (maType == null) { + fail("Missing @ModularizedAnnotation on type " + type); + } + assertEquals(maType.value(), "hello type"); + assertMetaAnnotation(type.getAnnotationElements()); + + ValueDescriptor messageField = type.getField("message"); + ModularizedAnnotation maField = messageField.getAnnotation(ModularizedAnnotation.class); + if (maField == null) { + fail("Missing @ModularizedAnnotation on field in " + type); + } + assertEquals(maField.value(), "hello field"); + assertMetaAnnotation(messageField.getAnnotationElements()); + } + } + + private static void assertMetaAnnotation(List aes) { + assertEquals(aes.size(), 1, "@ModularizedAnnotation should only have one meta-annotation"); + AnnotationElement ae = aes.get(0); + assertEquals(ae.getTypeName(), ModularizedAnnotation.class.getName(), "Incorrect meta-annotation"); + } + + private static void assertPeriodicEvent(List events) { + for (RecordedEvent e : events) { + String message = e.getValue("message"); + if (message.equals(HELLO_ORDINARY)) { + return; + } + } + throw new RuntimeException("Could not find ordinary event in recording"); + } + + private static void assertOrdinaryEvent(List events) { + for (RecordedEvent e : events) { + String message = e.getValue("message"); + if (message.equals(HELLO_PERIODIC)) { + return; + } + } + throw new RuntimeException("Could not find periodic event in recording"); + } + + public static List fromRecording(Recording recording) throws IOException { + return RecordingFile.readAllEvents(makeCopy(recording)); + } + + private static Path makeCopy(Recording recording) throws IOException { + Path p = recording.getDestination(); + if (p == null) { + File directory = new File("."); + ProcessHandle h = ProcessHandle.current(); + p = new File(directory.getAbsolutePath(), "recording-" + recording.getId() + "-pid" + h.pid() + ".jfr").toPath(); + recording.dump(p); + } + return p; + } + + private static void assertEquals(Object lhs, Object rhs) { + assertEquals(lhs, rhs, null); + } + + private static void assertEquals(Object lhs, Object rhs, String msg) { + if ((lhs != rhs) && ((lhs == null) || !(lhs.equals(rhs)))) { + msg = Objects.toString(msg, "assertEquals") + + ": expected " + Objects.toString(lhs) + + " to equal " + Objects.toString(rhs); + fail(msg); + } + } + + private static void fail(String message) { + throw new RuntimeException(message); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/modules/src_mods/test.jfr.setting/test/jfr/setting/ModularizedSetting.java 2019-02-08 18:33:00.773300638 +0300 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 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 test.jfr.setting; + +import java.util.Set; + +import jdk.jfr.SettingControl; + +public final class ModularizedSetting extends SettingControl { + + private String value = "false"; + private boolean isTrue = false; + + @Override + public String combine(Set settingValues) { + for (String s : settingValues) { + if ("true".equals(s)) { + return "true"; + } + } + return "false"; + } + + @Override + public void setValue(String settingValue) { + this.value = settingValue; + this.isTrue = Boolean.valueOf(settingValue); + } + + @Override + public String getValue() { + return value; + } + + public boolean accept() { + return isTrue; + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recorder/TestRecorderInitialized.java 2019-02-08 18:33:00.913295751 +0300 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 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.api.recorder; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.FlightRecorderListener; +import jdk.test.lib.Asserts; + +/** + * @test TestRecorderListener + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.api.recorder.TestRecorderInitialized + */ +public class TestRecorderInitialized { + + static class Listener implements FlightRecorderListener { + private boolean notified; + + @Override + public void recorderInitialized(FlightRecorder r) { + notified = true; + } + } + + public static void main(String...args) { + Listener l1 = new Listener(); + + FlightRecorder.addListener(l1); + Asserts.assertFalse(l1.notified, "Listener shouldn't be notified unless Flight Recorder is initialized"); + // initialize Flight Recorder + FlightRecorder.getFlightRecorder(); + Asserts.assertTrue(l1.notified, "Listener should be notified when Flight Recorder is initialized"); + l1.notified = false; + + Listener l2 = new Listener(); + FlightRecorder.addListener(l1); + FlightRecorder.addListener(l2); + Asserts.assertTrue(l2.notified, "Listener should be notified if Flight Recorder is already initialized"); + Asserts.assertTrue(l1.notified, "Only added listnener should be notified, if Flight Recorder is already initialized"); + + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recorder/TestRecorderListener.java 2019-02-08 18:33:01.057290725 +0300 @@ -0,0 +1,106 @@ +/* + * 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.api.recorder; + +import java.util.concurrent.CountDownLatch; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.FlightRecorderListener; +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; + +/** + * @test TestRecorderListener + * + * @key jfr + * + * @run main/othervm jdk.jfr.api.recorder.TestRecorderListener + */ +public class TestRecorderListener { + + static class Listener implements FlightRecorderListener { + + private final CountDownLatch latch = new CountDownLatch(1); + private final RecordingState waitFor; + + public Listener(RecordingState state) { + waitFor = state; + } + + @Override + public void recordingStateChanged(Recording recording) { + System.out.println("Listener: recording=" + recording.getName() + " state=" + recording.getState()); + RecordingState rs = recording.getState(); + if (rs == waitFor) { + latch.countDown(); + } + } + + public void waitFor() throws InterruptedException { + latch.await(); + } + } + + public static void main(String... args) throws Exception { + Listener recordingListener = new Listener(RecordingState.RUNNING); + FlightRecorder.addListener(recordingListener); + + Listener stoppedListener = new Listener(RecordingState.STOPPED); + FlightRecorder.addListener(stoppedListener); + + Listener finishedListener = new Listener(RecordingState.CLOSED); + FlightRecorder.addListener(finishedListener); + + Recording recording = new Recording(); + if (recording.getState() != RecordingState.NEW) { + recording.close(); + throw new Exception("New recording should be in NEW state"); + } + + recording.start(); + recordingListener.waitFor(); + + recording.stop(); + stoppedListener.waitFor(); + + recording.close(); + finishedListener.waitFor(); + + testDefaultrecordingStateChangedListener(); + + } + + private static class DummyListener implements FlightRecorderListener { + + } + + private static void testDefaultrecordingStateChangedListener() { + FlightRecorder.addListener(new DummyListener()); + Recording recording = new Recording(); + recording.start(); + recording.stop(); + recording.close(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recorder/TestStartStopRecording.java 2019-02-08 18:33:01.205285559 +0300 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 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.api.recorder; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; + +import jdk.jfr.Configuration; +import jdk.jfr.Recording; +import jdk.test.lib.Utils; + +/** + * @test TestStartStopRecording + * + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recorder.TestStartStopRecording + */ +public class TestStartStopRecording { + + public static void main(String... args) throws Exception { + Configuration defaultConfig = Configuration.getConfiguration("default"); + // Memory + Recording inMemory = new Recording(defaultConfig); + inMemory.setToDisk(false); + + inMemory.start(); + + Path memoryFile = Utils.createTempFile("start-stop-memory-recording", ".jfr"); + inMemory.dump(memoryFile); + assertValid(memoryFile, "Not a valid memory file."); + inMemory.stop(); + inMemory.close(); + // Disk + Recording toDisk = new Recording(defaultConfig); + toDisk.setToDisk(true); + + toDisk.start(); + toDisk.stop(); + Path diskFile = Utils.createTempFile("start-stop-disk-recording", ".jfr"); + toDisk.dump(diskFile); + assertValid(diskFile, "Not a valid disk file."); + toDisk.close(); + } + + private static void assertValid(Path path, String message) throws IOException { + if (!Files.exists(path, LinkOption.NOFOLLOW_LINKS)) { + throw new AssertionError(message + " Could not find file " + path); + } + if (Files.size(path) == 0) { + throw new AssertionError(message + " File size = 0 for " + path); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/destination/TestDestFileExist.java 2019-02-08 18:33:01.349280533 +0300 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 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.api.recording.destination; + +import static jdk.test.lib.Asserts.assertTrue; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @summary Set destination to an existing file. File should be overwritten. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.destination.TestDestFileExist + */ +public class TestDestFileExist { + + public static void main(String[] args) throws Throwable { + Path dest = Paths.get(".", "my.jfr"); + System.out.println("dest=" + dest); + Files.write(dest, "Dummy data".getBytes()); + assertTrue(Files.exists(dest), "Test error: Failed to create file"); + System.out.println("file size before recording:" + Files.size(dest)); + Recording r = new Recording(); + r.enable(EventNames.OSInformation); + r.setDestination(dest); + r.start(); + r.stop(); + List events = RecordingFile.readAllEvents(dest); + Asserts.assertFalse(events.isEmpty(), "No events found"); + System.out.println(events.iterator().next()); + r.close(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/destination/TestDestFileReadOnly.java 2019-02-08 18:33:01.497275367 +0300 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 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.api.recording.destination; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.FileHelper; + +/** + * @test + * @summary Set destination to a read-only file. Expects exception. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.destination.TestDestFileReadOnly + */ +public class TestDestFileReadOnly { + + public static void main(String[] args) throws Throwable { + Path dest = FileHelper.createReadOnlyFile(Paths.get(".", "readonly.txt")); + System.out.println("dest=" + dest.toFile().getAbsolutePath()); + if (!FileHelper.isReadOnlyPath(dest)) { + System.out.println("Failed to create a read-only file. Test ignored."); + return; + } + + Recording r = new Recording(); + r.setToDisk(true); + try { + r.setDestination(dest); + Asserts.fail("No exception when destination is read-only"); + } catch (IOException e) { + // Expected exception + } + r.close(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/destination/TestDestInvalid.java 2019-02-08 18:33:01.641270341 +0300 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 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.api.recording.destination; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.VoidFunction; + +/** + * @test + * @summary Test setDestination to invalid paths + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.destination.TestDestInvalid + */ +public class TestDestInvalid { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + r.enable(EventNames.OSInformation); + r.setToDisk(true); + + Asserts.assertNull(r.getDestination(), "dest not null by default"); + + // Set destination to empty path (same as curr dir, not a file) + verifyException(()->{r.setDestination(Paths.get(""));}, "No exception for setDestination(\"\")", IOException.class); + System.out.println("1 destination: " + r.getDestination()); + Asserts.assertNull(r.getDestination(), "default dest not null after failed setDest"); + + // Set dest to a valid path. This should be kept when a new setDest fails. + Path dest = Paths.get(".", "my.jfr"); + r.setDestination(dest); + System.out.println("2 destination: " + r.getDestination()); + Asserts.assertEquals(dest, r.getDestination(), "Wrong get/set dest"); + + // Null is allowed for setDestination() + r.setDestination(null); + System.out.println("3 destination: " + r.getDestination()); + Asserts.assertNull(r.getDestination(), "dest not null after setDest(null)"); + + // Reset dest to correct value and make ssure it is not overwritten + r.setDestination(dest); + System.out.println("4 destination: " + r.getDestination()); + Asserts.assertEquals(dest, r.getDestination(), "Wrong get/set dest"); + + // Set destination to an existing dir. Old dest is saved. + verifyException(()->{r.setDestination(Paths.get("."));}, "No exception for setDestination(.)", IOException.class); + System.out.println("5 destination: " + r.getDestination()); + Asserts.assertEquals(dest, r.getDestination(), "Wrong get/set dest"); + + // Set destination to a non-existing dir. Old dest is saved. + verifyException(()->{r.setDestination(Paths.get(".", "missingdir", "my.jfr"));}, "No exception for setDestination(dirNotExists)", IOException.class); + System.out.println("6 destination: " + r.getDestination()); + Asserts.assertEquals(dest, r.getDestination(), "Wrong get/set dest"); + + // Verify that it works with the old setDest value. + r.start(); + r.stop(); + r.close(); + Asserts.assertTrue(Files.exists(dest), "No recording file: " + dest); + List events = RecordingFile.readAllEvents(dest); + Asserts.assertFalse(events.isEmpty(), "No event found"); + System.out.printf("Found event %s in %s%n", events.get(0).getEventType().getName(), dest.toString()); + } + + private static void verifyException(VoidFunction f, String msg, Class exceptionClass) throws Throwable { + CommonHelper.verifyException(f, msg, IOException.class); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/destination/TestDestLongPath.java 2019-02-08 18:33:01.789265175 +0300 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 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.api.recording.destination; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.FileHelper; + +/** + * @test + * @summary Set destination to a long path + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.destination.TestDestLongPath + */ +public class TestDestLongPath { + + public static void main(String[] args) throws Throwable { + Path dir = FileHelper.createLongDir(Paths.get(".")); + Path dest = Paths.get(dir.toString(), "my.jfr"); + Recording r = new Recording(); + r.enable(EventNames.OSInformation); + r.setToDisk(true); + r.setDestination(dest); + r.start(); + r.stop(); + r.close(); + Asserts.assertTrue(Files.exists(dest), "No recording file: " + dest); + List events = RecordingFile.readAllEvents(dest); + Asserts.assertFalse(events.isEmpty(), "No event found"); + System.out.printf("Found event %s%n", events.get(0).getEventType().getName()); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/destination/TestDestMultiple.java 2019-02-08 18:33:01.933260149 +0300 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 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.api.recording.destination; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @summary Test setDestination with concurrent recordings + * @key jfr + * + * @library /lib / + * @run main/othervm -XX:+LogJFR jdk.jfr.api.recording.destination.TestDestMultiple + */ +public class TestDestMultiple { + + public static void main(String[] args) throws Throwable { + Recording rA = new Recording(); + Recording rB = new Recording(); + + Path destA = Paths.get(".", "recA.jfr"); + Path destB = Paths.get(".", "recB.jfr"); + rA.setDestination(destA); + rB.setDestination(destB); + + // Enable event in one recording and disable in the other. + // Both recordings should still get the events, since we always + // get the "union" of all settings. + SimpleEventHelper.enable(rA, true); + SimpleEventHelper.enable(rB, false); + + SimpleEventHelper.createEvent(0); // To no recording + + rA.start(); + SimpleEventHelper.createEvent(1); // Only to recA + + rB.start(); + SimpleEventHelper.createEvent(2); // To both recordings + + rA.stop(); + + // This event will not be in recB. + // The reason is that recA has stopped so event is no longer enabled. + SimpleEventHelper.createEvent(3); + + // Enable the event and create a new event for recB + SimpleEventHelper.enable(rB, true); + SimpleEventHelper.createEvent(4); + + rB.stop(); + SimpleEventHelper.createEvent(5); // To no recording + + rB.close(); + rA.close(); + + verifyRecording(destA, 1, 2); + verifyRecording(destB, 2, 4); + } + + private static void verifyRecording(Path path, int... ids) throws Exception { + Asserts.assertTrue(Files.exists(path), "Recording file does not exist: " + path); + int countEvent = 0; + List events = RecordingFile.readAllEvents(path); + for (RecordedEvent event : events) { + int id = Events.assertField(event, "id").getValue(); + System.out.printf("Recording '%s' id=%d%n", path, id); + } + for (RecordedEvent event : events) { + int id = Events.assertField(event, "id").getValue(); + System.out.printf("Recording '%s' id=%d%n", path, id); + Asserts.assertTrue(ids.length > countEvent, "Found extra event"); + Events.assertField(event, "id").equal(ids[countEvent]); + ++countEvent; + } + // We expect exactly 4 events in each file. 2 events * 2 chunks + Asserts.assertEquals(countEvent, ids.length, "Found too few events"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/destination/TestDestReadOnly.java 2019-02-08 18:33:02.077255123 +0300 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 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.api.recording.destination; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.FileHelper; +import jdk.test.lib.jfr.VoidFunction; + +/** + * @test + * @summary Test setDestination to read-only dir + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.destination.TestDestReadOnly + */ +public class TestDestReadOnly { + + public static void main(String[] args) throws Throwable { + Path readOnlyDir = FileHelper.createReadOnlyDir(Paths.get(".", "readonly")); + if (!FileHelper.isReadOnlyPath(readOnlyDir)) { + System.out.println("Failed to create read-only dir. Running as root?. Test ignored"); + return; + } + + Path readOnlyDest = Paths.get(readOnlyDir.toString(), "readonly.jfr"); + Recording r = new Recording(); + r.enable(EventNames.OSInformation); + r.setToDisk(true); + verifyException(()->{r.setDestination(readOnlyDest);}, "No exception for setDestination to read-only dir"); + + System.out.println("r.getDestination() = " + r.getDestination()); + + // Verify that it works if we set destination to a writable dir. + Path dest = Paths.get(".", "my.jfr"); + r.setDestination(dest); + r.start(); + r.stop(); + r.close(); + Asserts.assertTrue(Files.exists(dest), "No recording file: " + dest); + List events = RecordingFile.readAllEvents(dest); + Asserts.assertFalse(events.isEmpty(), "No event found"); + System.out.printf("Found event %s in %s%n", events.get(0).getEventType().getName(), dest.toString()); + } + + private static void verifyException(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, IOException.class); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/destination/TestDestState.java 2019-02-08 18:33:02.221250097 +0300 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 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.api.recording.destination; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @summary Call setDestination() when recording in different states + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.destination.TestDestState + */ +public class TestDestState { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + SimpleEventHelper.enable(r, true); + + final Path newDest = Paths.get(".", "new.jfr"); + r.setDestination(newDest); + System.out.println("new dest: " + r.getDestination()); + Asserts.assertEquals(newDest, r.getDestination(), "Wrong get/set dest when new"); + + r.start(); + SimpleEventHelper.createEvent(0); + Thread.sleep(100); + final Path runningDest = Paths.get(".", "running.jfr"); + r.setDestination(runningDest); + System.out.println("running dest: " + r.getDestination()); + Asserts.assertEquals(runningDest, r.getDestination(), "Wrong get/set dest when running"); + SimpleEventHelper.createEvent(1); + + r.stop(); + SimpleEventHelper.createEvent(2); + + // Expect recording to be saved at destination that was set when + // the recording was stopped, which is runningDest. + Asserts.assertTrue(Files.exists(runningDest), "No recording file: " + runningDest); + List events = RecordingFile.readAllEvents(runningDest); + Asserts.assertFalse(events.isEmpty(), "No event found"); + System.out.printf("Found event %s%n", events.get(0).getEventType().getName()); + r.close(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/destination/TestDestToDiskFalse.java 2019-02-08 18:33:02.361245211 +0300 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 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.api.recording.destination; + + +import static jdk.test.lib.Asserts.assertEquals; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @summary Basic test for setDestination with disk=false + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.destination.TestDestToDiskFalse + */ +public class TestDestToDiskFalse { + + public static void main(String[] args) throws Throwable { + final Path dest = Paths.get(".", "my.jfr"); + Recording r = new Recording(); + SimpleEventHelper.enable(r, true); + r.setToDisk(false); + r.setDestination(dest); + Asserts.assertEquals(dest, r.getDestination(), "Wrong get/set destination"); + r.start(); + SimpleEventHelper.createEvent(0); + r.stop(); + + // setToDisk(false) should not effect setDestination. + // We should still get a file when the recording stops. + Asserts.assertTrue(Files.exists(dest), "No recording file: " + dest); + System.out.printf("File size=%d, getSize()=%d%n", Files.size(dest), r.getSize()); + Asserts.assertNotEquals(Files.size(dest), 0L, "File length 0. Should at least be some bytes"); + + List events = RecordingFile.readAllEvents(dest); + Asserts.assertFalse(events.isEmpty(), "No event found"); + System.out.printf("Found event %s%n", events.get(0).getEventType().getName()); + + assertEquals(r.getSize(), 0L, "getSize() should return 0, chunks should have been released at stop"); + // getDestination() should return null after recording have been written to file. + Asserts.assertNull(r.getDestination(), "getDestination() should return null after file created"); + + r.close(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/destination/TestDestToDiskTrue.java 2019-02-08 18:33:02.505240186 +0300 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 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.api.recording.destination; + +import static jdk.test.lib.Asserts.assertEquals; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @summary Basic test for setDestination with disk=true + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.destination.TestDestToDiskTrue + */ +public class TestDestToDiskTrue { + + public static void main(String[] args) throws Throwable { + Path dest = Paths.get(".", "my.jfr"); + Recording r = new Recording(); + r.enable(EventNames.OSInformation); + r.setToDisk(true); + r.setDestination(dest); + Asserts.assertEquals(dest, r.getDestination(), "Wrong get/set destination"); + r.start(); + r.stop(); + + Asserts.assertEquals(dest, r.getDestination(), "Wrong get/set destination after stop"); + + Asserts.assertTrue(Files.exists(dest), "No recording file: " + dest); + List events = RecordingFile.readAllEvents(dest); + Asserts.assertFalse(events.isEmpty(), "No event found"); + System.out.printf("Found event %s%n", events.get(0).getEventType().getName()); + System.out.printf("File size=%d, getSize()=%d%n", Files.size(dest), r.getSize()); + assertEquals(r.getSize(), 0L, "getSize() should return 0, chunks should have be released at stop"); + Asserts.assertNotEquals(Files.size(dest), 0L, "File length 0. Should at least be some bytes"); + r.close(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/destination/TestDestWithDuration.java 2019-02-08 18:33:02.649235160 +0300 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 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.api.recording.destination; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @summary Test that recording is auto closed after duration + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.destination.TestDestWithDuration + */ +public class TestDestWithDuration { + + public static void main(String[] args) throws Throwable { + Path dest = Paths.get(".", "my.jfr"); + Recording r = new Recording(); + SimpleEventHelper.enable(r, true); + r.setDestination(dest); + r.start(); + SimpleEventHelper.createEvent(1); + + // Waiting for recording to auto close after duration + r.setDuration(Duration.ofSeconds(1)); + System.out.println("Waiting for recording to auto close after duration"); + CommonHelper.waitForRecordingState(r, RecordingState.CLOSED); + System.out.println("recording state = " + r.getState()); + Asserts.assertEquals(r.getState(), RecordingState.CLOSED, "Recording not closed"); + + Asserts.assertTrue(Files.exists(dest), "No recording file: " + dest); + System.out.printf("Recording file size=%d%n", Files.size(dest)); + Asserts.assertNotEquals(Files.size(dest), 0L, "File length 0. Should at least be some bytes"); + + List events = RecordingFile.readAllEvents(dest); + Asserts.assertFalse(events.isEmpty(), "No event found"); + System.out.printf("Found event %s%n", events.get(0).getEventType().getName()); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/dump/TestDump.java 2019-02-08 18:33:02.793230134 +0300 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 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.api.recording.dump; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @summary Test copyTo and parse file + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.dump.TestDump + */ +public class TestDump { + + public static void main(String[] args) throws Exception { + Recording r = new Recording(); + r.enable(EventNames.OSInformation); + r.start(); + r.stop(); + + Path path = Paths.get(".", "my.jfr"); + r.dump(path); + r.close(); + + Asserts.assertTrue(Files.exists(path), "Recording file does not exist: " + path); + Asserts.assertFalse(RecordingFile.readAllEvents(path).isEmpty(), "No events found"); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/dump/TestDumpInvalid.java 2019-02-08 18:33:02.937225109 +0300 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 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.api.recording.dump; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.VoidFunction; + +/** + * @test + * @summary Test copyTo and parse file + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.dump.TestDumpInvalid + */ +public class TestDumpInvalid { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + r.enable(EventNames.OSInformation); + r.start(); + r.stop(); + + verifyNullPointer(()->{r.dump(null);}, "No NullPointerException"); + + Path pathNotExists = Paths.get(".", "dirNotExists", "my.jfr"); + verifyFileNotFound(()->{r.dump(pathNotExists);}, "No Exception with missing dir"); + + Path pathEmpty = Paths.get(""); + verifyFileNotFound(()->{r.dump(pathEmpty);}, "No Exception with empty path"); + + Path pathDir = Paths.get(".", "newdir"); + Files.createDirectory(pathDir); + verifyFileNotFound(()->{r.dump(pathDir);}, "No Exception with dir"); + + // Verify that copyTo() works after all failed attempts. + Path pathOk = Paths.get(".", "newdir", "my.jfr"); + r.dump(pathOk); + Asserts.assertTrue(Files.exists(pathOk), "Recording file does not exist: " + pathOk); + Asserts.assertFalse(RecordingFile.readAllEvents(pathOk).isEmpty(), "No events found"); + + r.close(); + } + + private static void verifyFileNotFound(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, IOException.class); + } + + private static void verifyNullPointer(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, NullPointerException.class); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/dump/TestDumpLongPath.java 2019-02-08 18:33:03.081220084 +0300 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 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.api.recording.dump; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.FileHelper; + +/** + * @test + * @summary Test copyTo and parse file + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.dump.TestDumpLongPath + */ +public class TestDumpLongPath { + + public static void main(String[] args) throws Exception { + Recording r = new Recording(); + final String eventPath = EventNames.OSInformation; + r.enable(eventPath); + r.start(); + r.stop(); + + Path dir = FileHelper.createLongDir(Paths.get(".")); + Path path = Paths.get(dir.toString(), "my.jfr"); + r.dump(path); + r.close(); + + Asserts.assertTrue(Files.exists(path), "Recording file does not exist: " + path); + List events = RecordingFile.readAllEvents(path); + Asserts.assertFalse(events.isEmpty(), "No events found"); + String foundEventPath = events.get(0).getEventType().getName(); + System.out.printf("Found event: %s%n", foundEventPath); + Asserts.assertEquals(foundEventPath, eventPath, "Wrong event"); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/dump/TestDumpMultiple.java 2019-02-08 18:33:03.225215058 +0300 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 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.api.recording.dump; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @summary Test copyTo and parse file + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.dump.TestDumpMultiple + */ +public class TestDumpMultiple { + + public static void main(String[] args) throws Exception { + Recording rA = new Recording(); + Recording rB = new Recording(); + + // Enable event in one recording and disable in the other. + // Both recordings should still get the events, since we always + // get the "union" of all settings. + SimpleEventHelper.enable(rA, true); + SimpleEventHelper.enable(rB, false); + + SimpleEventHelper.createEvent(0); // To no recording + + rA.start(); + SimpleEventHelper.createEvent(1); // Only to recA + + rB.start(); + SimpleEventHelper.createEvent(2); // To both recordings + + rA.stop(); + + // This event will not be in recB. + // The reason is that recA has stopped so event is no longer enabled. + SimpleEventHelper.createEvent(3); + + // Enable the event and create a new event for recB + SimpleEventHelper.enable(rB, true); + SimpleEventHelper.createEvent(4); + + rB.stop(); + SimpleEventHelper.createEvent(5); // To no recording + + Path pathA = Paths.get(".", "recA.jfr"); + Path pathB = Paths.get(".", "recB.jfr"); + rA.dump(pathA); + rB.dump(pathB); + rB.close(); + rA.close(); + + verifyRecording(pathA, 1, 2); + verifyRecording(pathB, 2, 4); + } + + private static void verifyRecording(Path path, int... ids) throws Exception { + Asserts.assertTrue(Files.exists(path), "Recording file does not exist: " + path); + int countEvent = 0; + for (RecordedEvent event : RecordingFile.readAllEvents(path)) { + int id = Events.assertField(event, "id").getValue(); + System.out.printf("Recording '%s' id=%d%n", path, id); + Asserts.assertTrue(ids.length > countEvent, "Found extra event"); + Events.assertField(event, "id").equal(ids[countEvent]); + ++countEvent; + } + // We expect exactly 4 events in each file. 2 events * 2 chunks + Asserts.assertEquals(countEvent, ids.length, "Found too few events"); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/dump/TestDumpReadOnly.java 2019-02-08 18:33:03.369210033 +0300 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 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.api.recording.dump; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.jfr.Recording; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.FileHelper; +import jdk.test.lib.jfr.VoidFunction; + +/** + * @test + * @summary Test copyTo and parse file + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.dump.TestDumpReadOnly + */ +public class TestDumpReadOnly { + + private static final String OS_INFORMATION = EventNames.OSInformation; + + public static void main(String[] args) throws Throwable { + + Path readOnlyDir = FileHelper.createReadOnlyDir(Paths.get(".", "readonlydir")); + if (!FileHelper.isReadOnlyPath(readOnlyDir)) { + System.out.println("Failed to create read-only path. Maybe running a root? Test skipped"); + return; + } + Recording r = new Recording(); + r.enable(OS_INFORMATION); + r.start(); + r.stop(); + Path path = Paths.get(readOnlyDir.toString(), "my.jfr"); + verifyException(()->{r.dump(path);}, "No Exception when dumping read-only dir"); + r.close(); + } + + private static void verifyException(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, IOException.class); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/dump/TestDumpState.java 2019-02-08 18:33:03.509205147 +0300 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 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.api.recording.dump; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEvent; +import jdk.test.lib.jfr.SimpleEventHelper; +import jdk.test.lib.jfr.VoidFunction; + +/** + * @test + * @summary call copyTo() with recording in all states. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.dump.TestDumpState + */ +public class TestDumpState { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + SimpleEventHelper.enable(r, true); + + List expectedIds = new ArrayList<>(); + + SimpleEventHelper.createEvent(0); // Recording not started, should not be included. + verifyIOException(()->{checkEvents(r, expectedIds);}, "No Exception when dump() not started"); + + r.start(); + SimpleEventHelper.createEvent(1); + expectedIds.add(1); + checkEvents(r, expectedIds); + + SimpleEventHelper.createEvent(2); + expectedIds.add(2); + checkEvents(r, expectedIds); + + r.stop(); + checkEvents(r, expectedIds); + + SimpleEventHelper.createEvent(3); // Recording stopped, should not be included. + checkEvents(r, expectedIds); + + r.close(); + SimpleEventHelper.createEvent(4); + verifyIOException(()->{checkEvents(r, expectedIds);}, "No Exception when dump() closed"); + } + + private static int recordingCounter = 0; + private static void checkEvents(Recording r, List expectedIds) throws Exception { + Path path = Paths.get(".", String.format("%d.jfr", recordingCounter++)); + r.dump(path); + Asserts.assertTrue(Files.exists(path), "Recording file does not exist: " + path); + + int index = 0; + + for (RecordedEvent event : RecordingFile.readAllEvents(path)) { + Events.isEventType(event, SimpleEvent.class.getName()); + Integer id = Events.assertField(event, "id").getValue(); + System.out.println("Got event with id " + id); + Asserts.assertGreaterThan(expectedIds.size(), index, "Too many Events found"); + Asserts.assertEquals(id, expectedIds.get(index), "Wrong id at index " + index); + ++index; + } + Asserts.assertEquals(index, expectedIds.size(), "Too few Events found"); + } + + private static void verifyIOException(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, IOException.class); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/event/TEST.properties 2019-02-08 18:33:03.657199982 +0300 @@ -0,0 +1,2 @@ +modules = java.management + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/event/TestChunkPeriod.java 2019-02-08 18:33:03.805194818 +0300 @@ -0,0 +1,103 @@ +/* + * Copyright (c) 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.api.recording.event; + +import java.io.IOException; +import java.time.Duration; +import java.time.Instant; +import java.util.List; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEvent; + +/** + * @test + * @summary Test periodic setting that involves chunks. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.event.TestChunkPeriod + */ +public class TestChunkPeriod { + + // Margin of error is to avoid issues where JFR and + // System.currentMillis take the clock differently + private static final Duration MARGIN_OF_ERROR = Duration.ofNanos(1_000_000_000); // 1 s + + public static void main(String[] args) throws Throwable { + FlightRecorder.addPeriodicEvent(SimpleEvent.class, () -> { + SimpleEvent pe = new SimpleEvent(); + pe.commit(); + }); + testBeginChunk(); + testEndChunk(); + testEveryChunk(); + } + + private static void testBeginChunk() throws IOException { + Recording r = new Recording(); + r.enable(SimpleEvent.class).with("period", "beginChunk"); + Instant beforeStart = Instant.now().minus(MARGIN_OF_ERROR); + r.start(); + Instant afterStart = Instant.now().plus(MARGIN_OF_ERROR); + r.stop(); + List events = Events.fromRecording(r); + Asserts.assertEquals(events.size(), 1, "Expected one event with beginChunk"); + RecordedEvent event = events.get(0); + Asserts.assertGreaterThanOrEqual(event.getStartTime(), beforeStart); + Asserts.assertGreaterThanOrEqual(afterStart, event.getStartTime()); + r.close(); + } + + private static void testEndChunk() throws IOException { + Recording r = new Recording(); + r.enable(SimpleEvent.class).with("period", "endChunk"); + r.start(); + Instant beforeStop = Instant.now().minus(MARGIN_OF_ERROR); + r.stop(); + Instant afterStop = Instant.now().plus(MARGIN_OF_ERROR); + List events = Events.fromRecording(r); + Asserts.assertEquals(events.size(), 1, "Expected one event with endChunk"); + RecordedEvent event = events.get(0); + Asserts.assertGreaterThanOrEqual(event.getStartTime(), beforeStop); + Asserts.assertGreaterThanOrEqual(afterStop, event.getStartTime()); + r.close(); + } + + private static void testEveryChunk() throws IOException { + Recording r = new Recording(); + r.enable(SimpleEvent.class).with("period", "everyChunk"); + r.start(); + r.stop(); + List events = Events.fromRecording(r); + Asserts.assertEquals(events.size(), 2, "Expected two events with everyChunk"); + r.close(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/event/TestEnableClass.java 2019-02-08 18:33:03.953189652 +0300 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 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.api.recording.event; + +import jdk.jfr.Recording; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @summary Simple enable Event class. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.event.TestEnableClass + */ +public class TestEnableClass { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + SimpleEventHelper.enable(r, true); + SimpleEventHelper.createEvent(0); + r.start(); + SimpleEventHelper.createEvent(1); + r.stop(); + SimpleEventHelper.createEvent(2); + SimpleEventHelper.verifyEvents(r, 1); + r.close(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/event/TestEnableName.java 2019-02-08 18:33:04.097184627 +0300 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 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.api.recording.event; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Iterator; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Simple enable Event class. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.event.TestEnableName + */ +public class TestEnableName { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + final String eventType = EventNames.FileWrite; + r.enable(eventType).withoutStackTrace(); + r.start(); + Files.write(Paths.get(".", "dummy.txt"), "DummyFile".getBytes()); + r.stop(); + + Iterator iterator = Events.fromRecording(r).iterator(); + Asserts.assertTrue(iterator.hasNext(), "No events found"); + System.out.printf("Event:%n%s%n", iterator.next()); + r.close(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/event/TestEventTime.java 2019-02-08 18:33:04.237179742 +0300 @@ -0,0 +1,184 @@ +/* + * Copyright (c) 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.api.recording.event; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test getStartTime() and getEndTime(). Verify startTime <= endTime + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.event.TestEventTime + */ +public class TestEventTime { + + static List actualOrder = new ArrayList<>(); + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + r.enable(MyEvent.class).withoutStackTrace(); + // for debugging time related issues + r.enable(EventNames.CPUTimeStampCounter); + r.start(); + MyEvent event1 = beginEvent(1); + MyEvent event2 = beginEvent(2); + endEvent(event1); + MyEvent event3 = beginEvent(3); + endEvent(event2); + endEvent(event3); + + r.stop(); + + List events = Events.fromRecording(r); + + RecordedEvent recEvent1 = findEvent(1, events); + RecordedEvent recEvent2 = findEvent(2, events); + RecordedEvent recEvent3 = findEvent(3, events); + + List recordedOrder = new ArrayList<>(); + recordedOrder.add(new TimeEvent(1, true, recEvent1.getStartTime())); + recordedOrder.add(new TimeEvent(1, false, recEvent1.getEndTime())); + recordedOrder.add(new TimeEvent(2, true, recEvent2.getStartTime())); + recordedOrder.add(new TimeEvent(2, false, recEvent2.getEndTime())); + recordedOrder.add(new TimeEvent(3, true, recEvent3.getStartTime())); + recordedOrder.add(new TimeEvent(3, false, recEvent3.getEndTime())); + Collections.sort(recordedOrder); + + printTimedEvents("Actual order", actualOrder); + printTimedEvents("Recorded order", recordedOrder); + + for (int i = 0; i < 6; i++) { + if (!actualOrder.get(i).equals(recordedOrder.get(i))) { + throw new Exception("Event times not in expected order. Was " + recordedOrder.get(1) + " but expected " + actualOrder.get(1)); + } + } + } + + private static void printTimedEvents(String heading, List recordedOrder) { + System.out.println(); + System.out.println(heading); + System.out.println("======================"); + for (TimeEvent t : recordedOrder) { + System.out.println(t.toString()); + } + } + + private static MyEvent beginEvent(int id) throws Exception { + MyEvent event = new MyEvent(id); + event.begin(); + if (!CommonHelper.hasFastTimeEnabled()) { + CommonHelper.waitForSystemCurrentMillisToChange();; + } + actualOrder.add(new TimeEvent(id, true)); + return event; + } + + private static void endEvent(MyEvent event) throws Exception { + event.end(); + if (!CommonHelper.hasFastTimeEnabled()) { + CommonHelper.waitForSystemCurrentMillisToChange();; + } + event.commit(); + actualOrder.add(new TimeEvent(event.id, false)); + } + + private final static class TimeEvent implements Comparable { + long id; + private boolean begin; + private Instant time; + + public TimeEvent(int id, boolean begin) { + this.id = id; + this.begin = begin; + } + + public TimeEvent(int id, boolean begin, Instant time) { + this(id, begin); + this.time = time; + } + + @Override + public int compareTo(TimeEvent that) { + return this.time.compareTo(that.time); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + if (begin) { + sb.append("begin"); + } else { + sb.append("end"); + } + sb.append("Event"); + sb.append("("); + sb.append(id); + sb.append(")"); + return sb.toString(); + } + + public boolean equals(Object thatObject) { + if (thatObject instanceof TimeEvent) { + TimeEvent that = (TimeEvent) thatObject; + return that.id == this.id && that.begin == this.begin; + } + return false; + } + } + + private static RecordedEvent findEvent(int id, List events) { + for (RecordedEvent event : events) { + if (!event.getEventType().getName().equals(EventNames.CPUTimeStampCounter)) { + int eventId = Events.assertField(event, "id").getValue(); + if (eventId == id) { + return event; + } + } + } + Asserts.fail("No event with id " + id); + return null; + } + + private static class MyEvent extends Event { + int id; + + public MyEvent(int id) { + this.id = id; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/event/TestLoadEventAfterStart.java 2019-02-08 18:33:04.381174717 +0300 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 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.api.recording.event; + +import java.lang.reflect.Constructor; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Load event class after recording started. + * @key jfr + * + * @library /lib / + * @build jdk.test.lib.jfr.SimpleEvent + * @run main/othervm jdk.jfr.api.recording.event.TestLoadEventAfterStart + */ +public class TestLoadEventAfterStart { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + r.start(); + + ClassLoader classLoader = TestLoadEventAfterStart.class.getClassLoader(); + Class eventClass = + classLoader.loadClass("jdk.test.lib.jfr.SimpleEvent").asSubclass(Event.class); + + r.enable(eventClass).withThreshold(Duration.ofMillis(0)).withoutStackTrace(); + createEvent(eventClass, 1); + r.disable(eventClass); + createEvent(eventClass, 2); + r.enable(eventClass).withThreshold(Duration.ofMillis(0)).withoutStackTrace(); + createEvent(eventClass, 3); + + r.stop(); + verifyEvents(r, 1, 3); + } + + private static void verifyEvents(Recording r, int ... ids) throws Exception { + List eventIds = new ArrayList<>(); + for (RecordedEvent event : Events.fromRecording(r)) { + int id = Events.assertField(event, "id").getValue(); + System.out.println("Event id:" + id); + eventIds.add(id); + } + Asserts.assertEquals(eventIds.size(), ids.length, "Wrong number of events"); + for (int i = 0; i < ids.length; ++i) { + Asserts.assertEquals(eventIds.get(i).intValue(), ids[i], "Wrong id in event"); + } + } + + private static void createEvent(Class eventClass, int id) throws Exception { + Constructor constructor = eventClass.getConstructor(); + Event event = (Event) constructor.newInstance(); + event.begin(); + eventClass.getDeclaredField("id").setInt(event, id); + event.end(); + event.commit(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/event/TestPeriod.java 2019-02-08 18:33:04.525169693 +0300 @@ -0,0 +1,127 @@ +/* + * Copyright (c) 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.api.recording.event; + +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test event period. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.event.TestPeriod + */ +public class TestPeriod { + private static final String EVENT_PATH = EventNames.ThreadAllocationStatistics; + private static final long ERROR_MARGIN = 20; // 186 ms has been measured, when period was set to 200 ms + + public static void main(String[] args) throws Throwable { + long[] periods = { 100, 200 }; + int eventCount = 4; + int deltaCount; + for (long period : periods) { + List deltaBetweenEvents; + do { + deltaBetweenEvents = createPeriodicEvents(period, eventCount); + deltaCount = deltaBetweenEvents.size(); + if (deltaCount < eventCount - 1) { + System.out.println("Didn't get sufficent number of events. Retrying..."); + System.out.println(); + } + } while (deltaCount < eventCount - 1); + for (int i = 0; i < eventCount - 1; i++) { + verifyDelta(deltaBetweenEvents.get(i), period); + } + System.out.println(); + } + } + + private static List createPeriodicEvents(long period, int eventCount) throws Exception, IOException { + System.out.println("Provoking events with period " + period + " ms"); + Recording r = new Recording(); + r.start(); + runWithPeriod(r, period, eventCount + 1); + r.stop(); + + long prevTime = -1; + List deltas = new ArrayList<>(); + for (RecordedEvent event : Events.fromRecording(r)) { + if (Events.isEventType(event, EVENT_PATH) && isMyThread(event)) { + long timeMillis = event.getEndTime().toEpochMilli(); + if (prevTime != -1) { + long delta = timeMillis - prevTime; + deltas.add(delta); + System.out.printf("event: time=%d, delta=%d%n", timeMillis, delta); + } + prevTime = timeMillis; + } + } + r.close(); + return deltas; + } + + // We only check that time is at least as expected. + // We ignore if time is much longer than expected, since anything can happen + // during heavy load, + private static void verifyDelta(long actual, long expected) { + System.out.printf("verifyDelta: actaul=%d, expected=%d (errorMargin=%d)%n", actual, expected, ERROR_MARGIN); + Asserts.assertGreaterThan(actual, expected - ERROR_MARGIN, "period delta too short"); + } + + private static boolean isMyThread(RecordedEvent event) { + Object o = event.getValue("thread"); + if (o instanceof RecordedThread) { + RecordedThread rt = (RecordedThread) o; + return Thread.currentThread().getId() == rt.getJavaThreadId(); + } + return false; + } + + @SuppressWarnings("unused") + private static byte[] dummy = null; + + // Generate at least minEvents event with given period + private static void runWithPeriod(Recording r, long period, int minEventCount) throws Exception { + r.enable(EVENT_PATH).withPeriod(Duration.ofMillis(period)); + long endTime = System.currentTimeMillis() + period * minEventCount; + while (System.currentTimeMillis() < endTime) { + dummy = new byte[100]; + Thread.sleep(1); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/event/TestReEnableClass.java 2019-02-08 18:33:04.673164528 +0300 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 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.api.recording.event; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @summary Enable, disable, enable event during recording. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.event.TestReEnableClass + */ +public class TestReEnableClass { + + public static void main(String[] args) throws Throwable { + test(false); + test(true); + } + + // Loop and enable/disable events. + // Verify recording only contains event created during enabled. + private static void test(boolean isEnabled) throws Exception { + System.out.println("Start with isEnabled = " + isEnabled); + + List expectedIds = new ArrayList<>(); + Recording r = new Recording(); + SimpleEventHelper.enable(r, isEnabled); + r.start(); + + for (int i = 0; i < 10; ++i) { + SimpleEventHelper.createEvent(i); + if (isEnabled) { + expectedIds.add(i); + } + isEnabled = !isEnabled; + SimpleEventHelper.enable(r, isEnabled); + } + + r.stop(); + SimpleEventHelper.createEvent(100); + + int[] ids = new int[expectedIds.size()]; + for (int i = 0; i < expectedIds.size(); ++i) { + ids[i] = expectedIds.get(i); + } + SimpleEventHelper.verifyEvents(r, ids); + + r.close(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/event/TestReEnableMultiple.java 2019-02-08 18:33:04.817159503 +0300 @@ -0,0 +1,147 @@ +/* + * Copyright (c) 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.api.recording.event; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Random; + +import jdk.jfr.EventType; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEvent; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @summary Enable, disable, enable event during recording. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.event.TestReEnableMultiple + */ +public class TestReEnableMultiple { + private static final String EVENT_PATH = EventNames.FileWrite; + private static final Random rand = new Random(0); + + public static void main(String[] args) throws Throwable { + Recording rA = new Recording(); + Recording rB = new Recording(); + + final Path path = Paths.get(".", "dummy.txt").toAbsolutePath(); + rA.start(); + rB.start(); + + SimpleEventHelper.enable(rA, false); + SimpleEventHelper.enable(rA, false); + + int expectedMyEvents = 0; + int expectedIoEvents = 0; + for (int i = 0; i < 20; ++i) { + SimpleEventHelper.createEvent(i); + if (isMyEventEnabled(rA, rB)) { + expectedMyEvents++; + System.out.println("Expect MyEvent.id " + i); + } + + Files.write(path, "A".getBytes()); + if (isIoEnabled(rA, rB)) { + expectedIoEvents++; + } + + for (int j = 0; j < 4; ++j) { + Recording r = (rand.nextInt(2) == 0) ? rA : rB; + updateSettings(r); + } + } + + rA.stop(); + rB.stop(); + + verifyEventCount(rA, expectedMyEvents, expectedIoEvents, path); + verifyEventCount(rB, expectedMyEvents, expectedIoEvents, path); + + rA.close(); + rB.close(); + } + + private static void verifyEventCount(Recording r, int expectedMyEvents, int expectedIoEvents, Path path) throws Exception { + int actualMyEvents = 0; + int actualIoEvents = 0; + for (RecordedEvent event : Events.fromRecording(r)) { + if (Events.isEventType(event, EVENT_PATH)) { + if (path.toString().equals(Events.assertField(event, "path").getValue())) { + actualIoEvents++; + } + } else { + Asserts.assertTrue(Events.isEventType(event, SimpleEvent.class.getName())); + System.out.println("Got MyEvent.id=" + Events.assertField(event, "id").getValue()); + actualMyEvents++; + } + } + System.out.printf("MyEvents: expected=%d, actual=%d%n", expectedMyEvents, actualMyEvents); + System.out.printf("IoEvents: expected=%d, actual=%d%n", expectedIoEvents, actualIoEvents); + Asserts.assertEquals(expectedMyEvents, actualMyEvents, "Wrong number of MyEvents"); + Asserts.assertEquals(expectedIoEvents, actualIoEvents, "Wrong number of IoEvents"); + } + + private static void updateSettings(Recording r) { + boolean doEnable = rand.nextInt(3) == 0; // Disable 2 of 3, since event + // is enabled by union of + // recordings. + boolean doMyEvent = rand.nextInt(2) == 0; + if (doMyEvent) { + SimpleEventHelper.enable(r, doEnable); + } else { + if (doEnable) { + r.enable(EVENT_PATH).withoutStackTrace(); + } else { + r.disable(EVENT_PATH); + } + } + } + + private static boolean isMyEventEnabled(Recording rA, Recording rB) { + long eventTypeId = EventType.getEventType(SimpleEvent.class).getId(); + String settingName = eventTypeId + "#enabled"; + return isEnabled(rA, settingName) || isEnabled(rB, settingName); + } + + private static boolean isIoEnabled(Recording rA, Recording rB) { + String settingName = EVENT_PATH + "#enabled"; + return isEnabled(rA, settingName) || isEnabled(rB, settingName); + } + + private static boolean isEnabled(Recording r, String settingName) { + // System.out.printf("R(%s) %s=%s%n", r.getName(), settingName, + // r.getSettings().get(settingName)); + return Boolean.parseBoolean(r.getSettings().get(settingName)); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/event/TestReEnableName.java 2019-02-08 18:33:04.961154478 +0300 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 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.api.recording.event; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Enable/disable event by name during recording. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.event.TestReEnableName + */ +public class TestReEnableName { + + public static void main(String[] args) throws Throwable { + testRecordingWithEnabledEvent(EventNames.FileWrite, false); + testRecordingWithEnabledEvent(EventNames.FileWrite, true); + } + + // Loop and enable/disable events. Create events each loop. + // Verify we only get events created during enabled. + private static void testRecordingWithEnabledEvent(String eventname, boolean enabled) throws Exception { + System.out.println("Testing enabled=" + enabled + " for " + eventname); + final Path pathDisabled = Paths.get(".", "disabled.txt").toAbsolutePath(); + final Path pathEnabled = Paths.get(".", "enabled.txt").toAbsolutePath(); + + Recording r = new Recording(); + if (enabled) { + r.enable(eventname).withoutThreshold().withoutStackTrace(); + } else { + r.disable(eventname).withoutThreshold().withoutStackTrace(); + } + r.start(); + + // We should only get events for pathEnabled, not for pathDisabled. + int countExpectedEvents = 0; + for (int i = 0; i < 10; ++i) { + if (enabled) { + Files.write(pathEnabled, "E".getBytes()); + ++countExpectedEvents; + r.disable(eventname); + } else { + Files.write(pathDisabled, "D".getBytes()); + r.enable(eventname); + } + enabled = !enabled; + } + r.stop(); + + int countFoundEvents = 0; + for (RecordedEvent event : Events.fromRecording(r)) { + System.out.printf("Event %s%n", event); + Asserts.assertEquals(eventname, event.getEventType().getName(), "Wrong event type"); + String path = Events.assertField(event, "path").getValue(); + System.out.println(path); + if (pathEnabled.toString().equals(path)) { + ++countFoundEvents; + } + } + Asserts.assertGreaterThanOrEqual(countFoundEvents, countExpectedEvents, "Too few events found"); + + r.close(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/event/TestRecordingEnableDisable.java 2019-02-08 18:33:05.105149454 +0300 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 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.api.recording.event; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Random; + +import jdk.jfr.EventType; +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.SimpleEvent; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @summary Enable, disable, enable event during recording. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.event.TestRecordingEnableDisable + */ +public class TestRecordingEnableDisable { + private static final String EVENT_PATH = "java.file_write"; + private static final Random rand = new Random(0); + + public static void main(String[] args) throws Throwable { + + Recording rA = new Recording(); + Recording rB = new Recording(); + + rA.setName("rA"); + rB.setName("rB"); + + final Path path = Paths.get(".", "my.jfr"); + rA.start(); + rB.start(); + + for (int i = 0; i < 30; ++i) { + SimpleEventHelper.createEvent(i); + if (isMyEventEnabled(rA, rB)) { + System.out.println("MyEvent enabled"); + } + else { + System.out.println("MyEvent disabled"); + } + + Files.write(path, "A".getBytes()); + if (isIoEnabled(rA, rB)) { + System.out.println("IoEvent enabled"); + } + else { + System.out.println("IoEvent disabled"); + } + Recording r = ((i % 2) == 0) ? rA : rB; + updateSettings(r); + } + + rA.stop(); + rB.stop(); + rA.close(); + rB.close(); + } + + + private static void updateSettings(Recording r) { + int operationIndex = rand.nextInt(4); + switch (operationIndex) { + case 0: + SimpleEventHelper.enable(r, true); + break; + case 1: + SimpleEventHelper.enable(r, false); + break; + case 2: + r.enable(EVENT_PATH).withoutStackTrace(); + break; + case 3: + r.disable(EVENT_PATH); + break; + default: + Asserts.fail("Wrong operataionIndex. Test error"); + } + } + + private static boolean isMyEventEnabled(Recording rA, Recording rB) { + long eventTypeId = EventType.getEventType(SimpleEvent.class).getId(); + String settingName = "@" + eventTypeId + "#enabled"; + return isEnabled(rA, settingName) || isEnabled(rB, settingName); + } + + private static boolean isIoEnabled(Recording rA, Recording rB) { + String settingName = EVENT_PATH + "#enabled"; + return isEnabled(rA, settingName) || isEnabled(rB, settingName); + } + + private static boolean isEnabled(Recording r, String settingName) { + System.out.printf("R(%s) %s=%s%n", r.getName(), settingName, r.getSettings().get(settingName)); + return Boolean.parseBoolean(r.getSettings().get(settingName)); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/event/TestThreshold.java 2019-02-08 18:33:05.245144568 +0300 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 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.api.recording.event; + +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test event threshold. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.event.TestThreshold + */ +public class TestThreshold { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + + r.start(); + r.enable(MyEvent.class).withThreshold(Duration.ofMillis(500)); + createEvent(0, 100); + createEvent(1, 600); + createEvent(2, 100); + r.stop(); + + List events = Events.fromRecording(r); + Asserts.assertTrue(1 <= events.size(), "Should get at most 1 event"); + r.close(); + } + + private static void createEvent(int id, long duration) throws Exception { + MyEvent event = new MyEvent(id); + long start = System.currentTimeMillis(); + + event.begin(); + sleepUntil(start + duration); + event.end(); + + long actualDuration = System.currentTimeMillis() - start; + System.out.printf("targetDuration=%d, actualDuration=%d, shouldCommit=%b%n", + duration, actualDuration, event.shouldCommit()); + event.commit(); + } + + private static void sleepUntil(long endTime) throws Exception { + long sleepTime = endTime - System.currentTimeMillis(); + while(sleepTime > 0) { + Thread.sleep(sleepTime); + sleepTime = endTime - System.currentTimeMillis(); + } + } + + static class MyEvent extends Event { + public int id; + public MyEvent(int id) { + this.id = id; + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/misc/TestGetId.java 2019-02-08 18:33:05.389139544 +0300 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 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.api.recording.misc; + +import java.util.HashMap; +import java.util.Map; + +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Verify that each recording get unique a id + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.misc.TestGetId + */ +public class TestGetId { + + public static void main(String[] args) throws Throwable { + Map recordings = new HashMap<>(); + + final int iterations = 100; + for (int i = 0; i < iterations; ++i) { + Recording newRecording = new Recording(); + System.out.println("created: " + newRecording.getId()); + Recording oldRecording = recordings.get(newRecording.getId()); + Asserts.assertNull(oldRecording, "Duplicate recording ID: " + newRecording.getId()); + recordings.put(newRecording.getId(), newRecording); + + if (i % 3 == 0) { + Recording closeRecording = recordings.remove(recordings.keySet().iterator().next()); + Asserts.assertNotNull(closeRecording, "Could not find recording in map. Test error."); + closeRecording.close(); + System.out.println("closed: " + closeRecording.getId()); + } + } + + for (Recording r : recordings.values()) { + r.close(); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/misc/TestGetSize.java 2019-02-08 18:33:05.533134520 +0300 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 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.api.recording.misc; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertTrue; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.jfr.Recording; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @summary Test recording file size with Recording.getSize() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.misc.TestGetSize + */ +public class TestGetSize { + + public static void main(String[] args) throws Throwable { + Path dest = Paths.get(".", "my.jfr"); + Recording r = new Recording(); + r.setToDisk(true); + r.enable(EventNames.OSInformation); + assertEquals(r.getSize(), 0L, "getSize should return 0 before recording starts"); + r.start(); + r.stop(); + r.dump(dest); + assertTrue(Files.exists(dest), "TestGetSize recording missing: " + dest); + System.out.printf("%s size: %d%n", dest, Files.size(dest)); + System.out.printf("r.getSize(): %d%n", r.getSize()); + assertEquals(Files.size(dest), r.getSize(), "TestGetSize wrong recording size"); + r.close(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/misc/TestGetSizeToMem.java 2019-02-08 18:33:05.677129495 +0300 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 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.api.recording.misc; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertTrue; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @summary Test recording file size with Recording.getSize() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.misc.TestGetSizeToMem + */ +public class TestGetSizeToMem { + + + public static void main(String[] args) throws Throwable { + Path dest = Paths.get(".", "my.jfr"); + Recording r = new Recording(); + r.setToDisk(false); + r.setDestination(dest); + r.enable(EventNames.OSInformation); + r.start(); + r.stop(); + r.close(); + assertTrue(Files.exists(dest), "TestGetSize recording missing: " + dest); + System.out.printf("%s size: %d%n", dest, Files.size(dest)); + System.out.printf("r.getSize(): %d%n", r.getSize()); + Asserts.assertNotEquals(Files.size(dest), 0L, "File should not be empty"); + assertEquals(r.getSize(), 0L, "r.getSize() should be 0 after setToDisk(false)"); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/misc/TestGetStream.java 2019-02-08 18:33:05.825124331 +0300 @@ -0,0 +1,154 @@ +/* + * 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.api.recording.misc; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.SimpleEvent; + +/** + * @test + * @summary A simple test for Recording.getStream() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.misc.TestGetStream + */ +public class TestGetStream { + + private final static Instant offset = Instant.now(); + private static Instant previous; + + public static void main(String[] args) throws Exception { + + Recording recording = new Recording(); + Instant t0 = newTimestamp(); + recording.start(); + Instant t1 = newTimestamp(); + createChunkWithId(1); + Instant t2 = newTimestamp(); + createChunkWithId(2); + Instant t3 = newTimestamp(); + createChunkWithId(3); + Instant t4 = newTimestamp(); + recording.stop(); + Instant t5 = newTimestamp(); + printTimeStamp("t0", t0); + printTimeStamp("t1", t1); + printTimeStamp("t2", t2); + printTimeStamp("t3", t3); + printTimeStamp("t4", t4); + printTimeStamp("t5", t5); + + assertContainsId(recording, "t1-t4", t1, t4, 1, 2, 3); + assertContainsId(recording, "t1-t3", t1, t3, 1, 2); + assertContainsId(recording, "t2-t4", t2, t4, 2, 3); + assertContainsId(recording, "t1-t2", t1, t2, 1); + assertContainsId(recording, "t2-t3", t2, t3, 2); + assertContainsId(recording, "t3-t4", t3, t4, 3); + assertContainsId(recording, "t0-t1", t0, t1); + assertContainsId(recording, "t4-t5", t4, t5); + + assertEndBeforeBegin(); + } + + private static void printTimeStamp(String name, Instant t) { + Duration s = Duration.between(offset, t); + System.out.println(name + ": " + (s.getSeconds() * 1_000_000_000L + s.getNano())); + } + + private static void createChunkWithId(int id) throws InterruptedException { + newTimestamp(); // ensure every recording gets a unique start time + try (Recording r = new Recording()) { + r.start(); + SimpleEvent s = new SimpleEvent(); + s.id = id; + s.commit(); + r.stop(); + newTimestamp(); // ensure that start time is not equal to stop time + } + newTimestamp(); // ensure every recording gets a unique stop time + } + + // Create a timestamp that is not same as the previous one + private static Instant newTimestamp() throws InterruptedException { + while (true) { + Instant now = Instant.now(); + if (!now.equals(previous)) { + previous = now; + return now; + } + Thread.sleep(1); + } + } + + private static void assertContainsId(Recording r, String interval, Instant start, Instant end, Integer... ids) throws IOException { + List idList = new ArrayList<>(Arrays.asList(ids)); + long time = System.currentTimeMillis(); + String fileName = idList.stream().map(x -> x.toString()).collect(Collectors.joining("_", "recording-get-stream_" + time + "_", ".jfr")); + Path file = Paths.get(fileName); + try (InputStream is = r.getStream(start, end)) { + Files.copy(is, file, StandardCopyOption.REPLACE_EXISTING); + } + try (RecordingFile rf = new RecordingFile(file)) { + while (rf.hasMoreEvents()) { + RecordedEvent event = rf.readEvent(); + Integer id = event.getValue("id"); + Asserts.assertTrue(idList.contains(id), "Unexpected id " + id + " found in interval " + interval); + idList.remove(id); + } + Asserts.assertTrue(idList.isEmpty(), "Expected events with ids " + idList); + } + } + + private static void assertEndBeforeBegin() throws IOException { + try (Recording recording = new Recording()) { + recording.start(); + recording.stop(); + Instant begin = Instant.now(); + Instant end = begin.minusNanos(1); + recording.getStream(begin, end); + Asserts.fail("Expected IllegalArgumentException has not been thrown"); + } catch (IllegalArgumentException x) { + // Caught the expected exception + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/misc/TestRecordingBase.java 2019-02-08 18:33:05.973119167 +0300 @@ -0,0 +1,175 @@ +/* + * Copyright (c) 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.api.recording.misc; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertNotEquals; +import static jdk.test.lib.Asserts.assertNotNull; +import static jdk.test.lib.Asserts.assertNull; +import static jdk.test.lib.Asserts.assertTrue; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.util.Map; + +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; + +/** + * @test + * @summary Basic tests for Recording + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.misc.TestRecordingBase + */ +public class TestRecordingBase { + + public static void main(String[] args) throws Throwable { + testUninitialized(); + testUniqueIdentifier(); + testSetGetName(); + testSetGetDuration(); + testSetGetMaxAge(); + testSetGetDestination(); + testSetGetDumpOnExit(); + testSetGetToDisk(); + testSetGetToMaxSize(); + testGetSettings(); + } + + public static void testUninitialized() throws Throwable { + Recording r = new Recording(); + assertNull(r.getDuration(), "Wrong uninitialized duration"); + assertNull(r.getStartTime(), "Wrong uninitialized startTime"); + assertNull(r.getStopTime(), "Wrong uninitialized stopTime"); + assertNull(r.getDestination(), "Wrong uninitialized destination"); + assertNull(r.getMaxAge(), "Wrong uninitialized maxAge"); + assertEquals(0L, r.getMaxSize(), "Wrong uninitialized maxSize"); // TODO: Should this be null? Why Long if never null? + assertEquals(0L, r.getSize(), "Wrong uninitialized size"); + assertNotNull(r.getName(), "Uninitialized name should not be null"); + assertFalse(r.getName().isEmpty(), "Uninitialized name should not be empty"); + assertEquals(r.getState(), RecordingState.NEW, "Wrong uninitialized state"); + assertTrue(r.getSettings().isEmpty(), "Uninitialized settings should be empty"); + r.close(); + } + + public static void testUniqueIdentifier() throws Throwable { + Recording r1 = new Recording(); + Recording r2 = new Recording(); + assertNotEquals(r1.getId(), r2.getId(), "Same identifier"); + r1.close(); + r2.close(); + } + + public static void testSetGetName() throws Throwable { + Recording r = new Recording(); + final String name = "TestName"; + r.setName(name); + assertEquals(name, r.getName(), "Wrong set/get name"); + r.close(); + } + + public static void testSetGetDuration() throws Throwable { + Recording r = new Recording(); + final Duration duration = Duration.ofSeconds(60).plusMillis(50); + r.setDuration(duration); + assertEquals(duration, r.getDuration(), "Wrong set/get duration"); + r.close(); + } + + public static void testSetGetMaxAge() throws Throwable { + Recording r = new Recording(); + final Duration maxAge = Duration.ofSeconds(60).plusMillis(50); + r.setMaxAge(maxAge); + assertEquals(maxAge, r.getMaxAge(), "Wrong set/get maxAge"); + r.close(); + } + + public static void testSetGetDestination() throws Throwable { + Recording r = new Recording(); + final Path destination = Paths.get(".", "testSetGetDestination.jfr"); + r.setDestination(destination); + assertEquals(destination, r.getDestination(), "Wrong set/get destination"); + r.close(); + } + + public static void testSetGetDumpOnExit() throws Throwable { + Recording r = new Recording(); + r.setDumpOnExit(true); + assertTrue(r.getDumpOnExit(), "Wrong set/get dumpOnExit true"); + r.setDumpOnExit(false); + assertFalse(r.getDumpOnExit(), "Wrong set/get dumpOnExit false"); + r.close(); + } + + public static void testSetGetToDisk() throws Throwable { + Recording r = new Recording(); + r.setToDisk(true); + assertTrue(r.isToDisk(), "Wrong set/get isToDisk true"); + r.setToDisk(false); + assertFalse(r.isToDisk(), "Wrong set/get isToDisk false"); + r.close(); + } + + public static void testSetGetToMaxSize() throws Throwable { + Recording r = new Recording(); + final long maxSize = 10000000; + r.setMaxSize(maxSize); + assertEquals(maxSize, r.getMaxSize(), "Wrong set/get maxSize"); + r.close(); + } + + public static void testGetSettings() throws Throwable { + String eventPath = "my/test/enabledPath"; + String settingName = "myTestSetting"; + String settingValue = "myTestValue"; + + Recording r = new Recording(); + r.enable(eventPath).with(settingName, settingValue); + + boolean isEnabledPathFound = false; + boolean isSettingFound = false; + Map settings = r.getSettings(); + for (String name : settings.keySet()) { + System.out.println("name=" + name + ", value=" + settings.get(name)); + if (name.contains(eventPath) && name.contains("#enabled")) { + isEnabledPathFound = true; + assertEquals("true", settings.get(name), "Wrong value for enabled path: " + name); + } + if (name.contains(eventPath) && name.contains(settingName)) { + isSettingFound = true; + assertEquals(settingValue, settings.get(name), "Wrong value for setting: " + name); + } + } + assertTrue(isEnabledPathFound, "Enabled path not found in settings"); + assertTrue(isSettingFound, "Test setting not found in settings"); + r.close(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/misc/TestRecordingCopy.java 2019-02-08 18:33:06.117114142 +0300 @@ -0,0 +1,112 @@ +/* + * 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.api.recording.misc; + +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEvent; + +import java.util.List; + +/** + * @test + * @summary A simple test for Recording.copy() + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.misc.TestRecordingCopy + */ +public class TestRecordingCopy { + + private static final int EVENT_ID = 1001; + + public static void main(String[] args) throws Exception { + + Recording original = new Recording(); + original.enable(SimpleEvent.class); + + Recording newCopy = original.copy(false); + Asserts.assertEquals(newCopy.getState(), RecordingState.NEW); + + Recording newStoppedCopy = original.copy(true); + Asserts.assertEquals(newStoppedCopy.getState(), RecordingState.NEW); + + original.start(); + + SimpleEvent ev = new SimpleEvent(); + ev.id = EVENT_ID; + ev.commit(); + + // Verify a stopped copy + Recording stoppedCopy = original.copy(true); + Asserts.assertEquals(stoppedCopy.getState(), RecordingState.STOPPED); + assertCopy(stoppedCopy, original); + + // Verify a running copy + Recording runningCopy = original.copy(false); + Asserts.assertEquals(runningCopy.getState(), RecordingState.RUNNING); + assertCopy(runningCopy, original); + + original.stop(); + + // Verify a stopped copy of a stopped + stoppedCopy = original.copy(true); + Asserts.assertEquals(stoppedCopy.getState(), RecordingState.STOPPED); + assertCopy(stoppedCopy, original); + + // Clean-up + original.close(); + runningCopy.stop(); + runningCopy.close(); + stoppedCopy.close(); + } + + /** + * Verifies the basic assertions about a copied record + */ + private static void assertCopy(Recording recording, Recording original) throws Exception { + + Asserts.assertFalse(recording.getId() == original.getId(), "id of a copied record should differ from that of the original"); + Asserts.assertFalse(recording.getName().equals(original.getName()), "name of a copied record should differ from that of the original"); + + Asserts.assertEquals(recording.getSettings(), original.getSettings()); + Asserts.assertEquals(recording.getStartTime(), original.getStartTime()); + Asserts.assertEquals(recording.getMaxSize(), original.getMaxSize()); + Asserts.assertEquals(recording.getMaxAge(), original.getMaxAge()); + Asserts.assertEquals(recording.isToDisk(), original.isToDisk()); + Asserts.assertEquals(recording.getDumpOnExit(), original.getDumpOnExit()); + + List recordedEvents = Events.fromRecording(recording); + Events.hasEvents(recordedEvents); + Asserts.assertEquals(1, recordedEvents.size(), "Expected exactly one event"); + + RecordedEvent re = recordedEvents.get(0); + Asserts.assertEquals(EVENT_ID, re.getValue("id")); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/options/TestDuration.java 2019-02-08 18:33:06.261109118 +0300 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 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.api.recording.options; + +import java.time.Duration; +import java.time.Instant; + +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.CommonHelper; + +/** + * @test + * @summary Test setDuration(). Verify recording is stopped automatically. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.options.TestDuration + */ +public class TestDuration { + + public static void main(String[] args) throws Throwable { + final Duration duration = Duration.ofSeconds(1); + Recording r = new Recording(); + r.setDuration(duration); + Asserts.assertEquals(duration, r.getDuration(), "Wrong get/set duration"); + + r.start(); + Instant afterStart = Instant.now(); + CommonHelper.waitForRecordingState(r, RecordingState.STOPPED); + + Instant afterStop = Instant.now(); + Asserts.assertLessThanOrEqual(r.getStopTime(), afterStop, "getStopTime() > afterStop"); + long durationMillis = Duration.between(afterStart, r.getStopTime()).toMillis(); + + // Performance of test servers varies too much to make a strict check of actual duration. + // We only check that recording stops before timeout of 20 seconds. + System.out.printf("Recording stopped after %d ms, expected above 1000 ms%n", durationMillis); + r.close(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/options/TestName.java 2019-02-08 18:33:06.409103955 +0300 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 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.api.recording.options; + +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.VoidFunction; + +/** + * @test + * @summary Test setName(). + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.options.TestName + */ +public class TestName { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + + Asserts.assertNotNull(r.getName(), "Default name should not be null"); + System.out.println("name=" + r.getName()); + Asserts.assertEquals(r.getName(), Long.toString(r.getId()), "Default name != id"); + + testNames(r); + r.start(); + testNames(r); + r.stop(); + testNames(r); + r.close(); + } + + private static void testNames(Recording r) throws Throwable { + System.out.println("Recording state=" + r.getState().name()); + + // Set simple name + String name = "myName"; + r.setName(name); + System.out.println("name=" + r.getName()); + Asserts.assertEquals(name, r.getName(), "Wrong get/set name"); + + // Set null. Should get Exception and old name should be kept. + verifyNull(()->{r.setName(null);}, "No NullPointerException when setName(null)"); + Asserts.assertEquals(name, r.getName(), "Current name overwritten after null"); + + // Set empty name. Should work. + name = ""; + r.setName(name); + System.out.println("name=" + r.getName()); + Asserts.assertEquals(name, r.getName(), "Empty name is expected to work"); + + // Test a long name. + StringBuilder sb = new StringBuilder(500); + while (sb.length() < 400) { + sb.append("LongName-"); + } + name = sb.toString(); + System.out.println("Length of long name=" + name.length()); + r.setName(name); + System.out.println("name=" + r.getName()); + Asserts.assertEquals(name, r.getName(), "Wrong get/set long name"); + } + + private static void verifyNull(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, NullPointerException.class); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/settings/TestConfigurationGetContents.java 2019-02-08 18:33:06.553098931 +0300 @@ -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.api.recording.settings; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.Configuration; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary Verifies Configuration.getContents() for every configuration + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.settings.TestConfigurationGetContents + */ +public class TestConfigurationGetContents { + + private static final String SEP = System.getProperty("file.separator"); + private static final String JFR_DIR = System.getProperty("test.jdk") + + SEP + "jre" + SEP + "lib" + SEP + "jfr" + SEP; + + public static void main(String[] args) throws Throwable { + List predefinedConfigs = Configuration.getConfigurations(); + + Asserts.assertNotNull(predefinedConfigs, "List of predefined configs is null"); + Asserts.assertTrue(predefinedConfigs.size() > 0, "List of predefined configs is empty"); + + for (Configuration conf : predefinedConfigs) { + String name = conf.getName(); + System.out.println("Verifying configuration " + name); + String fpath = JFR_DIR + name + ".jfc"; + String contents = conf.getContents(); + String fileContents = readFile(fpath); + Asserts.assertEquals(fileContents, contents, "getContents() does not return the actual contents of the file " + fpath); + } + } + + private static String readFile(String path) throws IOException { + byte[] encoded = Files.readAllBytes(Paths.get(path)); + return new String(encoded); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/settings/TestCreateConfigFromPath.java 2019-02-08 18:33:06.697093906 +0300 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 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.api.recording.settings; + + +import java.io.Reader; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; + +import jdk.jfr.Configuration; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @summary Test setName(). + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.settings.TestCreateConfigFromPath + */ +public class TestCreateConfigFromPath { + + private static final Path DIR = Paths.get(System.getProperty("test.src", ".")); + + public static void main(String[] args) throws Throwable { + testOkConfig(); + testNullReader(); + } + + private static void testOkConfig() throws Exception { + Path settingsPath = DIR.resolve("settings.jfc"); + if(!settingsPath.toFile().exists()) throw new RuntimeException("File " + settingsPath.toFile().getAbsolutePath() + " not found "); + + Configuration config = Configuration.create(settingsPath); + Map settings = config.getSettings(); + + Asserts.assertEquals(4, settings.size(), "Settings size differes from the expected size"); + String[] keys = {"enabled", "stackTrace", "threshold", "custom"}; + String[] vals = {"true", "true", "1 ms", "5"}; + for(int i=0; i settings = config.getSettings(); + + Asserts.assertEquals(4, settings.size(), "Settings size differes from the expected size"); + String[] keys = {"enabled", "stackTrace", "threshold", "custom"}; + String[] vals = {"true", "true", "1 ms", "5"}; + for(int i=0; i predefinedConfigs = Configuration.getConfigurations(); + Asserts.assertNotNull(predefinedConfigs, "List of predefined configs is null"); + Asserts.assertEquals(predefinedConfigs.size(), 2, "Expected exactly two predefined configurations"); + + Configuration defaultConfig = findConfigByName(predefinedConfigs, DEFAULT_CONFIG_NAME); + Asserts.assertNotNull(defaultConfig, "Config '" + DEFAULT_CONFIG_NAME + "' not found"); + Asserts.assertEquals(defaultConfig.getLabel(), DEFAULT_CONFIG_LABEL); + Asserts.assertEquals(defaultConfig.getDescription(), DEFAULT_CONFIG_DESCRIPTION); + Asserts.assertEquals(defaultConfig.getProvider(), DEFAULT_CONFIG_PROVIDER); + + Configuration profileConfig = findConfigByName(predefinedConfigs, PROFILE_CONFIG_NAME); + Asserts.assertNotNull(profileConfig, "Config '" + PROFILE_CONFIG_NAME + "' not found"); + Asserts.assertEquals(profileConfig.getLabel(), PROFILE_CONFIG_LABEL); + Asserts.assertEquals(profileConfig.getDescription(), PROFILE_CONFIG_DESCRIPTION); + Asserts.assertEquals(profileConfig.getProvider(), PROFILE_CONFIG_PROVIDER); + } + + private static Configuration findConfigByName(List configs, String name) { + for (Configuration config : configs) { + if (name.equals(config.getName())) { + return config; + } + } + return null; + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/settings/TestSettingsAvailability.java 2019-02-08 18:33:07.137078555 +0300 @@ -0,0 +1,123 @@ +/* + * 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.api.recording.settings; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @summary Verifies that event types has the correct type of settings + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.settings.TestSettingsAvailability + */ +public class TestSettingsAvailability { + public static void main(String[] args) throws Throwable { + testKnownSettings(); + testSettingPersistence(); + } + + private static void testSettingPersistence() throws IOException, Exception { + Map inMemoryTypes = new HashMap<>(); + for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) { + inMemoryTypes.put(type.getName(), type); + } + + Path p = Paths.get("recording.jfr"); + try (Recording r = new Recording()) { + r.start(); + r.stop(); + r.dump(p); + try (RecordingFile rf = new RecordingFile(p)) { + for (EventType parsedType : rf.readEventTypes()) { + EventType inMem = inMemoryTypes.get(parsedType.getName()); + if (inMem == null) { + throw new Exception("Superflous event type " + parsedType.getName() + " in recording"); + } + Set inMemsettings = new HashSet<>(); + for (SettingDescriptor sd : inMem.getSettingDescriptors()) { + inMemsettings.add(sd.getName()); + } + + for (SettingDescriptor parsedSetting : parsedType.getSettingDescriptors()) { + if (!inMemsettings.contains(parsedSetting.getName())) { + throw new Exception("Superflous setting " + parsedSetting.getName() + " in " + parsedType.getName()); + } + inMemsettings.remove(parsedSetting.getName()); + } + if (!inMemsettings.isEmpty()) { + throw new Exception("Missing settings " + inMemsettings + " for event type " + parsedType.getName() + " in recording"); + } + } + } + } + } + + private static void testKnownSettings() throws Exception { + testSetting(EventNames.JVMInformation, "enabled", "period"); + testSetting(EventNames.FileRead, "enabled", "threshold", "stackTrace"); + testSetting(EventNames.FileWrite, "enabled", "threshold","stackTrace"); + testSetting(EventNames.ExceptionStatistics, "enabled", "period"); + testSetting(EventNames.SocketRead, "enabled", "threshold", "stackTrace"); + testSetting(EventNames.SocketWrite, "enabled", "threshold", "stackTrace"); + testSetting(EventNames.ActiveRecording, "enabled", "threshold", "stackTrace"); + testSetting(EventNames.ActiveSetting, "enabled", "threshold", "stackTrace"); + testSetting(EventNames.JavaExceptionThrow, "enabled", "threshold", "stackTrace"); + } + + private static void testSetting(String eventName, String... settingNames) throws Exception { + for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) { + if (eventName.equals(type.getName())) { + Set settings = new HashSet<>(); + for (SettingDescriptor sd : type.getSettingDescriptors()) { + settings.add(sd.getName()); + } + for (String settingName : settingNames) { + if (!settings.contains(settingName)) { + throw new Exception("Missing setting " + settingName + " in " + eventName); + } + settings.remove(settingName); + } + if (!settings.isEmpty()) { + throw new Exception("Superflous settings " + settings + " in event " + eventName); + } + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/settings/settings.jfc 2019-02-08 18:33:07.281073532 +0300 @@ -0,0 +1,11 @@ + + + + + true + true + 1 ms + 5 + + + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/state/TestOptionState.java 2019-02-08 18:33:07.425068508 +0300 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 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.api.recording.state; + +import java.time.Duration; +import java.util.function.Consumer; + +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * @summary Test options in different states + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.state.TestOptionState + */ +public class TestOptionState { + + // Name Age Size Dur. Dest. Disk + static boolean[] NEW = arrayOf(true, true, true, true, true, true); + static boolean[] DELAYED = arrayOf(true, true, true, true, true, true); + static boolean[] RUNNING = arrayOf(true, true, true, true, true, false); + static boolean[] STOPPED = arrayOf(true, true, true, false, false, false); + static boolean[] CLOSED = arrayOf(false, false, false, false, false, false); + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + assertRecordingState(r, NEW); + r.scheduleStart(Duration.ofHours(2)); + assertRecordingState(r, DELAYED); + r.start(); + assertRecordingState(r, RUNNING); + r.stop(); + assertRecordingState(r, STOPPED); + r.close(); + assertRecordingState(r, CLOSED); + } + + private static void assertRecordingState(Recording r, boolean[] states) throws Exception { + assertOperation("setName", r, s -> s.setName("Test Name"), states[0]); + assertOperation("setMaxAge", r, s -> s.setMaxAge(null), states[1]); + assertOperation("setMaxSize", r, s -> s.setMaxSize(0), states[2]); + assertOperation("setDuration", r, s -> s.setDuration(null), states[3]); + assertOperation("setDestination", r, s -> { + try { + s.setDestination(null); + } catch (IllegalStateException e) { + throw e; // rethrow for testing + } catch(Exception e) { + throw new RuntimeException(e); // should not happen + }}, states[4]); + assertOperation("setTodisk", r, s -> s.setToDisk(true), states[5]); + } + + private static void assertOperation(String opernationName, Recording s, Consumer c, boolean ok) { + try { + c.accept(s); + Asserts.assertTrue(ok, opernationName + " should throw ISE when recording is in state " + s.getState()); + } catch (IllegalStateException ise) { + Asserts.assertFalse(ok, opernationName + " should not throw ISE when recording is in state " + s.getState()); + } + } + + static boolean[] arrayOf(boolean... array) { + return array; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/state/TestState.java 2019-02-08 18:33:07.569063484 +0300 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 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.api.recording.state; + +import java.time.Duration; + +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.test.lib.jfr.CommonHelper; + +/** + * @test + * @summary Test Recording state + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.state.TestState + */ +public class TestState { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + CommonHelper.verifyRecordingState(r, RecordingState.NEW); + r.scheduleStart(Duration.ofHours(2)); + CommonHelper.verifyRecordingState(r, RecordingState.DELAYED); + r.start(); + CommonHelper.verifyRecordingState(r, RecordingState.RUNNING); + r.stop(); + CommonHelper.verifyRecordingState(r, RecordingState.STOPPED); + r.close(); + CommonHelper.verifyRecordingState(r, RecordingState.CLOSED); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/state/TestStateDuration.java 2019-02-08 18:33:07.717058321 +0300 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 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.api.recording.state; + +import java.time.Duration; +import java.time.Instant; + +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.VoidFunction; + +/** + * @test + * @summary Test Recording state + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.state.TestStateDuration + */ +public class TestStateDuration { + + public static void main(String[] args) throws Throwable { + Duration duration = Duration.ofSeconds(2); + Recording r = new Recording(); + r.setDuration(duration); + CommonHelper.verifyRecordingState(r, RecordingState.NEW); + Instant start = Instant.now(); + System.out.println("Recording with duration " + duration + " started at " + start); + r.start(); + + // Wait for recording to stop automatically + System.out.println("Waiting for recording to reach STOPPED state"); + CommonHelper.waitForRecordingState(r, RecordingState.STOPPED); + Instant stop = Instant.now(); + Duration measuredDuration = Duration.between(start, stop); + System.out.println("Recording stopped at " + stop + ". Measured duration " + measuredDuration); + // Timer task uses System.currentMillis, and java.time uses other source. + Duration deltaDueToClockNotInSync = Duration.ofMillis(100); + Asserts.assertGreaterThan(measuredDuration.plus(deltaDueToClockNotInSync), duration); + verifyIllegalState(() -> r.start(), "start() after stop()"); + r.close(); + CommonHelper.verifyRecordingState(r, RecordingState.CLOSED); + } + + private static void verifyIllegalState(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, IllegalStateException.class); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/state/TestStateIdenticalListeners.java 2019-02-08 18:33:07.865053158 +0300 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 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.api.recording.state; + +import static jdk.test.lib.Asserts.assertEquals; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.FlightRecorderListener; +import jdk.jfr.Recording; + +/** + * @test + * @summary Test Recording state with concurrent recordings + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.state.TestStateIdenticalListeners + */ +public class TestStateIdenticalListeners { + + public static void main(String[] args) throws Throwable { + TestListener testListener = new TestListener(); + // Add the same listener twice + FlightRecorder.addListener(testListener); + FlightRecorder.addListener(testListener); + + // Start recording + Recording recording = new Recording(); + recording.start(); + recording.stop(); + + // We expect 4 notifications in total: 1 RUNNING and 1 STOPPED + // notification for each listener registration + // NOT 2 notifications: we have added the same listener again + assertEquals(4, testListener.notificationCount, "Expected 2 notifications, got " + testListener.notificationCount); + + System.out.println("Test Passed"); + recording.close(); + } + + private static class TestListener implements FlightRecorderListener { + private int notificationCount; + + @Override + public void recordingStateChanged(Recording recording) { + System.out.println("recordingStateChanged: " + " recording: " + recording + " state: " + recording.getState()); + ++notificationCount; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/state/TestStateInvalid.java 2019-02-08 18:33:08.009048134 +0300 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 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.api.recording.state; + +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.VoidFunction; + +/** + * @test + * @summary Test start/stop/close recording from different recording states. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.state.TestStateInvalid + */ +public class TestStateInvalid { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + CommonHelper.verifyRecordingState(r, RecordingState.NEW); + verifyIllegalState(() -> r.stop(), "stop() when not started"); + CommonHelper.verifyRecordingState(r, RecordingState.NEW); + + r.start(); + CommonHelper.verifyRecordingState(r, RecordingState.RUNNING); + verifyIllegalState(() -> r.start(), "double start()"); + CommonHelper.verifyRecordingState(r, RecordingState.RUNNING); + + r.stop(); + CommonHelper.verifyRecordingState(r, RecordingState.STOPPED); + verifyIllegalState(() -> r.stop(), "double stop()"); + verifyIllegalState(() -> r.start(), "start() after stop()"); + CommonHelper.verifyRecordingState(r, RecordingState.STOPPED); + + r.close(); + CommonHelper.verifyRecordingState(r, RecordingState.CLOSED); + verifyIllegalState(() -> r.stop(), "stop() after close()"); + verifyIllegalState(() -> r.start(), "start() after close()"); + CommonHelper.verifyRecordingState(r, RecordingState.CLOSED); + } + + private static void verifyIllegalState(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, IllegalStateException.class); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/state/TestStateMultiple.java 2019-02-08 18:33:08.153043110 +0300 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 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.api.recording.state; + +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.VoidFunction; + +/** + * @test + * @summary Test Recording state with concurrent recordings + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.state.TestStateMultiple + */ +public class TestStateMultiple { + + public static void main(String[] args) throws Throwable { + Recording rA = new Recording(); + CommonHelper.verifyRecordingState(rA, RecordingState.NEW); + verifyIllegalState(() -> rA.stop(), "stop() when not started"); + + rA.start(); + CommonHelper.verifyRecordingState(rA, RecordingState.RUNNING); + + Recording rB = new Recording(); + CommonHelper.verifyRecordingState(rA, RecordingState.RUNNING); + verifyIllegalState(() -> rA.start(), "double start()"); + CommonHelper.verifyRecordingState(rB, RecordingState.NEW); + verifyIllegalState(() -> rB.stop(), "stop() when not started"); + + rB.start(); + CommonHelper.verifyRecordingState(rA, RecordingState.RUNNING); + CommonHelper.verifyRecordingState(rB, RecordingState.RUNNING); + + rB.stop(); + CommonHelper.verifyRecordingState(rA, RecordingState.RUNNING); + CommonHelper.verifyRecordingState(rB, RecordingState.STOPPED); + verifyIllegalState(() -> rB.start(), "start() after stop()"); + + rB.close(); + CommonHelper.verifyRecordingState(rA, RecordingState.RUNNING); + CommonHelper.verifyRecordingState(rB, RecordingState.CLOSED); + verifyIllegalState(() -> rB.start(), "start() after close()"); + + rA.stop(); + CommonHelper.verifyRecordingState(rA, RecordingState.STOPPED); + verifyIllegalState(() -> rA.start(), "start() after stop()"); + CommonHelper.verifyRecordingState(rB, RecordingState.CLOSED); + + rA.close(); + CommonHelper.verifyRecordingState(rA, RecordingState.CLOSED); + CommonHelper.verifyRecordingState(rB, RecordingState.CLOSED); + verifyIllegalState(() -> rA.stop(), "stop() after close()"); + verifyIllegalState(() -> rB.start(), "start() after close()"); + } + + private static void verifyIllegalState(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, IllegalStateException.class); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/state/TestStateScheduleStart.java 2019-02-08 18:33:08.301037948 +0300 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 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.api.recording.state; + +import java.time.Duration; +import java.time.Instant; + +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.VoidFunction; + +/** + * @test + * @summary Test Recording state + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.state.TestStateScheduleStart + */ +public class TestStateScheduleStart { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + CommonHelper.verifyRecordingState(r, RecordingState.NEW); + + Instant start = Instant.now(); + r.scheduleStart(Duration.ofMillis(2000)); + + System.out.println("Wait for recording to start: " + start); + CommonHelper.waitForRecordingState(r, RecordingState.RUNNING); + + // Duration should be about 2000 ms. + // Test servers vary too much in performance to make an accurate check. + // We only check that we don't time out after 20 seconds. + Instant started = Instant.now(); + long millis = Duration.between(start, started).toMillis(); + System.out.println("Recording started at " + started + ". Delta millis=" + millis + ", expeced about 2000"); + + verifyIllegalState(() -> r.start(), "double start()"); + r.stop(); + CommonHelper.verifyRecordingState(r, RecordingState.STOPPED); + r.close(); + CommonHelper.verifyRecordingState(r, RecordingState.CLOSED); + } + + private static void verifyIllegalState(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, IllegalStateException.class); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/time/TestTime.java 2019-02-08 18:33:08.449032784 +0300 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 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.api.recording.time; + +import java.time.Instant; + +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * @summary Test Recording.get*Time() + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.time.TestTime + */ + +public class TestTime { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + Asserts.assertNull(r.getStartTime(), "getStartTime() not null before start"); + Asserts.assertNull(r.getStopTime(), "getStopTime() not null before start"); + + final Instant beforeStart = Instant.now(); + r.start(); + final Instant afterStart = Instant.now(); + + Asserts.assertNotNull(r.getStartTime(), "getStartTime() null after"); + Asserts.assertGreaterThanOrEqual(r.getStartTime(), beforeStart, "getStartTime() < beforeStart"); + Asserts.assertLessThanOrEqual(r.getStartTime(), afterStart, "getStartTime() > afterStart"); + Asserts.assertNull(r.getStopTime(), "getStopTime() not null before stop"); + + final Instant beforeStop = Instant.now(); + r.stop(); + final Instant afterStop = Instant.now(); + + Asserts.assertGreaterThanOrEqual(r.getStartTime(), beforeStart, "getStartTime() < beforeStart"); + Asserts.assertLessThanOrEqual(r.getStartTime(), afterStart, "getStartTime() > afterStart"); + Asserts.assertNotNull(r.getStopTime(), "getStopTime() null after stop"); + Asserts.assertGreaterThanOrEqual(r.getStopTime(), beforeStop, "getStopTime() < beforeStop"); + Asserts.assertLessThanOrEqual(r.getStopTime(), afterStop, "getStopTime() > afterStop"); + + r.close(); + + // Same checks again to make sure close() did not change the times. + Asserts.assertGreaterThanOrEqual(r.getStartTime(), beforeStart, "getStartTime() < beforeStart"); + Asserts.assertLessThanOrEqual(r.getStartTime(), afterStart, "getStartTime() > afterStart"); + Asserts.assertNotNull(r.getStopTime(), "getStopTime() null after stop"); + Asserts.assertGreaterThanOrEqual(r.getStopTime(), beforeStop, "getStopTime() < beforeStop"); + Asserts.assertLessThanOrEqual(r.getStopTime(), afterStop, "getStopTime() > afterStop"); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/time/TestTimeDuration.java 2019-02-08 18:33:08.597027622 +0300 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 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.api.recording.time; + +import java.time.Duration; +import java.time.Instant; + +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * @summary Test Recording.setDuration() and Recording.get*Time() + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.time.TestTimeDuration + */ + +public class TestTimeDuration { + + public static void main(String[] args) throws Throwable { + Recording r = new Recording(); + final Duration duration = Duration.ofMillis(30000); + + r.setDuration(duration); + Asserts.assertNull(r.getStartTime(), "getStartTime() not null before start"); + Asserts.assertNull(r.getStopTime(), "getStopTime() not null before start"); + + final Instant beforeStart = Instant.now(); + r.start(); + final Instant afterStart = Instant.now(); + + Asserts.assertNotNull(r.getStartTime(), "getStartTime() null after start()"); + Asserts.assertGreaterThanOrEqual(r.getStartTime(), beforeStart, "getStartTime() < beforeStart"); + Asserts.assertLessThanOrEqual(r.getStartTime(), afterStart, "getStartTime() > afterStart"); + + Asserts.assertNotNull(r.getStopTime(), "getStopTime() null after start with duration"); + Asserts.assertGreaterThanOrEqual(r.getStopTime(), beforeStart.plus(duration), "getStopTime() < beforeStart + duration"); + Asserts.assertLessThanOrEqual(r.getStopTime(), afterStart.plus(duration), "getStopTime() > afterStart + duration"); + + r.close(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/time/TestTimeMultiple.java 2019-02-08 18:33:08.741022598 +0300 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 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.api.recording.time; + +import java.time.Instant; + +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * @summary Test recording times with concurrent recordings + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.time.TestTimeMultiple + */ + +public class TestTimeMultiple { + + public static void main(String[] args) throws Throwable { + Recording rA = new Recording(); + Asserts.assertNull(rA.getStartTime(), "getStartTime() not null before start"); + Asserts.assertNull(rA.getStopTime(), "getStopTime() not null before start"); + + final Instant beforeStartA = Instant.now(); + rA.start(); + final Instant afterStartA = Instant.now(); + + Recording rB = new Recording(); + Asserts.assertNull(rB.getStartTime(), "getStartTime() not null before start"); + Asserts.assertNull(rB.getStopTime(), "getStopTime() not null before start"); + + final Instant beforeStartB = Instant.now(); + rB.start(); + final Instant afterStartB = Instant.now(); + + final Instant beforeStopB = Instant.now(); + rB.stop(); + final Instant afterStopB = Instant.now(); + + final Instant beforeStopA = Instant.now(); + rA.stop(); + final Instant afterStopA = Instant.now(); + + rA.close(); + rB.close(); + + Asserts.assertNotNull(rA.getStartTime(), "getStartTime() null after start"); + Asserts.assertNotNull(rA.getStopTime(), "getStopTime() null after stop"); + Asserts.assertGreaterThanOrEqual(rA.getStartTime(), beforeStartA, "getStartTime() < beforeStart"); + Asserts.assertLessThanOrEqual(rA.getStartTime(), afterStartA, "getStartTime() > afterStart"); + Asserts.assertGreaterThanOrEqual(rA.getStopTime(), beforeStopA, "getStopTime() < beforeStop"); + Asserts.assertLessThanOrEqual(rA.getStopTime(), afterStopA, "getStopTime() > afterStop"); + + Asserts.assertNotNull(rB.getStartTime(), "getStartTime() null after start"); + Asserts.assertNotNull(rB.getStopTime(), "getStopTime() null after stop"); + Asserts.assertGreaterThanOrEqual(rB.getStartTime(), beforeStartB, "getStartTime() < beforeStart"); + Asserts.assertLessThanOrEqual(rB.getStartTime(), afterStartB, "getStartTime() > afterStart"); + Asserts.assertGreaterThanOrEqual(rB.getStopTime(), beforeStopB, "getStopTime() < beforeStop"); + Asserts.assertLessThanOrEqual(rB.getStopTime(), afterStopB, "getStopTime() > afterStop"); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/recording/time/TestTimeScheduleStart.java 2019-02-08 18:33:08.885017575 +0300 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 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.api.recording.time; + +import java.time.Duration; +import java.time.Instant; + +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.CommonHelper; + +/** + * @test + * @key jfr + * @summary Test Recording.scheduleStart() and Recording.get*Time() + * + * @library /lib / + * @run main/othervm jdk.jfr.api.recording.time.TestTimeScheduleStart + */ + +public class TestTimeScheduleStart { + + public static void main(String[] args) throws Throwable { + testScheduledRecordingIsDelayed(); + testScheduledRecordingIsRunning(); + } + + private static void testScheduledRecordingIsRunning() throws Exception { + Recording r = new Recording(); + r.scheduleStart(Duration.ofSeconds(2)); + Asserts.assertNotNull(r.getStartTime(), "start time is null after scheduleStart()"); + CommonHelper.waitForRecordingState(r, RecordingState.RUNNING); + Asserts.assertLessThanOrEqual(r.getStartTime(), Instant.now(), "start time should not exceed current time"); + r.stop(); + r.close(); + } + + private static void testScheduledRecordingIsDelayed() throws Exception { + Recording r = new Recording(); + r.scheduleStart(Duration.ofHours(10)); + CommonHelper.verifyRecordingState(r, RecordingState.DELAYED); + r.close(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/settings/RegExpControl.java 2019-02-08 18:33:09.029012552 +0300 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 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.api.settings; + +import java.util.Set; +import java.util.regex.Pattern; + +import jdk.jfr.SettingControl; + +public final class RegExpControl extends SettingControl { + private Pattern pattern = Pattern.compile(".*"); + + public void setValue(String value) { + this.pattern = Pattern.compile(value); + } + + public String combine(Set values) { + return String.join("|", values); + } + + public String getValue() { + return pattern.toString(); + } + + public boolean matches(String uri) { + return pattern.matcher(uri).find(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/settings/StringListSetting.java 2019-02-08 18:33:09.173007528 +0300 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 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.api.settings; +import java.util.HashSet; +import java.util.Set; +import java.util.StringJoiner; + +import jdk.jfr.Description; +import jdk.jfr.Label; +import jdk.jfr.MetadataDefinition; +import jdk.jfr.SettingControl; + +@Label("String List") +@Description("Accepts set of strings, such as \"text\" or \"text1\", \"text2\", or nothing to reject all strings") +@MetadataDefinition +public final class StringListSetting extends SettingControl { + + private Set acceptedStrings = new HashSet<>(); + + @Override + public void setValue(String s) { + acceptedStrings = parseSetting(s); + } + + private Set parseSetting(String s) { + Set stringSet = new HashSet<>(); + StringBuilder sb = new StringBuilder(); + boolean inString = false; + for (int index = 0; index < s.length(); index++) { + char c = s.charAt(index); + if (c != '"') { + if (inString) { + // escape double quotes + if (c == '\\' && index + 1 < s.length()) { + if (s.charAt(index + 1) == '"') { + index++; + c = '"'; + } + } + sb.append(c); + } + } else { + if (inString) { + stringSet.add(sb.toString()); + sb.setLength(0); + } + inString = !inString; + } + } + return stringSet; + } + + @Override + public String getValue() { + StringJoiner sj = new StringJoiner(", ", "\"", "\""); + for (String s : acceptedStrings) { + sj.add(s); + } + return sj.toString(); + } + + @Override + public String combine(Set values) { + Set nameSet = new HashSet<>(); + for (String s : values) { + nameSet.addAll(parseSetting(s)); + } + if (nameSet.isEmpty()) { + return ""; + } + StringJoiner sj = new StringJoiner(", "); + for (String s : nameSet) { + s = s.replace("\"", "\\\""); // escape quotes + sj.add("\"" + s + "\""); + } + return sj.toString(); + } + + public boolean accept(String string) { + return acceptedStrings.contains(string); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/api/settings/TestFilterEvents.java 2019-02-08 18:33:09.313002645 +0300 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 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.api.settings; + +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.Label; +import jdk.jfr.Recording; +import jdk.jfr.SettingDefinition; +import jdk.test.lib.jfr.Events; + +import static jdk.test.lib.Asserts.assertEquals; + +/** + * @test + * @summary The test uses SettingControl + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.api.settings.TestFilterEvents + */ +public class TestFilterEvents { + + private static class AbstractHTTPEvent extends Event { + @Label("HTTP URI") + protected String uri; + + @Label("URI Filter") + @SettingDefinition + protected boolean uriFilter(RegExpControl control) { + return control.matches(uri); + } + } + + private static final class HTTPGetEvent extends AbstractHTTPEvent { + @Label("Thread Names") + @Description("List of thread names to accept, such as \"main\" or \"workerThread1\", \"taskThread\"") + @SettingDefinition + private boolean threadNames(StringListSetting setting) { + return setting.accept(Thread.currentThread().getName()); + } + + } + private static final class HTTPPostEvent extends AbstractHTTPEvent { + } + + public static void main(String[] args) throws Exception { + Recording continuous = new Recording(); + continuous.enable(HTTPGetEvent.class).with("threadNames", "\"unused-threadname-1\""); + assertEquals(0, makeProfilingRecording("\"unused-threadname-2\"")); + assertEquals(1, makeProfilingRecording("\"" + Thread.currentThread().getName() + "\"")); + continuous.close(); + } + + private static int makeProfilingRecording(String threadNames) throws Exception { + try (Recording recording = new Recording()) { + recording.enable(HTTPGetEvent.class).with("threadNames", threadNames); + recording.enable(HTTPGetEvent.class).with("uriFilter", "https://www.example.com/list/.*"); + recording.enable(HTTPPostEvent.class).with("uriFilter", "https://www.example.com/list/.*"); + recording.start(); + + HTTPGetEvent getEvent = new HTTPGetEvent(); + getEvent.uri = "https://www.example.com/list/item?id=4"; + getEvent.commit(); + + HTTPPostEvent postEvent = new HTTPPostEvent(); + postEvent.uri = "https://www.example.com/admin/login?name=john"; + postEvent.commit(); + + recording.stop(); + + return Events.fromRecording(recording).size(); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/cmd/ExecuteHelper.java 2019-02-08 18:33:09.456997622 +0300 @@ -0,0 +1,140 @@ +/* + * 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.cmd; + +import java.nio.file.Path; +import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jdk.jfr.Configuration; +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.test.lib.Utils; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools;; + +final class ExecuteHelper { + + public static Object[] array; + + static class CustomEvent extends Event { + int intValue; + long longValue; + double doubliValue; + float floatValue; + String stringValue; + Short shortValue; + boolean booleanValue; + char charValue; + double trickyDouble; + } + + public static OutputAnalyzer run(String... args) { + String[] array = new String[args.length + 1]; + System.arraycopy(args, 0, array, 1, args.length); + array[0] = "jdk.jfr.internal.cmd.Execute"; + try { + return ProcessTools.executeTestJava(array); + } catch (Exception e) { + String message = String.format("Caught exception while executing '%s'", Arrays.asList(array)); + throw new RuntimeException(message, e); + } + } + + public static void emitCustomEvents() { + // Custom events with potentially tricky values + CustomEvent event1 = new CustomEvent(); + event1.trickyDouble = Double.NaN; + event1.intValue = Integer.MIN_VALUE; + event1.longValue = Long.MIN_VALUE; + event1.doubliValue = Double.MIN_VALUE; + event1.floatValue = Float.MIN_VALUE; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 512; i++) { + sb.append((char) i); + } + sb.append("\u2324"); + event1.stringValue = sb.toString(); + event1.shortValue = Short.MIN_VALUE; + event1.booleanValue = true; + event1.booleanValue = false; + event1.charValue = '\b'; + event1.commit(); + + CustomEvent event2 = new CustomEvent(); + event2.trickyDouble = Double.NEGATIVE_INFINITY; + event2.intValue = Integer.MAX_VALUE; + event2.longValue = Long.MAX_VALUE; + event2.doubliValue = Double.MAX_VALUE; + event2.floatValue = Float.MAX_VALUE; + event2.stringValue = null; + event2.shortValue = Short.MAX_VALUE; + event2.booleanValue = false; + event2.charValue = 0; + event2.commit(); + } + + public static Path createProfilingRecording() throws Exception { + Path file = Utils.createTempFile("profiling-recording", ".jfr"); + // Create a recording with some data + try (Recording r = new Recording(Configuration.getConfiguration("profile"))) { + r.start(); + + // Allocation event + array = new Object[1000000]; + array = null; + + // Class loading event etc + provokeClassLoading(); + + // GC events + System.gc(); + + // ExecutionSample + long t = System.currentTimeMillis(); + while (System.currentTimeMillis() - t < 50) { + // do nothing + } + + // Other periodic events, i.e CPU load + Thread.sleep(1000); + + r.stop(); + r.dump(file); + } + + return file; + } + + private static void provokeClassLoading() { + // Matching a string with regexp + // is expected to load some classes and generate some VM events + Pattern p = Pattern.compile("a*b"); + Matcher m = p.matcher("aaaaab"); + m.matches(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/cmd/TestHelp.java 2019-02-08 18:33:09.600992599 +0300 @@ -0,0 +1,57 @@ +/* + * 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.cmd; + +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary Test help + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.cmd.TestHelp + */ +public class TestHelp { + + public static void main(String[] args) throws Exception { + OutputAnalyzer output = ExecuteHelper.run("help"); + output.shouldContain("Available commands are:"); + output.shouldContain("print"); + output.shouldContain("reconstruct"); + output.shouldContain("summary"); + output.shouldContain("help"); + + output = ExecuteHelper.run("help", "help"); + output.shouldContain("Available commands are:"); + + output = ExecuteHelper.run("help", "wrongcommand"); + output.shouldContain("Unknown command"); + + output = ExecuteHelper.run("help", "wrongcommand", "wrongarguments"); + output.shouldContain("Too many arguments"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/cmd/TestPrint.java 2019-02-08 18:33:09.744987576 +0300 @@ -0,0 +1,66 @@ +/* + * 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.cmd; + +import java.io.FileWriter; +import java.nio.file.Files; +import java.nio.file.Path; + +import jdk.test.lib.Utils; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary Test jfr print + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.cmd.TestPrint + */ +public class TestPrint { + + public static void main(String[] args) throws Exception { + + OutputAnalyzer output = ExecuteHelper.run("print"); + output.shouldContain("Missing file"); + + output = ExecuteHelper.run("print", "missing.jfr"); + output.shouldContain("Could not find file "); + + output = ExecuteHelper.run("print", "missing.jfr", "option1", "option2"); + output.shouldContain("Too many arguments"); + + Path file = Utils.createTempFile("faked-print-file", ".jfr"); + FileWriter fw = new FileWriter(file.toFile()); + fw.write('d'); + fw.close(); + output = ExecuteHelper.run("print", "--wrongOption", file.toAbsolutePath().toString()); + output.shouldContain("Unknown option"); + Files.delete(file); + + // Also see TestPrintJSON, TestPrintXML and TestPrintDefault. + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/cmd/TestPrintDefault.java 2019-02-08 18:33:09.888982553 +0300 @@ -0,0 +1,53 @@ +/* + * 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.cmd; + +import java.nio.file.Path; + +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @key jfr + * @summary Tests print --json + * + * + * @library /lib / + * + + * + * @run main/othervm jdk.jfr.cmd.TestPrintDefault + */ +public class TestPrintDefault { + + public static void main(String... args) throws Exception { + + Path recordingFile = ExecuteHelper.createProfilingRecording().toAbsolutePath(); + + OutputAnalyzer output = ExecuteHelper.run("print", recordingFile.toString()); + output.shouldContain("JVMInformation"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/cmd/TestPrintJSON.java 2019-02-08 18:33:10.036977391 +0300 @@ -0,0 +1,149 @@ +/* + * 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.cmd; + +import java.nio.file.Path; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; + +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.consumer.RecordingFile; +import jdk.nashorn.api.scripting.JSObject; +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @key jfr + * @summary Tests print --json + * + * + * @library /lib / + * + + * + * @run main/othervm jdk.jfr.cmd.TestPrintJSON + */ +public class TestPrintJSON { + + public static void main(String... args) throws Exception { + + Path recordingFile = ExecuteHelper.createProfilingRecording().toAbsolutePath(); + + OutputAnalyzer output = ExecuteHelper.run("print", "--json", recordingFile.toString()); + String json = output.getStdout(); + + // Parse JSON using Nashorn + String statement = "var jsonObject = " + json; + ScriptEngineManager factory = new ScriptEngineManager(); + ScriptEngine engine = factory.getEngineByName("nashorn"); + engine.eval(statement); + JSObject o = (JSObject) engine.get("jsonObject"); + JSObject recording = (JSObject) o.getMember("recording"); + JSObject events = (JSObject) recording.getMember("events"); + + // Verify events are equal + try (RecordingFile rf = new RecordingFile(recordingFile)) { + for (Object jsonEvent : events.values()) { + RecordedEvent recordedEvent = rf.readEvent(); + double typeId = recordedEvent.getEventType().getId(); + String startTime = recordedEvent.getStartTime().toString(); + String duration = recordedEvent.getDuration().toString(); + Asserts.assertEquals(typeId, ((Number) ((JSObject) jsonEvent).getMember("typeId")).doubleValue()); + Asserts.assertEquals(startTime, ((JSObject) jsonEvent).getMember("startTime")); + Asserts.assertEquals(duration, ((JSObject) jsonEvent).getMember("duration")); + assertEquals(jsonEvent, recordedEvent); + } + Asserts.assertFalse(rf.hasMoreEvents(), "Incorrect number of events"); + } + } + + private static void assertEquals(Object jsonObject, Object jfrObject) throws Exception { + // Check object + if (jfrObject instanceof RecordedObject) { + JSObject values = (JSObject) ((JSObject) jsonObject).getMember("values"); + RecordedObject recObject = (RecordedObject) jfrObject; + Asserts.assertEquals(values.values().size(), recObject.getFields().size()); + for (ValueDescriptor v : recObject.getFields()) { + String name = v.getName(); + assertEquals(values.getMember(name), recObject.getValue(name)); + return; + } + } + // Check array + if (jfrObject != null && jfrObject.getClass().isArray()) { + Object[] jfrArray = (Object[]) jfrObject; + JSObject jsArray = (JSObject) jsonObject; + for (int i = 0; i < jfrArray.length; i++) { + assertEquals(jsArray.getSlot(i), jfrArray[i]); + } + return; + } + String jsonText = String.valueOf(jsonObject); + // Double.NaN / Double.Inifinity is not supported by JSON format, + // use null + if (jfrObject instanceof Double) { + double expected = ((Double) jfrObject); + if (Double.isInfinite(expected) || Double.isNaN(expected)) { + Asserts.assertEquals("null", jsonText); + return; + } + double value = Double.parseDouble(jsonText); + Asserts.assertEquals(expected, value); + return; + } + // Float.NaN / Float.Inifinity is not supported by JSON format, + // use null + if (jfrObject instanceof Float) { + float expected = ((Float) jfrObject); + if (Float.isInfinite(expected) || Float.isNaN(expected)) { + Asserts.assertEquals("null", jsonText); + return; + } + float value = Float.parseFloat(jsonText); + Asserts.assertEquals(expected, value); + return; + } + if (jfrObject instanceof Integer) { + Integer expected = ((Integer) jfrObject); + double value = Double.parseDouble(jsonText); + Asserts.assertEquals(expected.doubleValue(), value); + return; + } + if (jfrObject instanceof Long) { + Long expected = ((Long) jfrObject); + double value = Double.parseDouble(jsonText); + Asserts.assertEquals(expected.doubleValue(), value); + return; + } + + String jfrText = String.valueOf(jfrObject); + Asserts.assertEquals(jfrText, jsonText, "Primitive values don't match. JSON = " + jsonText); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/cmd/TestPrintXML.java 2019-02-08 18:33:10.180972368 +0300 @@ -0,0 +1,221 @@ +/* + * 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.cmd; + +import java.io.StringReader; +import java.nio.file.Path; +import java.time.Duration; +import java.time.Instant; +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.process.OutputAnalyzer; + +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + +/** + * @test + * @key jfr + * @summary Tests print --xml + * + * + * @library /lib / + * + + * + * @run main/othervm jdk.jfr.cmd.TestPrintXML + */ +public class TestPrintXML { + + public static void main(String... args) throws Exception { + + Path recordingFile = ExecuteHelper.createProfilingRecording().toAbsolutePath(); + + OutputAnalyzer output = ExecuteHelper.run("print", "--xml", recordingFile.toString()); + String xml = output.getStdout(); + System.out.println(xml); + // Parse XML string + SAXParserFactory factory = SAXParserFactory.newInstance(); + SAXParser sp = factory.newSAXParser(); + XMLReader xr = sp.getXMLReader(); + RecordingHandler handler = new RecordingHandler(); + xr.setContentHandler(handler); + xr.parse(new InputSource(new StringReader(xml))); + + // Verify that all data was written correctly + Iterator it = RecordingFile.readAllEvents(recordingFile).iterator(); + for (XMLEvent xmlEvent : handler.events) { + RecordedEvent re = it.next(); + if (!compare(re, xmlEvent.values)) { + System.out.println(re); + System.out.println(xmlEvent.values.toString()); + throw new Exception("Event doesn't match"); + } + } + + } + + @SuppressWarnings("unchecked") + static boolean compare(Object eventObject, Object xmlObject) { + if (eventObject == null) { + return xmlObject == null; + } + if (eventObject instanceof RecordedObject) { + RecordedObject re = (RecordedObject) eventObject; + Map xmlMap = (Map) xmlObject; + List fields = re.getFields(); + if (fields.size() != xmlMap.size()) { + return false; + } + for (ValueDescriptor v : fields) { + String name = v.getName(); + if (!compare(re.getValue(name), xmlMap.get(name))) { + return false; + } + } + return true; + } + if (eventObject.getClass().isArray()) { + Object[] array = (Object[]) eventObject; + Object[] xmlArray = (Object[]) xmlObject; + if (array.length != xmlArray.length) { + return false; + } + for (int i = 0; i < array.length; i++) { + if (!compare(array[i], xmlArray[i])) { + return false; + } + } + return true; + } + String s1 = String.valueOf(eventObject); + String s2 = (String) xmlObject; + return s1.equals(s2); + } + + static class XMLEvent { + String name; + Instant startTime; + Duration duration; + Map values = new HashMap<>(); + + XMLEvent(String name, Instant startTime, Duration duration) { + this.name = name; + this.startTime = startTime; + this.duration = duration; + } + } + + public static final class RecordingHandler extends DefaultHandler { + + private Stack objects = new Stack<>(); + private Stack> elements = new Stack<>(); + private List events = new ArrayList<>(); + + @Override + public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException { + elements.push(new SimpleEntry<>(attrs.getValue("name"), attrs.getValue("index"))); + switch (qName) { + case "null": + objects.pop(); + objects.push(null); + break; + case "event": + Instant startTime = Instant.parse(attrs.getValue("startTime")); + Duration duration = Duration.parse(attrs.getValue("duration")); + objects.push(new XMLEvent(attrs.getValue("name"), startTime, duration)); + break; + case "struct": + objects.push(new HashMap()); + break; + case "array": + objects.push(new Object[Integer.parseInt(attrs.getValue("size"))]); + break; + case "value": + objects.push(new StringBuilder()); + break; + } + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + if (!objects.isEmpty()) { + Object o = objects.peek(); + if (o instanceof StringBuilder) { + ((StringBuilder) o).append(ch, start, length); + } + } + } + + @SuppressWarnings("unchecked") + @Override + public void endElement(String uri, String localName, String qName) { + SimpleEntry element = elements.pop(); + switch (qName) { + case "event": + case "struct": + case "array": + case "value": + String name = element.getKey(); + Object value = objects.pop(); + if (objects.isEmpty()) { + events.add((XMLEvent) value); + return; + } + if (value instanceof StringBuilder) { + value = ((StringBuilder) value).toString(); + } + Object parent = objects.peek(); + if (parent instanceof XMLEvent) { + ((XMLEvent) parent).values.put(name, value); + } + if (parent instanceof Map) { + ((Map) parent).put(name, value); + } + if (parent != null && parent.getClass().isArray()) { + int index = Integer.parseInt(element.getValue()); + ((Object[]) parent)[index] = value; + } + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/cmd/TestReconstruct.java 2019-02-08 18:33:10.324967345 +0300 @@ -0,0 +1,146 @@ +/* + * 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.cmd; + +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.jfr.Event; +import jdk.jfr.Name; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.jfr.internal.Repository; +import jdk.jfr.internal.SecuritySupport.SafePath; +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary Test jfr reconstruct + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.cmd.TestReconstruct + */ +public class TestReconstruct { + + @Name("Correlation") + static class CorrelationEvent extends Event { + int id; + } + private static int RECORDING_COUNT = 5; + + @SuppressWarnings("resource") + public static void main(String[] args) throws Exception { + // Create some disk recordings + Recording[] recordings = new Recording[5]; + for (int i = 0; i < RECORDING_COUNT; i++) { + Recording r = new Recording(); + r.setToDisk(true); + r.start(); + CorrelationEvent ce = new CorrelationEvent(); + ce.id = i; + ce.commit(); + r.stop(); + recordings[i] = r; + } + Path dir = Paths.get("reconstruction-parts"); + Files.createDirectories(dir); + + long expectedCount = 0; + for (int i = 0; i < RECORDING_COUNT; i++) { + Path tmp = dir.resolve("chunk-part-" + i + ".jfr"); + recordings[i].dump(tmp); + expectedCount += countEventInRecording(tmp); + } + + SafePath repository = Repository.getRepository().getRepositoryPath(); + Path destinationPath = Paths.get("reconstructed.jfr"); + + String directory = repository.toString(); + String destination = destinationPath.toAbsolutePath().toString(); + + // Test failure + OutputAnalyzer output = ExecuteHelper.run("reconstruct"); + + output.shouldContain("Too few arguments"); + + output = ExecuteHelper.run("reconstruct", directory); + output.shouldContain("Too few arguments"); + + output = ExecuteHelper.run("reconstruct", "not-a-directory", destination); + output.shouldContain("Could not find disk repository at"); + + output = ExecuteHelper.run("reconstruct", directory, "not-a-destination"); + output.shouldContain("Filename must end with .jfr"); + + output = ExecuteHelper.run("reconstruct", "--wrongOption", directory, destination); + output.shouldContain("Too many arguments"); + + FileWriter fw = new FileWriter(destination); + fw.write('d'); + fw.close(); + output = ExecuteHelper.run("reconstruct", directory, destination); + output.shouldContain("already exists"); + Files.delete(destinationPath); + + // test success + output = ExecuteHelper.run("reconstruct", directory, destination); + System.out.println(output.getOutput()); + output.shouldContain("Reconstruction complete"); + + long reconstructedCount = countEventInRecording(destinationPath); + Asserts.assertEquals(expectedCount, reconstructedCount); + // Cleanup + for (int i = 0; i < RECORDING_COUNT; i++) { + recordings[i].close(); + } + } + + private static long countEventInRecording(Path file) throws IOException { + Integer lastId = -1; + try (RecordingFile rf = new RecordingFile(file)) { + long count = 0; + while (rf.hasMoreEvents()) { + RecordedEvent re = rf.readEvent(); + if (re.getEventType().getName().equals("Correlation")) { + Integer id = re.getValue("id"); + if (id < lastId) { + Asserts.fail("Expected chunk number to increase"); + } + lastId = id; + } + count++; + } + return count; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/cmd/TestSplit.java 2019-02-08 18:33:10.468962323 +0300 @@ -0,0 +1,121 @@ +/* + * 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.cmd; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import jdk.jfr.Configuration; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary Test jfr split + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.cmd.TestSplit + */ +public class TestSplit { + + public static void main(String[] args) throws Exception { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss"); + String dateText = formatter.format(new Date()); + + Path recordingFileA = Paths.get("many-chunks-A-" + dateText + ".jfr"); + Path recordingFileB = Paths.get("many-chunks-B-" + dateText + ".jfr"); + makeRecordingWithChunks(6, recordingFileA); + Files.copy(recordingFileA, recordingFileB); + + String fileAText = recordingFileA.toAbsolutePath().toString(); + String fileBText = recordingFileB.toAbsolutePath().toString(); + + OutputAnalyzer output = ExecuteHelper.run("split"); + output.shouldContain("Missing file"); + + output = ExecuteHelper.run("split", "--wrongOption1", "..wrongOption2", "..wrongOption3", fileAText); + output.shouldContain("Too many arguments"); + + output = ExecuteHelper.run("split", "--wrongOption", fileAText); + output.shouldContain("Unknown option"); + + output = ExecuteHelper.run("split", "--wrongOption", "1", fileAText); + output.shouldContain("Unknown option"); + + output = ExecuteHelper.run("split", "--maxchunks", "-3", fileAText); + output.shouldContain("Must be at least one chunk per file"); + + output = ExecuteHelper.run("split", "--maxchunks", "1000", fileAText); + output.shouldContain("Number of chunks in recording"); + output.shouldContain("doesn't exceed max chunks"); + output = ExecuteHelper.run("split", fileAText); // maxchunks is 5 by + // default + System.out.println(output.getOutput()); + System.out.println(fileAText); + verifyRecording(fileAText.substring(0, fileAText.length() - 4) + "_1.jfr"); + verifyRecording(fileAText.substring(0, fileAText.length() - 4) + "_2.jfr"); + + output = ExecuteHelper.run("split", "--maxchunks", "2", fileBText); + + verifyRecording(fileBText.substring(0, fileBText.length() - 4) + "_1.jfr"); + verifyRecording(fileBText.substring(0, fileBText.length() - 4) + "_2.jfr"); + verifyRecording(fileBText.substring(0, fileBText.length() - 4) + "_3.jfr"); + + output = ExecuteHelper.run("split", "--maxchunks", "2", fileBText); + output.shouldContain("file with that name already exist"); + } + + private static void verifyRecording(String name) throws IOException { + System.out.println("split name " + name); + try (RecordingFile rf = new RecordingFile(Paths.get(name))) { + rf.readEvent(); + } + } + + // Will create at least 2 * count + 1 chunks. + private static void makeRecordingWithChunks(int count, Path file) throws IOException, ParseException { + Recording main = new Recording(Configuration.getConfiguration("default")); + main.setToDisk(true); + main.start(); + for (int i = 0; i < count; i++) { + Recording r = new Recording(); + r.setToDisk(true); + r.start(); + r.stop(); + r.close(); + } + main.stop(); + main.dump(file); + main.close(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/cmd/TestSummary.java 2019-02-08 18:33:10.612957300 +0300 @@ -0,0 +1,62 @@ +/* + * 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.cmd; + +import java.nio.file.Path; + +import jdk.jfr.EventType; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary Test jfr info + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.cmd.TestSummary + */ +public class TestSummary { + + public static void main(String[] args) throws Exception { + Path f = ExecuteHelper.createProfilingRecording().toAbsolutePath(); + String file = f.toAbsolutePath().toString(); + + OutputAnalyzer output = ExecuteHelper.run("summary"); + output.shouldContain("Missing file"); + + output = ExecuteHelper.run("summary", "--wrongOption", file); + output.shouldContain("Too many arguments"); + + output = ExecuteHelper.run("summary", file); + try (RecordingFile rf = new RecordingFile(f)) { + for (EventType t : rf.readEventTypes()) { + output.shouldContain(t.getName()); + } + } + output.shouldContain("Version"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/TEST.properties 2019-02-08 18:33:10.756952278 +0300 @@ -0,0 +1,2 @@ +modules = java.management + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/compiler/TestAllocInNewTLAB.java 2019-02-08 18:33:10.900947255 +0300 @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2013, 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.event.compiler; + +import static java.lang.Math.floor; +import static jdk.test.lib.Asserts.assertGreaterThanOrEqual; +import static jdk.test.lib.Asserts.assertLessThanOrEqual; + +import java.time.Duration; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test that event is triggered when an object is allocated in a new TLAB. + * @key jfr + * + * @library /lib / + * @run main/othervm -XX:+UseTLAB -XX:TLABSize=100k -XX:-ResizeTLAB -XX:TLABRefillWasteFraction=1 jdk.jfr.event.compiler.TestAllocInNewTLAB + * @run main/othervm -XX:+UseTLAB -XX:TLABSize=100k -XX:-ResizeTLAB -XX:TLABRefillWasteFraction=1 -XX:-FastTLABRefill jdk.jfr.event.compiler.TestAllocInNewTLAB + * @run main/othervm -XX:+UseTLAB -XX:TLABSize=100k -XX:-ResizeTLAB -XX:TLABRefillWasteFraction=1 -Xint jdk.jfr.event.compiler.TestAllocInNewTLAB + */ + +/** + * Test that when an object is allocated in a new Thread Local Allocation Buffer (TLAB) + * an event will be triggered. The test is done for C1-compiler, + * C2-compiler (-XX:-FastTLABRefill) and interpreted mode (-Xint). + * + * To force objects to be allocated in a new TLAB: + * the size of TLAB is set to 100k (-XX:TLABSize=100k); + * the size of allocated objects is set to 100k minus 16 bytes overhead; + * max TLAB waste at refill is set to minimum (-XX:TLABRefillWasteFraction=1), + * to provoke a new TLAB creation. + */ +public class TestAllocInNewTLAB { + private final static String EVENT_NAME = EventNames.ObjectAllocationInNewTLAB; + + private static final int BYTE_ARRAY_OVERHEAD = 16; // Extra bytes used by a byte array. + private static final int OBJECT_SIZE = 100 * 1024; + private static final int OBJECT_SIZE_ALT = OBJECT_SIZE + 8; // Object size in case of disabled CompressedOops + private static final int OBJECTS_TO_ALLOCATE = 100; + private static final String BYTE_ARRAY_CLASS_NAME = new byte[0].getClass().getName(); + private static final int INITIAL_TLAB_SIZE = 100 * 1024; + + // make sure allocation isn't dead code eliminated + public static byte[] tmp; + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + + recording.start(); + System.gc(); + for (int i = 0; i < OBJECTS_TO_ALLOCATE; ++i) { + tmp = new byte[OBJECT_SIZE - BYTE_ARRAY_OVERHEAD]; + } + recording.stop(); + + int countAllTlabs = 0; // Count all matching tlab allocations. + int countFullTlabs = 0; // Count matching tlab allocations with full tlab size. + for (RecordedEvent event : Events.fromRecording(recording)) { + if (!EVENT_NAME.equals(event.getEventType().getName())) { + continue; + } + System.out.println("Event:" + event); + + long allocationSize = Events.assertField(event, "allocationSize").atLeast(1L).getValue(); + long tlabSize = Events.assertField(event, "tlabSize").atLeast(allocationSize).getValue(); + String className = Events.assertField(event, "objectClass.name").notEmpty().getValue(); + + boolean isMyEvent = Thread.currentThread().getId() == event.getThread().getJavaThreadId() + && className.equals(BYTE_ARRAY_CLASS_NAME) + && (allocationSize == OBJECT_SIZE || allocationSize == OBJECT_SIZE_ALT); + if (isMyEvent) { + countAllTlabs++; + if (tlabSize == INITIAL_TLAB_SIZE + OBJECT_SIZE || tlabSize == INITIAL_TLAB_SIZE + OBJECT_SIZE_ALT) { + countFullTlabs++; + } + } + } + + int minCount = (int) floor(OBJECTS_TO_ALLOCATE * 0.80); + assertGreaterThanOrEqual(countAllTlabs, minCount, "Too few tlab objects allocated"); + assertLessThanOrEqual(countAllTlabs, OBJECTS_TO_ALLOCATE, "Too many tlab objects allocated"); + + // For most GCs we expect the size of each tlab to be + // INITIAL_TLAB_SIZE + ALLOCATION_SIZE, but that is not always true for G1. + // G1 may use a smaller tlab size if the full tlab does not fit in the + // selected memory region. + // + // For example, if a G1 memory region has room for 4.7 tlabs, + // then the first 4 tlabs will have the expected size, + // but the fifth tlab would only have a size of 0.7*expected. + // + // It is only the last tlab in each region that has a smaller size. + // This means that at least 50% of the allocated tlabs should + // have the expected size (1 full tlab, and 1 fractional tlab). + assertGreaterThanOrEqual(2*countFullTlabs, countAllTlabs, "Too many fractional tlabs."); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/compiler/TestAllocOutsideTLAB.java 2019-02-08 18:33:11.044942233 +0300 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013, 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.event.compiler; + +import static java.lang.Math.floor; +import static jdk.test.lib.Asserts.assertGreaterThanOrEqual; +import static jdk.test.lib.Asserts.assertLessThanOrEqual; + +import java.time.Duration; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test that when an object is allocated outside a TLAB an event will be triggered. + * @key jfr + * + * @library /lib / + * @run main/othervm -XX:+UseTLAB -XX:-FastTLABRefill -XX:TLABSize=90k -XX:-ResizeTLAB -XX:TLABRefillWasteFraction=256 jdk.jfr.event.compiler.TestAllocOutsideTLAB + * @run main/othervm -XX:+UseTLAB -XX:-FastTLABRefill -XX:TLABSize=90k -XX:-ResizeTLAB -XX:TLABRefillWasteFraction=256 jdk.jfr.event.compiler.TestAllocOutsideTLAB + * @run main/othervm -XX:+UseTLAB -XX:-FastTLABRefill -XX:TLABSize=90k -XX:-ResizeTLAB -XX:TLABRefillWasteFraction=256 -Xint jdk.jfr.event.compiler.TestAllocOutsideTLAB + */ + +/** + * Test that an event is triggered when an object is allocated outside a + * Thread Local Allocation Buffer (TLAB). The test is done for C1-compiler, + * C2-compiler (-XX:-FastTLABRefill) and interpreted mode (-Xint). + * + * To force objects to be allocated outside TLAB: + * the size of TLAB is set to 90k (-XX:TLABSize=90k); + * the size of allocated objects is set to 100k. + * max TLAB waste at refill is set to 256 (-XX:TLABRefillWasteFraction=256), + * to prevent a new TLAB creation. +*/ +public class TestAllocOutsideTLAB { + private static final String EVENT_NAME = EventNames.ObjectAllocationOutsideTLAB; + + private static final int BYTE_ARRAY_OVERHEAD = 16; // Extra bytes used by a byte array + private static final int OBJECT_SIZE = 100 * 1024; + private static final int OBJECT_SIZE_ALT = OBJECT_SIZE + 8; // Object size in case of disabled CompressedOops + private static final int OBJECTS_TO_ALLOCATE = 100; + private static final String BYTE_ARRAY_CLASS_NAME = new byte[0].getClass().getName(); + + public static byte[] tmp; // Used to prevent optimizer from removing code. + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + recording.start(); + for (int i = 0; i < OBJECTS_TO_ALLOCATE; ++i) { + tmp = new byte[OBJECT_SIZE - BYTE_ARRAY_OVERHEAD]; + } + recording.stop(); + + int countEvents = 0; + for (RecordedEvent event : Events.fromRecording(recording)) { + if (!EVENT_NAME.equals(event.getEventType().getName())) { + continue; + } + System.out.println("Event:" + event); + + long allocationSize = Events.assertField(event, "allocationSize").atLeast(1L).getValue(); + String className = Events.assertField(event, "objectClass.name").notEmpty().getValue(); + + boolean isMyEvent = Thread.currentThread().getId() == event.getThread().getJavaThreadId() + && className.equals(BYTE_ARRAY_CLASS_NAME) + && (allocationSize == OBJECT_SIZE || allocationSize == OBJECT_SIZE_ALT); + if (isMyEvent) { + ++countEvents; + } + } + + int minCount = (int) floor(OBJECTS_TO_ALLOCATE * 0.80); + assertGreaterThanOrEqual(countEvents, minCount, "Too few tlab objects allocated"); + assertLessThanOrEqual(countEvents, OBJECTS_TO_ALLOCATE, "Too many tlab objects allocated"); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/compiler/TestCodeCacheConfig.java 2019-02-08 18:33:11.188937210 +0300 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013, 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.event.compiler; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import sun.hotspot.WhiteBox; + +/** + * @test TestCodeCacheConfig + * @key jfr + * + * @library /lib / + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+SegmentedCodeCache jdk.jfr.event.compiler.TestCodeCacheConfig + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-SegmentedCodeCache jdk.jfr.event.compiler.TestCodeCacheConfig + * @summary check "Code Cache Configuration" jfr event + */ +public class TestCodeCacheConfig { + private final static String EVENT_NAME = EventNames.CodeCacheConfiguration; + + private static final long CodeCacheExpectedSize = WhiteBox.getWhiteBox().getUintxVMFlag("ReservedCodeCacheSize"); + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + RecordedEvent event = events.get(0); + long initialSize = (long) event.getValue("initialSize"); + long reservedSize = (long) event.getValue("reservedSize"); + long nonNMethodSize = (long) event.getValue("nonNMethodSize"); + long profiledSize = (long) event.getValue("profiledSize"); + long nonProfiledSize = (long) event.getValue("nonProfiledSize"); + long expansionSize = (long) event.getValue("expansionSize"); + long minBlockLength = (long) event.getValue("minBlockLength"); + long startAddress = (long) event.getValue("startAddress"); + long reservedTopAddress = (long) event.getValue("reservedTopAddress"); + + Asserts.assertGT(initialSize, 1024L, + "initialSize less than 1024 byte, got " + initialSize); + + Asserts.assertEQ(reservedSize, CodeCacheExpectedSize, + String.format("Unexpected reservedSize value. Expected %d but " + "got %d", CodeCacheExpectedSize, reservedSize)); + + Asserts.assertLTE(nonNMethodSize, CodeCacheExpectedSize, + String.format("Unexpected nonNMethodSize value. Expected <= %d but " + "got %d", CodeCacheExpectedSize, nonNMethodSize)); + + Asserts.assertLTE(profiledSize, CodeCacheExpectedSize, + String.format("Unexpected profiledSize value. Expected <= %d but " + "got %d", CodeCacheExpectedSize, profiledSize)); + + Asserts.assertLTE(nonProfiledSize, CodeCacheExpectedSize, + String.format("Unexpected nonProfiledSize value. Expected <= %d but " + "got %d", CodeCacheExpectedSize, nonProfiledSize)); + + Asserts.assertGTE(expansionSize, 1024L, + "expansionSize less than 1024 " + "bytes, got " + expansionSize); + + Asserts.assertGTE(minBlockLength, 1L, + "minBlockLength less than 1 byte, got " + minBlockLength); + + Asserts.assertNE(startAddress, 0L, + "startAddress null"); + + Asserts.assertNE(reservedTopAddress, 0L, + "codeCacheReservedTopAddr null"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/compiler/TestCodeCacheFull.java 2019-02-08 18:33:11.332932188 +0300 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013, 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.event.compiler; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import sun.hotspot.WhiteBox; +import sun.hotspot.code.BlobType; + +/** + * @test TestCodeCacheFull + * + * + * @library /lib / + * + * jdk.management.jfr + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+SegmentedCodeCache -XX:-UseLargePages jdk.jfr.event.compiler.TestCodeCacheFull + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-SegmentedCodeCache jdk.jfr.event.compiler.TestCodeCacheFull + */ +public class TestCodeCacheFull { + + public static void main(String[] args) throws Exception { + for (BlobType btype : BlobType.getAvailable()) { + testWithBlobType(btype, calculateAvailableSize(btype)); + } + } + + private static void testWithBlobType(BlobType btype, long availableSize) throws Exception { + Recording r = new Recording(); + r.enable(EventNames.CodeCacheFull); + r.start(); + WhiteBox.getWhiteBox().allocateCodeBlob(availableSize, btype.id); + r.stop(); + + List events = Events.fromRecording(r); + Events.hasEvents(events); + RecordedEvent event = events.get(0); + + String codeBlobType = Events.assertField(event, "codeBlobType").notNull().getValue(); + BlobType blobType = blobTypeFromName(codeBlobType); + Asserts.assertTrue(blobType.allowTypeWhenOverflow(blobType), "Unexpected overflow BlobType " + blobType.id); + Events.assertField(event, "entryCount").atLeast(0); + Events.assertField(event, "methodCount").atLeast(0); + Events.assertEventThread(event); + Events.assertField(event, "fullCount").atLeast(0); + Events.assertField(event, "startAddress").notEqual(0L); + Events.assertField(event, "commitedTopAddress").notEqual(0L); + Events.assertField(event, "reservedTopAddress").notEqual(0L); + } + + private static BlobType blobTypeFromName(String codeBlobTypeName) throws Exception { + for (BlobType t : BlobType.getAvailable()) { + if (t.beanName.equals(codeBlobTypeName)) { + return t; + } + } + throw new Exception("Unexpected event " + codeBlobTypeName); + } + + // Compute the available size for this BlobType by taking into account + // that it may be stored in a different code heap in case it does not fit + // into the current one. + private static long calculateAvailableSize(BlobType btype) { + long availableSize = btype.getSize(); + for (BlobType alternative : BlobType.getAvailable()) { + if (btype.allowTypeWhenOverflow(alternative)) { + availableSize = Math.max(availableSize, alternative.getSize()); + } + } + return availableSize; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/compiler/TestCodeCacheStats.java 2019-02-08 18:33:11.480927027 +0300 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013, 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.event.compiler; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.compiler.TestCodeCacheStats + */ + +public class TestCodeCacheStats { + private final static String EVENT_NAME = EventNames.CodeCacheStatistics; + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + Events.assertField(event, "startAddress"); + Events.assertField(event, "reservedTopAddress"); + Events.assertField(event, "entryCount").atLeast(0); + Events.assertField(event, "methodCount").atLeast(0); + Events.assertField(event, "adaptorCount").atLeast(0); + Events.assertField(event, "unallocatedCapacity").atLeast(1024L); + Events.assertField(event, "fullCount").equal(0); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/compiler/TestCodeSweeper.java 2019-02-08 18:33:11.628921865 +0300 @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2014, 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.event.compiler; + +import java.lang.management.MemoryPoolMXBean; +import java.lang.reflect.Method; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import sun.hotspot.WhiteBox; +import sun.hotspot.code.BlobType; +import sun.hotspot.code.CodeBlob; + +/** + * Test for events: vm/code_sweeper/sweep vm/code_cache/full vm/compiler/failure + * + * We verify: 1. That sweptCount >= flushedCount + zombifiedCount 2. That + * sweepIndex increases by 1. 3. We should get at least one of each of the + * events listed above. + * + * NOTE! The test is usually able to trigger the events but not always. If an + * event is received, the event is verified. If an event is missing, we do NOT + * fail. + */ +/** + * @test TestCodeSweeper + * @key jfr + * + * @library /lib / + * @build sun.hotspot.WhiteBox + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:-SegmentedCodeCache -XX:+WhiteBoxAPI jdk.jfr.event.compiler.TestCodeSweeper + */ + +public class TestCodeSweeper { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + private static final int COMP_LEVEL_SIMPLE = 1; + private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; + private static final int SIZE = 1; + private static final String METHOD_NAME = "verifyFullEvent"; + private static final String pathSweep = EventNames.SweepCodeCache; + private static final String pathFull = EventNames.CodeCacheFull; + private static final String pathFailure = EventNames.CompilationFailure; + public static final long SEGMENT_SIZE = WhiteBox.getWhiteBox().getUintxVMFlag("CodeCacheSegmentSize"); + public static final long MIN_BLOCK_LENGTH = WhiteBox.getWhiteBox().getUintxVMFlag("CodeCacheMinBlockLength"); + public static final long MIN_ALLOCATION = SEGMENT_SIZE * MIN_BLOCK_LENGTH; + private static final double CACHE_USAGE_COEF = 0.95d; + + public static void main(String[] args) throws Throwable { + Asserts.assertTrue(BlobType.getAvailable().contains(BlobType.All), "Test does not support SegmentedCodeCache"); + + System.out.println("************************************************"); + System.out.println("This test will warn that the code cache is full."); + System.out.println("That is expected and is the purpose of the test."); + System.out.println("************************************************"); + + Recording r = new Recording(); + r.enable(pathSweep); + r.enable(pathFull); + r.enable(pathFailure); + r.start(); + provokeEvents(); + r.stop(); + + int countEventSweep = 0; + int countEventFull = 0; + int countEventFailure = 0; + + List events = Events.fromRecording(r); + Events.hasEvents(events); + for (RecordedEvent event : events) { + switch (event.getEventType().getName()) { + case pathSweep: + countEventSweep++; + verifySingleSweepEvent(event); + break; + case pathFull: + countEventFull++; + verifyFullEvent(event); + break; + case pathFailure: + countEventFailure++; + verifyFailureEvent(event); + break; + } + } + + System.out.println(String.format("eventCount: %d, %d, %d", countEventSweep, countEventFull, countEventFailure)); + } + + private static boolean canAllocate(double size, long maxSize, MemoryPoolMXBean bean) { + // Don't fill too much to have space for adapters. So, stop after crossing 95% and + // don't allocate in case we'll cross 97% on next allocation. + double used = bean.getUsage().getUsed(); + return (used <= CACHE_USAGE_COEF * maxSize) && + (used + size <= (CACHE_USAGE_COEF + 0.02d) * maxSize); + } + + private static void provokeEvents() throws NoSuchMethodException, InterruptedException { + // Prepare for later, since we don't want to trigger any compilation + // setting this up. + Method method = TestCodeSweeper.class.getDeclaredMethod(METHOD_NAME, new Class[] { RecordedEvent.class }); + String directive = "[{ match: \"" + TestCodeSweeper.class.getName().replace('.', '/') + + "." + METHOD_NAME + "\", " + "BackgroundCompilation: false }]"; + + // Fill up code heaps until they are almost full + // to trigger the vm/code_sweeper/sweep event. + ArrayList blobs = new ArrayList<>(); + MemoryPoolMXBean bean = BlobType.All.getMemoryPool(); + long max = bean.getUsage().getMax(); + long headerSize = getHeaderSize(BlobType.All); + long minAllocationUnit = Math.max(1, MIN_ALLOCATION - headerSize); + long stopAt = max - minAllocationUnit; + long addr = 0; + + // First allocate big blobs to speed things up + for (long size = 100_000 * minAllocationUnit; size > 0; size /= 10) { + while (canAllocate(size, max, bean) && + (addr = WHITE_BOX.allocateCodeBlob(size, BlobType.All.id)) != 0) { + blobs.add(addr); + } + } + + // Now allocate small blobs until the heap is almost full + while (bean.getUsage().getUsed() < stopAt && + (addr = WHITE_BOX.allocateCodeBlob(SIZE, BlobType.All.id)) != 0) { + blobs.add(addr); + } + + // Trigger the vm/code_cache/full event by compiling one more + // method. This also triggers the vm/compiler/failure event. + Asserts.assertTrue(WHITE_BOX.addCompilerDirective(directive) == 1); + try { + if (!WHITE_BOX.enqueueMethodForCompilation(method, COMP_LEVEL_FULL_OPTIMIZATION)) { + WHITE_BOX.enqueueMethodForCompilation(method, COMP_LEVEL_SIMPLE); + } + } finally { + WHITE_BOX.removeCompilerDirective(1); + } + + // Free memory + for (Long blob : blobs) { + WHITE_BOX.freeCodeBlob(blob); + } + } + + private static void verifyFullEvent(RecordedEvent event) throws Throwable { + Events.assertField(event, "codeBlobType").notEmpty(); + Events.assertField(event, "unallocatedCapacity").atLeast(0L); + Events.assertField(event, "entryCount").atLeast(0); + Events.assertField(event, "methodCount").atLeast(0); + Events.assertField(event, "adaptorCount").atLeast(0); + Events.assertField(event, "fullCount").atLeast(0); + + // Verify startAddress <= commitedTopAddress <= reservedTopAddress. + // Addresses may be so big that they overflow a long (treated as a + // negative value), convert value to an octal string and compare the + // string. + String startAddress = Long.toOctalString(Events.assertField(event, "startAddress").getValue()); + String commitedTopAddress = Long.toOctalString(Events.assertField(event, "commitedTopAddress").getValue()); + String reservedTopAddress = Long.toOctalString(Events.assertField(event, "reservedTopAddress").getValue()); + Asserts.assertTrue(isOctalLessOrEqual(startAddress, commitedTopAddress), "startAddress<=commitedTopAddress: " + startAddress + "<=" + commitedTopAddress); + Asserts.assertTrue(isOctalLessOrEqual(commitedTopAddress, reservedTopAddress), "commitedTopAddress<=reservedTopAddress: " + commitedTopAddress + "<=" + reservedTopAddress); + } + + private static void verifyFailureEvent(RecordedEvent event) throws Throwable { + Events.assertField(event, "failureMessage").notEmpty(); + Events.assertField(event, "compileId").atLeast(0); + } + + private static void verifySingleSweepEvent(RecordedEvent event) throws Throwable { + int flushedCount = Events.assertField(event, "flushedCount").atLeast(0).getValue(); + int zombifiedCount = Events.assertField(event, "zombifiedCount").atLeast(0).getValue(); + Events.assertField(event, "sweptCount").atLeast(flushedCount + zombifiedCount); + Events.assertField(event, "sweepId").atLeast(0); + Asserts.assertGreaterThanOrEqual(event.getStartTime(), Instant.EPOCH, "startTime was < 0"); + Asserts.assertGreaterThanOrEqual(event.getEndTime(), event.getStartTime(), "startTime was > endTime"); + } + + /** Returns true if less <= bigger. */ + private static boolean isOctalLessOrEqual(String less, String bigger) { + if (less.length() > bigger.length()) { + return false; + } + if (less.length() < bigger.length()) { + return true; + } + return less.compareTo(bigger) <= 0; + } + + public static final long getHeaderSize(BlobType btype) { + long addr = WHITE_BOX.allocateCodeBlob(0, btype.id); + int size = CodeBlob.getCodeBlob(addr).size; + WHITE_BOX.freeCodeBlob(addr); + return size; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/compiler/TestCodeSweeperConfig.java 2019-02-08 18:33:11.776916703 +0300 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013, 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.event.compiler; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm -XX:+UseCodeCacheFlushing -XX:-SegmentedCodeCache jdk.jfr.event.compiler.TestCodeSweeperConfig + * @run main/othervm -XX:+UseCodeCacheFlushing -XX:+SegmentedCodeCache jdk.jfr.event.compiler.TestCodeSweeperConfig + */ +public class TestCodeSweeperConfig { + + private final static String EVENT_NAME = EventNames.CodeSweeperConfiguration; + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event: " + event); + Events.assertField(event, "sweeperEnabled").equal(true); + Events.assertField(event, "flushingEnabled").equal(true); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/compiler/TestCodeSweeperStats.java 2019-02-08 18:33:11.920911681 +0300 @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2013, 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.event.compiler; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.file.Paths; +import java.util.List; + +import sun.hotspot.WhiteBox; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.classloader.FilterClassLoader; +import jdk.test.lib.classloader.ParentLastURLClassLoader; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.Utils; + +/** + * @test TestCodeSweeperStats + * @key jfr + * + * @library /lib / + * + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:CompileOnly=jdk.jfr.event.compiler.TestCodeSweeperStats::dummyMethod + * -XX:+SegmentedCodeCache jdk.jfr.event.compiler.TestCodeSweeperStats + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:CompileOnly=jdk.jfr.event.compiler.TestCodeSweeperStats::dummyMethod + * -XX:-SegmentedCodeCache jdk.jfr.event.compiler.TestCodeSweeperStats + */ +public class TestCodeSweeperStats { + private static final String EVENT_NAME = EventNames.CodeSweeperStatistics; + private static final int WAIT_TIME = 10_000; + private static final String CLASS_METHOD_TO_COMPILE = "dummyMethod"; + private static final int METHODS_TO_COMPILE = Integer.getInteger("compile.methods.count", 10); + private static final int COMP_LEVEL_SIMPLE = 1; + private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).with("period", "endChunk"); + recording.start(); + compileAndSweep(); + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + Events.assertField(event, "sweepCount").atLeast(1); + Events.assertField(event, "methodReclaimedCount").equal(METHODS_TO_COMPILE); + Events.assertField(event, "totalSweepTime").atLeast(0L); + Events.assertField(event, "peakFractionTime").atLeast(0L); + Events.assertField(event, "peakSweepTime").atLeast(0L); + } + } + + private static void compileAndSweep() throws InterruptedException { + WhiteBox WB = WhiteBox.getWhiteBox(); + for (int i = 0; i < METHODS_TO_COMPILE; i++) { + System.out.println("compile " + i); + compileMethod(); + } + + WB.deoptimizeAll(); + System.out.println("All methods deoptimized"); + + // method will be sweeped out of code cache after 5 sweep cycles + for (int i = 0; i < 5; i++) { + WB.fullGC(); + WB.forceNMethodSweep(); + + } + // now wait for event(s) to be fired + Thread.sleep(WAIT_TIME); + } + + public void dummyMethod() { + System.out.println("Hello World!"); + } + + protected static void compileMethod() { + ClassLoader current = TestCodeSweeperStats.class.getClassLoader(); + String[] cpaths = System.getProperty("test.classes", ".").split(File.pathSeparator); + URL[] urls = new URL[cpaths.length]; + try { + for (int i = 0; i < cpaths.length; i++) { + urls[i] = Paths.get(cpaths[i]).toUri().toURL(); + } + } catch (MalformedURLException e) { + throw new Error(e); + } + + String currentClassName = TestCodeSweeperStats.class.getName(); + FilterClassLoader cl = new FilterClassLoader(new ParentLastURLClassLoader(urls, current), ClassLoader.getSystemClassLoader(), (name) -> currentClassName.equals(name)); + Class loadedClass = null; + String className = currentClassName; + try { + loadedClass = cl.loadClass(className); + } catch (ClassNotFoundException ex) { + throw new Error("Couldn't load class " + className, ex); + } + try { + Method mtd = loadedClass.getMethod(CLASS_METHOD_TO_COMPILE); + WhiteBox WB = WhiteBox.getWhiteBox(); + WB.testSetDontInlineMethod(mtd, true); + String directive = "[{ match: \"" + TestCodeSweeperStats.class.getName().replace('.', '/') + + "." + CLASS_METHOD_TO_COMPILE + "\", " + "BackgroundCompilation: false }]"; + WB.addCompilerDirective(directive); + if (!WB.enqueueMethodForCompilation(mtd, COMP_LEVEL_FULL_OPTIMIZATION)) { + WB.enqueueMethodForCompilation(mtd, COMP_LEVEL_SIMPLE); + } + Utils.waitForCondition(() -> WB.isMethodCompiled(mtd)); + } catch (NoSuchMethodException e) { + throw new Error("An exception while trying compile method " + e.getMessage(), e); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/compiler/TestCompilerCompile.java 2019-02-08 18:33:12.064906659 +0300 @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2013, 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.event.compiler; + +import static jdk.test.lib.Asserts.assertFalse; + +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Utils; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import sun.hotspot.WhiteBox; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * jdk.jfr.event.compiler.TestCompilerCompile + */ +public class TestCompilerCompile { + private final static String EVENT_NAME = EventNames.Compilation; + private final static String METHOD_NAME = "dummyMethod"; + private boolean foundKnownMethod = false; + private boolean foundOsrMethod = false; + + public static void main(String[] args) throws Throwable { + TestCompilerCompile test = new TestCompilerCompile(); + test.doTest(); + } + + static void dummyMethod() { + System.out.println("hello!"); + } + + public void doTest() throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + + recording.start(); + long start = System.currentTimeMillis(); + // provoke OSR compilation + for (int i = 0; i < Integer.MAX_VALUE; i++) { + } + // compile dummyMethod() + Method mtd = TestCompilerCompile.class.getDeclaredMethod(METHOD_NAME, new Class[0]); + WhiteBox WB = WhiteBox.getWhiteBox(); + String directive = "[{ match: \"" + TestCompilerCompile.class.getName().replace('.', '/') + + "." + METHOD_NAME + "\", " + "BackgroundCompilation: false }]"; + WB.addCompilerDirective(directive); + if (!WB.enqueueMethodForCompilation(mtd, 4 /* CompLevel_full_optimization */)) { + WB.enqueueMethodForCompilation(mtd, 1 /* CompLevel_simple */); + } + Utils.waitForCondition(() -> WB.isMethodCompiled(mtd)); + dummyMethod(); + + System.out.println("time:" + (System.currentTimeMillis() - start)); + recording.stop(); + + Set compileIds = new HashSet(); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + verifyEvent(event); + Integer compileId = Events.assertField(event, "compileId").getValue(); + assertFalse(compileIds.contains(compileId), "compile id not unique: " + compileId); + compileIds.add(compileId); + } + + // Verify that we actually encountered our expected method + if (!foundKnownMethod) { + throw new Exception("Couldn't find method jdk/jfr/event/compiler/TestCompilerCompile.dummyMethod()V among compilation events"); + } + + // Verify that doTest() function has been replaced on stack. + if (!foundOsrMethod) { + throw new Exception("No On Stack Replacement of function doTest()"); + } + } + + private void verifyEvent(RecordedEvent event) throws Throwable { + Events.assertJavaMethod(event); + Events.assertEventThread(event); + + String methodName = Events.assertField(event, "method.name").notEmpty().getValue(); + String methodDescriptor = Events.assertField(event, "method.descriptor").notEmpty().getValue(); + String methodType = Events.assertField(event, "method.type.name").notEmpty().getValue(); + + // Compare with a known candidate + if ("jdk/jfr/event/compiler/TestCompilerCompile".equals(methodType) && "dummyMethod".equals(methodName) && "()V".equals(methodDescriptor)) { + foundKnownMethod = true; + } + + // The doTest() function is live almost the entire time the test runs. + // We should get at least 1 "on stack replacement" for that method. + if (TestCompilerCompile.class.getName().replace('.', '/').equals(methodType) && "doTest".equals(methodName)) { + boolean isOsr = Events.assertField(event, "isOsr").getValue(); + if (isOsr) { + foundOsrMethod = true; + } + } + + Events.assertField(event, "compileId").atLeast(0); + Events.assertField(event, "compileLevel").atLeast((short) 0).atMost((short) 4); + Events.assertField(event, "inlinedBytes").atLeast(0L); + Events.assertField(event, "codeSize").atLeast(0L); + Events.assertField(event, "isOsr"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/compiler/TestCompilerConfig.java 2019-02-08 18:33:12.208901637 +0300 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013, 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.event.compiler; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.compiler.TestCompilerConfig + */ +public class TestCompilerConfig { + private final static String EVENT_NAME = EventNames.CompilerConfig; + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + Events.assertField(event, "threadCount").atLeast(0); + Events.assertField(event, "tieredCompilation"); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/compiler/TestCompilerInlining.java 2019-02-08 18:33:12.348896755 +0300 @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2015, 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.event.compiler; + +import jdk.internal.org.objectweb.asm.*; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedMethod; +import jdk.jfr.consumer.RecordedObject; +import jdk.test.lib.Asserts; +import jdk.test.lib.Platform; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import sun.hotspot.WhiteBox; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.util.*; +import java.util.stream.IntStream; + +/** + * @test CompilerInliningTest + * @bug 8073607 + * @key jfr + * @summary Verifies that corresponding JFR events are emitted in case of inlining. + * + * + * + * @library /lib / + * + + * + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xbatch jdk.jfr.event.compiler.TestCompilerInlining + */ +public class TestCompilerInlining { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + private static final int LEVEL_SIMPLE = 1; + private static final int LEVEL_FULL_OPTIMIZATION = 4; + private static final Executable ENTRY_POINT = getConstructor(TestCase.class); + private static final String TEST_CASE_CLASS_NAME = TestCase.class.getName().replace('.', '/'); + + public static void main(String[] args) throws Exception { + InlineCalls inlineCalls = new InlineCalls(TestCase.class); + inlineCalls.disableInline(getConstructor(Object.class)); + inlineCalls.disableInline(getMethod(TestCase.class, "qux", boolean.class)); + inlineCalls.forceInline(getMethod(TestCase.class, "foo")); + inlineCalls.forceInline(getMethod(TestCase.class, "foo", int.class)); + inlineCalls.forceInline(getMethod(TestCase.class, "bar")); + inlineCalls.forceInline(getMethod(TestCase.class, "baz")); + + Map result = inlineCalls.getExpected(ENTRY_POINT); + for (int level : determineAvailableLevels()) { + testLevel(result, level); + } + } + + private static void testLevel(Map expectedResult, int level) throws IOException { + System.out.println("****** Testing level " + level + " *******"); + Recording r = new Recording(); + r.enable(EventNames.CompilerInlining); + r.start(); + WHITE_BOX.enqueueMethodForCompilation(ENTRY_POINT, level); + WHITE_BOX.deoptimizeMethod(ENTRY_POINT); + r.stop(); + System.out.println("Expected:"); + + List events = Events.fromRecording(r); + Set foundEvents = new HashSet<>(); + int foundRelevantEvent = 0; + for (RecordedEvent event : events) { + RecordedMethod callerObject = event.getValue("caller"); + RecordedObject calleeObject = event.getValue("callee"); + MethodDesc caller = methodToMethodDesc(callerObject); + MethodDesc callee = ciMethodToMethodDesc(calleeObject); + // only TestCase.* -> TestCase.* OR TestCase.* -> Object. are tested/filtered + if (caller.className.equals(TEST_CASE_CLASS_NAME) && (callee.className.equals(TEST_CASE_CLASS_NAME) + || (callee.className.equals("java/lang/Object") && callee.methodName.equals("")))) { + System.out.println(event); + boolean succeeded = (boolean) event.getValue("succeeded"); + int bci = Events.assertField(event, "bci").atLeast(0).getValue(); + Call call = new Call(caller, callee, bci); + foundRelevantEvent++; + Boolean expected = expectedResult.get(call); + Asserts.assertNotNull(expected, "Unexpected inlined call : " + call); + Asserts.assertEquals(expected, succeeded, "Incorrect result for " + call); + Asserts.assertTrue(foundEvents.add(call), "repeated event for " + call); + } + } + Asserts.assertEquals(foundRelevantEvent, expectedResult.size(), String.format("not all events found at lavel %d. " + "found = '%s'. expected = '%s'", level, events, expectedResult.keySet())); + System.out.println(); + System.out.println(); + } + + private static int[] determineAvailableLevels() { + if (WHITE_BOX.getBooleanVMFlag("TieredCompilation")) { + return IntStream.rangeClosed(LEVEL_SIMPLE, WHITE_BOX.getIntxVMFlag("TieredStopAtLevel").intValue()).toArray(); + } + if (Platform.isServer() && !Platform.isEmulatedClient()) { + return new int[] { LEVEL_FULL_OPTIMIZATION }; + } + if (Platform.isClient() || Platform.isEmulatedClient()) { + return new int[] { LEVEL_SIMPLE }; + } + throw new Error("TESTBUG: unknown VM"); + } + + private static MethodDesc methodToMethodDesc(RecordedMethod method) { + String internalClassName = method.getType().getName().replace('.', '/'); + String methodName = method.getValue("name"); + String methodDescriptor = method.getValue("descriptor"); + return new MethodDesc(internalClassName, methodName, methodDescriptor); + } + + private static MethodDesc ciMethodToMethodDesc(RecordedObject ciMethod) { + String internalClassName = ciMethod.getValue("type"); + String methodName = ciMethod.getValue("name"); + String methodDescriptor = ciMethod.getValue("descriptor"); + return new MethodDesc(internalClassName, methodName, methodDescriptor); + } + + private static Method getMethod(Class aClass, String name, Class... params) { + try { + return aClass.getDeclaredMethod(name, params); + } catch (NoSuchMethodException | SecurityException e) { + throw new Error("TESTBUG : cannot get method " + name + Arrays.toString(params), e); + } + } + + private static Constructor getConstructor(Class aClass, Class... params) { + try { + return aClass.getDeclaredConstructor(params); + } catch (NoSuchMethodException | SecurityException e) { + throw new Error("TESTBUG : cannot get constructor" + Arrays.toString(params), e); + } + } +} + +class TestCase { + public TestCase() { + foo(); + } + + public void foo() { + qux(true); + bar(); + foo(2); + } + + private void foo(int i) { + } + + private void bar() { + baz(); + qux(false); + qux(true); + } + + protected static double baz() { + qux(false); + return .0; + } + + private static int qux(boolean b) { + qux(b); + return 0; + } +} + +/** + * data structure for method call + */ +class Call { + public final MethodDesc caller; + public final MethodDesc callee; + public final int bci; + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || !(o instanceof Call)) + return false; + + Call call = (Call) o; + + if (bci != call.bci) + return false; + if (!callee.equals(call.callee)) + return false; + if (!caller.equals(call.caller)) + return false; + + return true; + } + + @Override + public int hashCode() { + int result = caller.hashCode(); + result = 31 * result + callee.hashCode(); + result = 47 * result + bci; + return result; + } + + public Call(MethodDesc caller, MethodDesc callee, int bci) { + Objects.requireNonNull(caller); + Objects.requireNonNull(callee); + this.caller = caller; + this.callee = callee; + this.bci = bci; + } + + @Override + public String toString() { + return String.format("Call{caller='%s', callee='%s', bci=%d}", caller, callee, bci); + } +} + +/** + * data structure for method description + */ +class MethodDesc { + public final String className; + public final String methodName; + public final String descriptor; + + public MethodDesc(Class aClass, String methodName, String descriptor) { + this(aClass.getName().replace('.', '/'), methodName, descriptor); + } + + public MethodDesc(String className, String methodName, String descriptor) { + Objects.requireNonNull(className); + Objects.requireNonNull(methodName); + Objects.requireNonNull(descriptor); + this.className = className.replace('.', '/'); + this.methodName = methodName; + this.descriptor = descriptor; + } + + public MethodDesc(Executable executable) { + Class aClass = executable.getDeclaringClass(); + className = Type.getInternalName(aClass).replace('.', '/'); + + if (executable instanceof Constructor) { + methodName = ""; + descriptor = Type.getConstructorDescriptor((Constructor) executable); + } else { + methodName = executable.getName(); + descriptor = Type.getMethodDescriptor((Method) executable); + } + + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + MethodDesc that = (MethodDesc) o; + + if (!className.equals(that.className)) + return false; + if (!methodName.equals(that.methodName)) + return false; + if (!descriptor.equals(that.descriptor)) + return false; + + return true; + } + + @Override + public int hashCode() { + int result = className.hashCode(); + result = 31 * result + methodName.hashCode(); + result = 47 * result + descriptor.hashCode(); + return result; + } + + @Override + public String toString() { + return String.format("MethodDesc{className='%s', methodName='%s', descriptor='%s'}", className, methodName, descriptor); + } +} + +/** + * Aux class to get all calls in an arbitrary class. + */ +class InlineCalls { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + + private final Collection calls; + private final Map inline; + + public InlineCalls(Class aClass) { + calls = getCalls(aClass); + inline = new HashMap<>(); + } + + /** + * @return expected inline events + */ + public Map getExpected(Executable entry) { + Map result = new HashMap<>(); + Queue methods = new ArrayDeque<>(); + Set finished = new HashSet<>(); + methods.add(new MethodDesc(entry)); + while (!methods.isEmpty()) { + MethodDesc method = methods.poll(); + if (finished.add(method)) { + inline.entrySet().stream().filter(k -> k.getKey().caller.equals(method)).forEach(k -> { + result.put(k.getKey(), k.getValue()); + if (k.getValue()) { + methods.add(k.getKey().callee); + } + }); + } + } + + return result; + } + + public void disableInline(Executable executable) { + WHITE_BOX.testSetDontInlineMethod(executable, true); + MethodDesc md = new MethodDesc(executable); + calls.stream().filter(c -> c.callee.equals(md)).forEach(c -> inline.put(c, false)); + } + + public void forceInline(Executable executable) { + WHITE_BOX.testSetForceInlineMethod(executable, true); + MethodDesc md = new MethodDesc(executable); + calls.stream().filter(c -> c.callee.equals(md)).forEach(c -> inline.putIfAbsent(c, true)); + } + + private static Collection getCalls(Class aClass) { + List calls = new ArrayList<>(); + ClassWriter cw; + ClassReader cr; + try { + cr = new ClassReader(aClass.getName()); + } catch (IOException e) { + throw new Error("TESTBUG : unexpected IOE during class reading", e); + } + cw = new ClassWriter(cr, 0); + ClassVisitor cv = new ClassVisitor(Opcodes.ASM5, cw) { + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String descriptor, String[] exceptions) { + System.out.println("Method: " +name); + MethodVisitor mv = super.visitMethod(access, name, desc, descriptor, exceptions); + return new CallTracer(aClass, name, desc, mv, calls); + } + }; + cr.accept(cv, 0); + + return calls; + } + + private static class CallTracer extends MethodVisitor { + private final MethodDesc caller; + private Collection calls; + + public CallTracer(Class aClass, String name, String desc, MethodVisitor mv, Collection calls) { + super(Opcodes.ASM5, mv); + caller = new MethodDesc(aClass.getName(), name, desc); + this.calls = calls; + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + Label label = new Label(); + visitLabel(label); + super.visitMethodInsn(opcode, owner, name, desc, itf); + calls.add(new Call(caller, new MethodDesc(owner, name, desc), label.getOffset())); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/compiler/TestCompilerPhase.java 2019-02-08 18:33:12.496891593 +0300 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2013, 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.event.compiler; + +import java.lang.reflect.Method; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Utils; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import sun.hotspot.WhiteBox; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:CompileOnly=jdk.jfr.event.compiler.TestCompilerPhase::dummyMethod + * -XX:+SegmentedCodeCache -Xbootclasspath/a:. + * jdk.jfr.event.compiler.TestCompilerPhase + */ +public class TestCompilerPhase { + private final static String EVENT_NAME = EventNames.CompilerPhase; + private final static String METHOD_NAME = "dummyMethod"; + private static final int COMP_LEVEL_SIMPLE = 1; + private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + + // Provoke compilation + Method mtd = TestCompilerPhase.class.getDeclaredMethod(METHOD_NAME, new Class[0]); + WhiteBox WB = WhiteBox.getWhiteBox(); + String directive = "[{ match: \"" + TestCompilerPhase.class.getName().replace('.', '/') + + "." + METHOD_NAME + "\", " + "BackgroundCompilation: false }]"; + WB.addCompilerDirective(directive); + if (!WB.enqueueMethodForCompilation(mtd, COMP_LEVEL_FULL_OPTIMIZATION)) { + WB.enqueueMethodForCompilation(mtd, COMP_LEVEL_SIMPLE); + } + Utils.waitForCondition(() -> WB.isMethodCompiled(mtd)); + dummyMethod(); + + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + Events.assertField(event, "phase").notEmpty(); + Events.assertField(event, "compileId").atLeast(0); + Events.assertField(event, "phaseLevel").atLeast((short)0).atMost((short)4); + Events.assertEventThread(event); + } + } + + static void dummyMethod() { + System.out.println("hello!"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/compiler/TestCompilerStats.java 2019-02-08 18:33:12.640886571 +0300 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013, 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.event.compiler; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.compiler.TestCompilerStats + */ +public class TestCompilerStats { + private final static String EVENT_NAME = EventNames.CompilerStatistics; + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + Events.assertField(event, "compileCount").atLeast(0); + Events.assertField(event, "bailoutCount").atLeast(0); + Events.assertField(event, "invalidatedCount").atLeast(0); + Events.assertField(event, "osrCompileCount").atLeast(0); + Events.assertField(event, "standardCompileCount").atLeast(0); + Events.assertField(event, "osrBytesCompiled").atLeast(0L); + Events.assertField(event, "standardBytesCompiled").atLeast(0L); + Events.assertField(event, "nmetodsSize").atLeast(0L); + Events.assertField(event, "nmetodCodeSize").atLeast(0L); + Events.assertField(event, "peakTimeSpent").atLeast(0L); + Events.assertField(event, "totalTimeSpent").atLeast(0L); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/AppGCProvoker.java 2019-02-08 18:33:12.784881549 +0300 @@ -0,0 +1,57 @@ +/* + * 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.event.gc.collection; + +import java.util.ArrayList; +import java.util.List; + +/** + * Simple application to provoke various kinds of GC. + */ +public class AppGCProvoker { + + private static List garbage = new ArrayList<>(); + public static Object trash; + + public static void main(String args[]) { + // young gc + for (int i = 0; i < 100; i++) { + trash = new byte[100_000]; + } + + // system gc + System.gc(); + + // full gc caused by OOM + try { + while(true) { + garbage.add(new byte[150_000]); + } + } catch (OutOfMemoryError e) { + garbage = null; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/GCEventAll.java 2019-02-08 18:33:12.932876388 +0300 @@ -0,0 +1,530 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +import java.lang.management.ManagementFactory; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.stream.Collectors; + +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.GCHelper; + +/** + * Tests for event garbage_collection. + * The test function is called from TestGCEvent*.java, with different worker threads. + * Groups all events belonging to the same garbage collection (the same gcId). + * The group of events belonging to the same GC is called a batch. + * + * This class contains the verifications done and the worker threads used to generate GCs. + * The helper logic are in class GCHelper. + * + * Summary of verifications: + * All gcIds in garbage_collection event are unique. + * + * All events in batch has the same gcId. + * + * Number of garbage_collection events == GarbageCollectionMXBean.getCollectionCount() + * + * garbage_collection.sum_pause_time approximately equals GarbageCollectionMXBean.getCollectionTime() + * + * Batch contains expected events depending on garbage_collection.name + * + * garbage_collection_start.timestamp == garbage_collection.startTime. + * + * garbage_collection.timestamp >= timestamp for all other events in batch. + * + * The start_garbage_collection and garbage_collection events must be synchronized. + * This means that there may be multiple start_garbage_collection before a garbage_collection, + * but garbage_collection.gcId must be equal to latest start_garbage_collection.gcId. + * + * start_garbage_collection must be the first event in the batch, + * that means no event with same gcId before garbage_collection_start event. + * + * garbage_collection.name matches what is expected by the collectors specified in initial_configuration. + * + * Duration for event "vm/gc/phases/pause" >= 1. Duration for phase level events >= 0. + * + * + */ +public class GCEventAll { + private String youngCollector = null; + private String oldCollector = null; + + /** + * Trigger GC events by generating garbage and calling System.gc() concurrently. + */ + public static void doTest() throws Throwable { + // Trigger GC events by generating garbage and calling System.gc() concurrently. + Thread[] workerThreads = new Thread[] { + new Thread(GCEventAll.GarbageRunner.create(10)), + new Thread(GCEventAll.SystemGcWaitRunner.create(10, 2, 1000))}; + GCEventAll test = new GCEventAll(); + test.doSingleTest(workerThreads); + } + + /** + * Runs the test once with given worker threads. + * @param workerThreads Threads that generates GCs. + * @param gcIds Set of all used gcIds + * @throws Exception + */ + private void doSingleTest(Thread[] workerThreads) throws Throwable { + Recording recording = new Recording(); + enableAllGcEvents(recording); + + // Start with a full GC to minimize risk of getting extra GC between + // getBeanCollectionCount() and recording.start(). + doSystemGc(); + GCHelper.CollectionSummary startBeanCount = GCHelper.CollectionSummary.createFromMxBeans(); + recording.start(); + + for (Thread t : workerThreads) { + t.start(); + } + for (Thread t : workerThreads) { + t.join(); + } + + // End with a full GC to minimize risk of getting extra GC between + // recording.stop and getBeanCollectionCount(). + doSystemGc(); + // Add an extra System.gc() to make sure we get at least one full garbage_collection batch at + // the end of the test. This extra System.gc() is only necessary when using "UseConcMarkSweepGC" and "+ExplicitGCInvokesConcurrent". + doSystemGc(); + + recording.stop(); + GCHelper.CollectionSummary deltaBeanCount = GCHelper.CollectionSummary.createFromMxBeans(); + deltaBeanCount = deltaBeanCount.calcDelta(startBeanCount); + + List events = Events.fromRecording(recording).stream() + .filter(evt -> EventNames.isGcEvent(evt.getEventType())) + .collect(Collectors.toList()); + RecordedEvent configEvent = GCHelper.getConfigEvent(events); + youngCollector = Events.assertField(configEvent, "youngCollector").notEmpty().getValue(); + oldCollector = Events.assertField(configEvent, "oldCollector").notEmpty().getValue(); + verify(events, deltaBeanCount); + } + + private void enableAllGcEvents(Recording recording) { + FlightRecorder flightrecorder = FlightRecorder.getFlightRecorder(); + for (EventType et : flightrecorder.getEventTypes()) { + if (EventNames.isGcEvent(et)) { + recording.enable(et.getName()); + System.out.println("Enabled GC event: " + et.getName()); + } + } + System.out.println("All GC events enabled"); + } + + private static synchronized void doSystemGc() { + System.gc(); + } + + /** + * Does all verifications of the received events. + * + * @param events All flight recorder events. + * @param beanCounts Number of collections and sum pause time reported by GarbageCollectionMXBeans. + * @param gcIds All used gcIds. Must be unique. + * @throws Exception + */ + private void verify(List events, GCHelper.CollectionSummary beanCounts) throws Throwable { + List gcBatches = null; + GCHelper.CollectionSummary eventCounts = null; + + // For some GC configurations, the JFR recording may have stopped before we received the last gc event. + try { + events = filterIncompleteGcBatch(events); + gcBatches = GCHelper.GcBatch.createFromEvents(events); + eventCounts = GCHelper.CollectionSummary.createFromEvents(gcBatches); + + verifyUniqueIds(gcBatches); + verifyCollectorNames(gcBatches); + verifyCollectionCause(gcBatches); + verifyCollectionCount(eventCounts, beanCounts); + verifyPhaseEvents(gcBatches); + verifySingleGcBatch(gcBatches); + } catch (Throwable t) { + log(events, gcBatches, eventCounts, beanCounts); + if (gcBatches != null) { + for (GCHelper.GcBatch batch : gcBatches) { + System.out.println(String.format("Batch:%n%s", batch.getLog())); + } + } + throw t; + } + } + + /** + * When using collector ConcurrentMarkSweep with -XX:+ExplicitGCInvokesConcurrent, the JFR recording may + * stop before we have received the last garbage_collection event. + * + * This function does 3 things: + * 1. Check if the last batch is incomplete. + * 2. If it is incomplete, then asserts that incomplete batches are allowed for this configuration. + * 3. If incomplete batches are allowed, then the incomplete batch is removed. + * + * @param events All events + * @return All events with any incomplete batch removed. + * @throws Throwable + */ + private List filterIncompleteGcBatch(List events) throws Throwable { + List returnEvents = new ArrayList(events); + int lastGcId = getLastGcId(events); + List lastBatchEvents = getEventsWithGcId(events, lastGcId); + String[] endEvents = {GCHelper.event_garbage_collection, GCHelper.event_old_garbage_collection, GCHelper.event_young_garbage_collection}; + boolean isComplete = containsAnyPath(lastBatchEvents, endEvents); + if (!isComplete) { + // The last GC batch does not contain an end event. The batch is incomplete. + // This is only allowed if we are using old_collector="ConcurrentMarkSweep" and "-XX:+ExplicitGCInvokesConcurrent" + boolean isExplicitGCInvokesConcurrent = hasInputArgument("-XX:+ExplicitGCInvokesConcurrent"); + boolean isConcurrentMarkSweep = GCHelper.gcConcurrentMarkSweep.equals(oldCollector); + String msg = String.format( + "Incomplete batch only allowed for '%s' with -XX:+ExplicitGCInvokesConcurrent", + GCHelper.gcConcurrentMarkSweep); + Asserts.assertTrue(isConcurrentMarkSweep && isExplicitGCInvokesConcurrent, msg); + + // Incomplete batch is allowed with the current settings. Remove incomplete batch. + returnEvents.removeAll(lastBatchEvents); + } + return returnEvents; + } + + private boolean hasInputArgument(String arg) { + return ManagementFactory.getRuntimeMXBean().getInputArguments().contains(arg); + } + + private List getEventsWithGcId(List events, int gcId) { + List batchEvents = new ArrayList<>(); + for (RecordedEvent event : events) { + if (GCHelper.isGcEvent(event) && GCHelper.getGcId(event) == gcId) { + batchEvents.add(event); + } + } + return batchEvents; + } + + private boolean containsAnyPath(List events, String[] paths) { + List pathList = Arrays.asList(paths); + for (RecordedEvent event : events) { + if (pathList.contains(event.getEventType().getName())) { + return true; + } + } + return false; + } + + private int getLastGcId(List events) { + int lastGcId = -1; + for (RecordedEvent event : events) { + if (GCHelper.isGcEvent(event)) { + int gcId = GCHelper.getGcId(event); + if (gcId > lastGcId) { + lastGcId = gcId; + } + } + } + Asserts.assertTrue(lastGcId != -1, "No gcId found"); + return lastGcId; + } + + /** + * Verifies collection count reported by flight recorder events against the values + * reported by GarbageCollectionMXBean. + * Number of collections should match exactly. + * Sum pause time are allowed some margin of error because of rounding errors in measurements. + */ + private void verifyCollectionCount(GCHelper.CollectionSummary eventCounts, GCHelper.CollectionSummary beanCounts) { + verifyCollectionCount(youngCollector, eventCounts.collectionCountYoung, beanCounts.collectionCountYoung); + verifyCollectionCount(oldCollector, eventCounts.collectionCountOld, beanCounts.collectionCountOld); + } + + private void verifyCollectionCount(String collector, long eventCounts, long beanCounts) { + if (GCHelper.gcConcurrentMarkSweep.equals(collector) || GCHelper.gcG1Old.equals(oldCollector)) { + // ConcurrentMarkSweep mixes old and new collections. Not same values as in MXBean. + // MXBean does not report old collections for G1Old, so we have nothing to compare with. + return; + } + // JFR events and GarbageCollectorMXBean events are not updated at the same time. + // This means that number of collections may diff. + // We allow a diff of +- 1 collection count. + long minCount = Math.max(0, beanCounts - 1); + long maxCount = beanCounts + 1; + Asserts.assertGreaterThanOrEqual(eventCounts, minCount, "Too few event counts for collector " + collector); + Asserts.assertLessThanOrEqual(eventCounts, maxCount, "Too many event counts for collector " + collector); + } + + /** + * Verifies that all events belonging to a single GC are ok. + * A GcBatch contains all flight recorder events that belong to a single GC. + */ + private void verifySingleGcBatch(List batches) { + for (GCHelper.GcBatch batch : batches) { + //System.out.println("batch:\r\n" + batch.getLog()); + try { + RecordedEvent endEvent = batch.getEndEvent(); + Asserts.assertNotNull(endEvent, "No end event in batch."); + Asserts.assertNotNull(batch.getName(), "No method name in end event."); + long longestPause = Events.assertField(endEvent, "longestPause").atLeast(0L).getValue(); + Events.assertField(endEvent, "sumOfPauses").atLeast(longestPause).getValue(); + Instant batchStartTime = endEvent.getStartTime(); + Instant batchEndTime = endEvent.getEndTime(); + for (RecordedEvent event : batch.getEvents()) { + if (event.getEventType().getName().contains("AllocationRequiringGC")) { + // Unlike other events, these are sent *before* a GC. + Asserts.assertLessThanOrEqual(event.getStartTime(), batchStartTime, "Timestamp in event after start event, should be sent before GC start"); + } else { + Asserts.assertGreaterThanOrEqual(event.getStartTime(), batchStartTime, "startTime in event before batch start event, should be sent after GC start"); + } + Asserts.assertLessThanOrEqual(event.getEndTime(), batchEndTime, "endTime in event after batch end event, should be sent before GC end"); + } + + // Verify that all required events has been received. + String[] requiredEvents = GCHelper.requiredEvents.get(batch.getName()); + Asserts.assertNotNull(requiredEvents, "No required events specified for " + batch.getName()); + for (String requiredEvent : requiredEvents) { + boolean b = batch.containsEvent(requiredEvent); + Asserts.assertTrue(b, String.format("%s does not contain event %s", batch, requiredEvent)); + } + + // Verify that we have exactly one heap_summary "Before GC" and one "After GC". + int countBeforeGc = 0; + int countAfterGc = 0; + for (RecordedEvent event : batch.getEvents()) { + if (GCHelper.event_heap_summary.equals(event.getEventType().getName())) { + String when = Events.assertField(event, "when").notEmpty().getValue(); + if ("Before GC".equals(when)) { + countBeforeGc++; + } else if ("After GC".equals(when)) { + countAfterGc++; + } else { + Asserts.fail("Unknown value for heap_summary.when: '" + when + "'"); + } + } + } + if (!GCHelper.gcConcurrentMarkSweep.equals(batch.getName())) { + // We do not get heap_summary events for ConcurrentMarkSweep + Asserts.assertEquals(1, countBeforeGc, "Unexpected number of heap_summary.before_gc"); + Asserts.assertEquals(1, countAfterGc, "Unexpected number of heap_summary.after_gc"); + } + } catch (Throwable e) { + GCHelper.log("verifySingleGcBatch failed for gcEvent:"); + GCHelper.log(batch.getLog()); + throw e; + } + } + } + + private Set verifyUniqueIds(List batches) { + Set gcIds = new HashSet<>(); + for (GCHelper.GcBatch batch : batches) { + Integer gcId = new Integer(batch.getGcId()); + Asserts.assertFalse(gcIds.contains(gcId), "Duplicate gcId: " + gcId); + gcIds.add(gcId); + } + return gcIds; + } + + private void verifyPhaseEvents(List batches) { + for (GCHelper.GcBatch batch : batches) { + for(RecordedEvent event : batch.getEvents()) { + if (event.getEventType().getName().contains(GCHelper.pauseLevelEvent)) { + Instant batchStartTime = batch.getEndEvent().getStartTime(); + Asserts.assertGreaterThanOrEqual( + event.getStartTime(), batchStartTime, "Phase startTime >= batch startTime. Event:" + event); + + // Duration for event "vm/gc/phases/pause" must be >= 1. Other phase event durations must be >= 0. + Duration minDuration = Duration.ofNanos(GCHelper.event_phases_pause.equals(event.getEventType().getName()) ? 1 : 0); + Duration duration = event.getDuration(); + Asserts.assertGreaterThanOrEqual(duration, minDuration, "Wrong duration. Event:" + event); + } + } + } + } + + /** + * Verifies that the collector name in initial configuration matches the name in garbage configuration event. + * If the names are not equal, then we check if this is an expected collector override. + * For example, if old collector in initial config is "G1Old" we allow both event "G1Old" and "SerialOld". + */ + private void verifyCollectorNames(List batches) { + for (GCHelper.GcBatch batch : batches) { + String name = batch.getName(); + Asserts.assertNotNull(name, "garbage_collection.name was null"); + boolean isYoung = batch.isYoungCollection(); + String expectedName = isYoung ? youngCollector : oldCollector; + if (!expectedName.equals(name)) { + // Collector names not equal. Check if the collector has been overridden by an expected collector. + String overrideKey = expectedName + "." + name; + boolean isOverride = GCHelper.collectorOverrides.contains(overrideKey); + Asserts.assertTrue(isOverride, String.format("Unexpected event name(%s) for collectors(%s, %s)", name, youngCollector, oldCollector)); + } + } + } + + /** + * Verifies field "cause" in garbage_collection event. + * Only check that at cause is not null and that at least 1 cause is "System.gc()" + * We might want to check more cause reasons later. + */ + private void verifyCollectionCause(List batches) { + int systemGcCount = 0; + for (GCHelper.GcBatch batch : batches) { + RecordedEvent endEvent = batch.getEndEvent(); + String cause = Events.assertField(endEvent, "cause").notEmpty().getValue(); + // A System.GC() can be consolidated into a GCLocker GC + if (cause.equals("System.gc()") || cause.equals("GCLocker Initiated GC")) { + systemGcCount++; + } + Asserts.assertNotNull(batch.getName(), "garbage_collection.name was null"); + } + final String msg = "No event with cause=System.gc(), collectors(%s, %s)"; + Asserts.assertTrue(systemGcCount > 0, String.format(msg, youngCollector, oldCollector)); + } + + private void log(List events, List batches, + GCHelper.CollectionSummary eventCounts, GCHelper.CollectionSummary beanCounts) { + GCHelper.log("EventCounts:"); + if (eventCounts != null) { + GCHelper.log(eventCounts.toString()); + } + GCHelper.log("BeanCounts:"); + if (beanCounts != null) { + GCHelper.log(beanCounts.toString()); + } + } + + /** + * Thread that does a number of System.gc(). + */ + public static class SystemGcRunner implements Runnable { + private final int totalCollections; + + public SystemGcRunner(int totalCollections) { + this.totalCollections = totalCollections; + } + + public static SystemGcRunner create(int totalCollections) { + return new SystemGcRunner(totalCollections); + } + + public void run() { + for (int i = 0; i < totalCollections; i++) { + GCEventAll.doSystemGc(); + } + } + } + + /** + * Thread that creates garbage until a certain number of GCs has been run. + */ + public static class GarbageRunner implements Runnable { + private final int totalCollections; + public byte[] dummyBuffer = null; + + public GarbageRunner(int totalCollections) { + this.totalCollections = totalCollections; + } + + public static GarbageRunner create(int totalCollections) { + return new GarbageRunner(totalCollections); + } + + public void run() { + long currCollections = GCHelper.CollectionSummary.createFromMxBeans().sum(); + long endCollections = totalCollections + currCollections; + Random r = new Random(0); + while (true) { + for (int i = 0; i < 1000; i++) { + dummyBuffer = new byte[r.nextInt(10000)]; + } + if (GCHelper.CollectionSummary.createFromMxBeans().sum() >= endCollections) { + break; + } + } + } + } + + /** + * Thread that runs System.gc() and then wait for a number of GCs or a maximum time. + */ + public static class SystemGcWaitRunner implements Runnable { + private final int totalCollections; + private final int minWaitCollections; + private final long maxWaitMillis; + + public SystemGcWaitRunner(int totalCollections, int minWaitCollections, long maxWaitMillis) { + this.totalCollections = totalCollections; + this.minWaitCollections = minWaitCollections; + this.maxWaitMillis = maxWaitMillis; + } + + public static SystemGcWaitRunner create(int deltaCollections, int minWaitCollections, long maxWaitMillis) { + return new SystemGcWaitRunner(deltaCollections, minWaitCollections, maxWaitMillis); + } + + public void run() { + long currCount = GCHelper.CollectionSummary.createFromMxBeans().sum(); + long endCount = totalCollections + currCount; + long nextSystemGcCount = currCount + minWaitCollections; + long now = System.currentTimeMillis(); + long nextSystemGcMillis = now + maxWaitMillis; + + while (true) { + if (currCount >= nextSystemGcCount || System.currentTimeMillis() > nextSystemGcMillis) { + GCEventAll.doSystemGc(); + currCount = GCHelper.CollectionSummary.createFromMxBeans().sum(); + nextSystemGcCount = currCount + minWaitCollections; + } else { + try { + Thread.sleep(20); + } catch (InterruptedException e) { + e.printStackTrace(); + break; + } + } + currCount = GCHelper.CollectionSummary.createFromMxBeans().sum(); + if (currCount >= endCount) { + break; + } + } + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/GCGarbageCollectionUtil.java 2019-02-08 18:33:13.076871367 +0300 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +import static jdk.test.lib.Asserts.assertGreaterThan; +import static jdk.test.lib.Asserts.assertTrue; + +import java.io.File; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.jfr.AppExecutorHelper; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.process.OutputAnalyzer; + + +/** + * Class to verify GarbageCollection event. + * It starts the application provoking GCGarbageCollection with enabled JFR, + * collects events and verify the 'name' and 'cause' fields to be expected. + * It's supposed to be invoked from tests. + */ +public class GCGarbageCollectionUtil { + private final static String EVENT_SETTINGS_FILE = + System.getProperty("test.src", ".") + File.separator + "gc-testsettings.jfc"; + + /** + * Verifies the 'name' and 'cause' fields of received events to be expected. + * @param testID - a string to identify test + * @param testFlags - VM flags including GC to start the app + * @param gcNames - expected values for the 'name' field + * @param gcCauses - expected values for the 'cause' field + * @throws Exception in case of any failure + */ + public static void test(String testID, String[] testFlags, + String[] gcNames, String... gcCauses) throws Exception { + + String jfrFile = testID + ".jfr"; + + List summaryFlags = new ArrayList<>(); + Collections.addAll(summaryFlags, testFlags); + summaryFlags.add("-Xmx100m"); + summaryFlags.add("-XX:+UnlockExperimentalVMOptions"); + summaryFlags.add("-XX:-UseFastUnorderedTimeStamps"); + summaryFlags.add("-XX:+PrintGCDetails"); + summaryFlags.add("-XX:+PrintGC"); + + + String args[] = {}; + OutputAnalyzer analyzer = AppExecutorHelper.executeAndRecord(EVENT_SETTINGS_FILE, jfrFile, + (String[])summaryFlags.toArray(new String[0]), AppGCProvoker.class.getName(), args); + analyzer.shouldHaveExitValue(0); + + Set gcValidNames = new HashSet<>(); + for (String n: gcNames) { + gcValidNames.add(n); + } + Set gcValidCauses = new HashSet<>(); + for (String n: gcCauses) { + gcValidCauses.add(n); + } + + int total = 0; + for (RecordedEvent event : RecordingFile.readAllEvents(Paths.get(jfrFile))) { + total++; + System.out.println("Event: " + event); + + final String name = Events.assertField(event, "name").notEmpty().getValue(); + assertTrue(gcValidNames.contains(name), "GC name '" + name + "' not in the valid list" + gcValidNames); + + final String cause = Events.assertField(event, "cause").notEmpty().getValue(); + assertTrue(gcValidCauses.contains(cause), "GC cause '" + cause + "' not in the valid causes" + gcValidCauses); + } + assertGreaterThan(total, 0, "Expected at least one event"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCCauseWithCMSConcurrent.java 2019-02-08 18:33:13.220866345 +0300 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run driver jdk.jfr.event.gc.collection.TestGCCauseWithCMSConcurrent + */ +public class TestGCCauseWithCMSConcurrent { + public static void main(String[] args) throws Exception { + String testID = "CMSConcurrent"; + String[] vmFlags = {"-XX:+UseConcMarkSweepGC", "-XX:+ExplicitGCInvokesConcurrent"}; + String[] gcNames = {GCHelper.gcConcurrentMarkSweep, GCHelper.gcParNew, GCHelper.gcSerialOld}; + String[] gcCauses = {"CMS Concurrent Mark", "Allocation Failure", "System.gc()"}; + GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses); + } +} + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCCauseWithCMSMarkSweep.java 2019-02-08 18:33:13.368861184 +0300 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * + * + * @library /lib / + * @run driver jdk.jfr.event.gc.collection.TestGCCauseWithCMSMarkSweep + */ +public class TestGCCauseWithCMSMarkSweep { + public static void main(String[] args) throws Exception { + String testID = "CMSMarkSweep"; + String[] vmFlags = {"-XX:+UseConcMarkSweepGC"}; + String[] gcNames = {GCHelper.gcConcurrentMarkSweep, GCHelper.gcParNew, GCHelper.gcSerialOld}; + String[] gcCauses = {"CMS Concurrent Mark", "Allocation Failure", "System.gc()"}; + GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses); + } +} + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCCauseWithG1ConcurrentMark.java 2019-02-08 18:33:13.508856302 +0300 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * + * + * @library /lib / + * + * @run driver jdk.jfr.event.gc.collection.TestGCCauseWithG1ConcurrentMark + */ +public class TestGCCauseWithG1ConcurrentMark { + public static void main(String[] args) throws Exception { + String testID = "G1ConcurrentMark"; + String[] vmFlags = {"-XX:+UseG1GC", "-XX:+ExplicitGCInvokesConcurrent"}; + String[] gcNames = {GCHelper.gcG1New, GCHelper.gcG1Old, GCHelper.gcG1Full}; + String[] gcCauses = {"G1 Evacuation Pause", "Allocation Failure", "System.gc()"}; + GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses); + } +} + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCCauseWithG1FullCollection.java 2019-02-08 18:33:13.652851281 +0300 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * + * + * @library /lib / + * + * @run driver jdk.jfr.event.gc.collection.TestGCCauseWithG1FullCollection + */ +public class TestGCCauseWithG1FullCollection { + public static void main(String[] args) throws Exception { + String testID = "G1FullCollection"; + String[] vmFlags = {"-XX:+UseG1GC"}; + String[] gcNames = {GCHelper.gcG1New, GCHelper.gcG1Old, GCHelper.gcG1Full}; + String[] gcCauses = {"G1 Evacuation Pause", "Allocation Failure", "System.gc()"}; + GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCCauseWithPSMarkSweep.java 2019-02-08 18:33:13.796846259 +0300 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * + * @run driver jdk.jfr.event.gc.collection.TestGCCauseWithPSMarkSweep + */ +public class TestGCCauseWithPSMarkSweep { + public static void main(String[] args) throws Exception { + String testID = "PSMarkSweep"; + String[] vmFlags = {"-XX:+UseParallelGC", "-XX:-UseParallelOldGC"}; + String[] gcNames = {GCHelper.gcParallelScavenge, GCHelper.gcSerialOld}; + String[] gcCauses = {"Allocation Failure", "Ergonomics", "System.gc()"}; + GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses); + } +} + + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCCauseWithParallelOld.java 2019-02-08 18:33:13.944841098 +0300 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * + * @run driver jdk.jfr.event.gc.collection.TestGCCauseWithParallelOld + */ +public class TestGCCauseWithParallelOld { + public static void main(String[] args) throws Exception { + String testID = "ParallelOld"; + String[] vmFlags = {"-XX:+UseParallelGC", "-XX:+UseParallelOldGC"}; + String[] gcNames = {GCHelper.gcParallelScavenge, GCHelper.gcParallelOld}; + String[] gcCauses = {"Allocation Failure", "Ergonomics", "System.gc()"}; + GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCCauseWithSerial.java 2019-02-08 18:33:14.088836077 +0300 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * + * @run driver jdk.jfr.event.gc.collection.TestGCCauseWithSerial + */ +public class TestGCCauseWithSerial { + public static void main(String[] args) throws Exception { + String testID = "Serial"; + String[] vmFlags = {"-XX:+UseSerialGC"}; + String[] gcNames = {GCHelper.gcDefNew, GCHelper.gcSerialOld}; + String[] gcCauses = {"Allocation Failure", "System.gc()"}; + GCGarbageCollectionUtil.test(testID, vmFlags, gcNames, gcCauses); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCEventMixedWithCMSConcurrent.java 2019-02-08 18:33:14.232831055 +0300 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xmx32m -Xmn8m -XX:+UseConcMarkSweepGC -XX:+ExplicitGCInvokesConcurrent jdk.jfr.event.gc.collection.TestGCEventMixedWithCMSConcurrent + * good debug flags:-XX:+PrintGCDetails -XX:+PrintGC + */ +public class TestGCEventMixedWithCMSConcurrent { + public static void main(String[] args) throws Throwable { + GCEventAll.doTest(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCEventMixedWithCMSMarkSweep.java 2019-02-08 18:33:14.376826035 +0300 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xmx32m -Xmn8m -XX:+UseConcMarkSweepGC -XX:-ExplicitGCInvokesConcurrent jdk.jfr.event.gc.collection.TestGCEventMixedWithCMSMarkSweep + * good debug flags:-XX:+PrintGCDetails -XX:+PrintGC + */ +public class TestGCEventMixedWithCMSMarkSweep { + public static void main(String[] args) throws Throwable { + GCEventAll.doTest(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCEventMixedWithG1ConcurrentMark.java 2019-02-08 18:33:14.524820874 +0300 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xmx32m -Xmn8m -XX:+UseG1GC -XX:+ExplicitGCInvokesConcurrent jdk.jfr.event.gc.collection.TestGCEventMixedWithG1ConcurrentMark + * good debug flags: -XX:+PrintGCDetails -XX:+PrintGC + */ +// TODO: Try to run without: -XX:+UnlockExperimentalVMOptions XX:-UseFastUnorderedTimeStamps +public class TestGCEventMixedWithG1ConcurrentMark { + public static void main(String[] args) throws Throwable { + GCEventAll.doTest(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCEventMixedWithG1FullCollection.java 2019-02-08 18:33:14.668815853 +0300 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xmx32m -Xmn8m -XX:+UseG1GC -XX:-ExplicitGCInvokesConcurrent jdk.jfr.event.gc.collection.TestGCEventMixedWithG1FullCollection + * good debug flags: -XX:+PrintGCDetails -XX:+PrintGC + */ +public class TestGCEventMixedWithG1FullCollection { + public static void main(String[] args) throws Throwable { + GCEventAll.doTest(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCEventMixedWithPSMarkSweep.java 2019-02-08 18:33:14.812810832 +0300 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xmx32m -Xmn8m -XX:+UseParallelGC -XX:-UseParallelOldGC jdk.jfr.event.gc.collection.TestGCEventMixedWithPSMarkSweep + * good debug flags:-XX:+PrintGCDetails -XX:+PrintGC + */ +public class TestGCEventMixedWithPSMarkSweep { + public static void main(String[] args) throws Throwable { + GCEventAll.doTest(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCEventMixedWithParNew.java 2019-02-08 18:33:14.956805810 +0300 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main/othervm -Xmx32m -Xmn8m -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseConcMarkSweepGC jdk.jfr.event.gc.collection.TestGCEventMixedWithParNew + * good debug flags:-XX:+PrintGCDetails -XX:+PrintGC + */ + +public class TestGCEventMixedWithParNew { + public static void main(String[] args) throws Throwable { + GCEventAll.doTest(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCEventMixedWithParallelOld.java 2019-02-08 18:33:15.100800790 +0300 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xmx32m -Xmn8m -XX:+UseParallelGC -XX:+UseParallelOldGC jdk.jfr.event.gc.collection.TestGCEventMixedWithParallelOld + * good debug flags: -XX:+PrintGCDetails -XX:+PrintGC -verbose:class + */ +public class TestGCEventMixedWithParallelOld { + public static void main(String[] args) throws Throwable { + GCEventAll.doTest(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCEventMixedWithSerial.java 2019-02-08 18:33:15.244795769 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -Xmx32m -Xmn8m -XX:+UseSerialGC jdk.jfr.event.gc.collection.TestGCEventMixedWithSerial + */ +public class TestGCEventMixedWithSerial { + public static void main(String[] args) throws Throwable { + GCEventAll.doTest(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCGarbageCollectionEvent.java 2019-02-08 18:33:15.392790608 +0300 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +import static jdk.test.lib.Asserts.assertTrue; + +import java.time.Duration; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm -XX:+PrintGCDetails -XX:+PrintGC -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps jdk.jfr.event.gc.collection.TestGCGarbageCollectionEvent + */ +public class TestGCGarbageCollectionEvent { + + private final static String EVENT_NAME = GCHelper.event_garbage_collection; + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + recording.start(); + System.gc(); + recording.stop(); + + boolean isAnyFound = false; + for (RecordedEvent event : Events.fromRecording(recording)) { + if (!EVENT_NAME.equals(event.getEventType().getName())) { + continue; + } + System.out.println("Event: " + event); + isAnyFound = true; + + long longestPause = Events.assertField(event, "longestPause").atLeast(0L).getValue(); + Events.assertField(event, "sumOfPauses").atLeast(longestPause); + } + assertTrue(isAnyFound, "No matching event found"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestGCWithFasttime.java 2019-02-08 18:33:15.536785588 +0300 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UseParallelGC -XX:+UseParallelOldGC jdk.jfr.event.gc.collection.TestGCWithFasttime + */ +public class TestGCWithFasttime { + private static final String EVENT_NAME = GCHelper.event_garbage_collection; + + // TODO: Check if all GC tests can be run with fast time. If so, this test is probably not needed. + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + recording.start(); + System.gc(); + System.gc(); + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + if (!EVENT_NAME.equals(event.getEventType().getName())) { + continue; + } + Events.assertField(event, "name").notEmpty(); + Events.assertField(event, "cause").notEmpty(); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestYoungGarbageCollectionEventWithDefNew.java 2019-02-08 18:33:15.676780707 +0300 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -Xmx50m -Xmn2m -XX:+UseSerialGC -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+PrintGCDetails -XX:+PrintGC jdk.jfr.event.gc.collection.TestYoungGarbageCollectionEventWithDefNew + */ +public class TestYoungGarbageCollectionEventWithDefNew { + + public static void main(String[] args) throws Exception { + YoungGarbageCollectionEvent.test(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestYoungGarbageCollectionEventWithG1New.java 2019-02-08 18:33:15.824775547 +0300 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -Xmx50m -Xmn2m -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+PrintGCDetails -XX:+PrintGC jdk.jfr.event.gc.collection.TestYoungGarbageCollectionEventWithG1New + */ +public class TestYoungGarbageCollectionEventWithG1New { + + public static void main(String[] args) throws Exception { + YoungGarbageCollectionEvent.test(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestYoungGarbageCollectionEventWithParNew.java 2019-02-08 18:33:15.972770386 +0300 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -Xmx50m -Xmn2m -XX:+UseConcMarkSweepGC -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+PrintGCDetails -XX:+PrintGC jdk.jfr.event.gc.collection.TestYoungGarbageCollectionEventWithParNew + */ +public class TestYoungGarbageCollectionEventWithParNew { + + public static void main(String[] args) throws Exception { + YoungGarbageCollectionEvent.test(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/TestYoungGarbageCollectionEventWithParallelScavenge.java 2019-02-08 18:33:16.116765366 +0300 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -Xmx50m -Xmn2m -XX:+UseParallelGC -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:-UseAdaptiveSizePolicy -XX:+PrintGCDetails -XX:+PrintGC jdk.jfr.event.gc.collection.TestYoungGarbageCollectionEventWithParallelScavenge + */ +public class TestYoungGarbageCollectionEventWithParallelScavenge { + + public static void main(String[] args) throws Exception { + YoungGarbageCollectionEvent.test(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/YoungGarbageCollectionEvent.java 2019-02-08 18:33:16.260760346 +0300 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2013, 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.event.gc.collection; + +import static jdk.test.lib.Asserts.assertGreaterThan; +import static jdk.test.lib.Asserts.assertTrue; + +import java.time.Duration; +import java.time.Instant; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.GCHelper; + + +public class YoungGarbageCollectionEvent { + + private static final String EVENT_NAME = GCHelper.event_young_garbage_collection; + + public static void test() throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + recording.start(); + triggerGC(); + recording.stop(); + + boolean isAnyFound = false; + for (RecordedEvent event : Events.fromRecording(recording)) { + if (!EVENT_NAME.equals(event.getEventType().getName())) { + continue; + } + System.out.println("Event: " + event); + isAnyFound = true; + Events.assertField(event, "gcId").atLeast(0); + Events.assertField(event, "tenuringThreshold").atLeast(1); + + Instant startTime = event.getStartTime(); + Instant endTime = event.getEndTime(); + Duration duration = event.getDuration(); + assertGreaterThan(startTime, Instant.EPOCH, "startTime should be at least 0"); + assertGreaterThan(endTime, Instant.EPOCH, "endTime should be at least 0"); + // TODO: Maybe we should accept duration of 0, but old test did not. + assertGreaterThan(duration, Duration.ZERO, "Duration should be above 0"); + assertGreaterThan(endTime, startTime, "End time should be after start time"); + } + assertTrue(isAnyFound, "No matching event found"); + } + + public static byte[] garbage; + private static void triggerGC() { + for (int i = 0; i < 3072; i++) { + garbage = new byte[1024]; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/collection/gc-testsettings.jfc 2019-02-08 18:33:16.404755325 +0300 @@ -0,0 +1,32 @@ + + + + + + true + 0 ms + + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/configuration/GCHeapConfigurationEventTester.java 2019-02-08 18:33:16.548750305 +0300 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013, 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.event.gc.configuration; + +import static jdk.test.lib.Asserts.assertGreaterThanOrEqual; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.EventVerifier; +import jdk.test.lib.jfr.Events; + + +public abstract class GCHeapConfigurationEventTester { + public void run() throws Exception { + Recording recording = new Recording(); + recording.enable(EventNames.GCHeapConfiguration); + recording.start(); + recording.stop(); + List events = Events.fromRecording(recording); + assertGreaterThanOrEqual(events.size(), 1, "Expected at least 1 event"); + EventVerifier v = createVerifier(events.get(0)); + v.verify(); + } + + protected abstract EventVerifier createVerifier(RecordedEvent e); +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/configuration/GCHeapConfigurationEventVerifier.java 2019-02-08 18:33:16.692745284 +0300 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013, 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.event.gc.configuration; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventVerifier; + +public abstract class GCHeapConfigurationEventVerifier extends EventVerifier { + public GCHeapConfigurationEventVerifier(RecordedEvent e) { + super(e); + } + + protected void verifyMinHeapSizeIs(long expected) throws Exception { + verifyEquals("minSize", expected); + } + + protected void verifyInitialHeapSizeIs(long expected) throws Exception { + verifyEquals("initialSize", expected); + } + + protected void verifyMaxHeapSizeIs(long expected) throws Exception { + verifyEquals("maxSize", expected); + } + + protected void verifyUsesCompressedOopsIs(boolean expected) throws Exception { + verifyEquals("usesCompressedOops", expected); + } + + protected void verifyObjectAlignmentInBytesIs(int expected) throws Exception { + verifyEquals("objectAlignment", (long)expected); + } + + protected void verifyHeapAddressBitsIs(int expected) throws Exception { + verifyEquals("heapAddressBits", (byte)expected); + } + + protected void verifyCompressedOopModeIs(String expected) throws Exception { + verifyEquals("compressedOopsMode", expected); + } + + protected void verifyCompressedOopModeContains(String expected) throws Exception { + verifyContains("compressedOopsMode", expected); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/configuration/GCYoungGenerationConfigurationEventTester.java 2019-02-08 18:33:16.836740264 +0300 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013, 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.event.gc.configuration; + +import static jdk.test.lib.Asserts.assertGreaterThanOrEqual; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.EventVerifier; +import jdk.test.lib.jfr.Events; + +public abstract class GCYoungGenerationConfigurationEventTester { + public void run() throws Exception { + Recording recording = new Recording(); + recording.enable(EventNames.YoungGenerationConfiguration); + recording.start(); + recording.stop(); + List events = Events.fromRecording(recording); + assertGreaterThanOrEqual(events.size(), 1, "Expected at least 1 event"); + EventVerifier v = createVerifier(events.get(0)); + v.verify(); + } + + protected abstract EventVerifier createVerifier(RecordedEvent e); +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/configuration/TestGCConfigurationEvent.java 2019-02-08 18:33:16.980735244 +0300 @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2013, 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.event.gc.configuration; + +import static jdk.test.lib.Asserts.assertGreaterThanOrEqual; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.EventVerifier; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:ParallelGCThreads=3 -XX:ConcGCThreads=2 -XX:+UseDynamicNumberOfGCThreads -XX:-ExplicitGCInvokesConcurrent -XX:-DisableExplicitGC -XX:MaxGCPauseMillis=800 -XX:GCTimeRatio=19 jdk.jfr.event.gc.configuration.TestGCConfigurationEvent + */ +public class TestGCConfigurationEvent { + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EventNames.GCConfiguration); + recording.start(); + recording.stop(); + List events = Events.fromRecording(recording); + assertGreaterThanOrEqual(events.size(), 1, "Expected at least 1 event"); + GCConfigurationEventVerifier verifier = new GCConfigurationEventVerifier(events.get(0)); + verifier.verify(); + } +} + +class GCConfigurationEventVerifier extends EventVerifier { + public GCConfigurationEventVerifier(RecordedEvent event) { + super(event); + } + + @Override + public void verify() throws Exception { + verifyYoungGCIs("ParallelScavenge"); + verifyOldGCIs("ParallelOld"); + verifyParallelGCThreadsIs(3); + verifyConcurrentGCThreadsIs(2); + verifyUsesDynamicGCThreadsIs(true); + verifyIsExplicitGCConcurrentIs(false); + verifyIsExplicitGCDisabledIs(false); + verifyPauseTargetIs(800); + verifyGCTimeRatioIs(19); + } + + private void verifyYoungGCIs(String expected) throws Exception { + verifyEquals("youngCollector", expected); + } + + private void verifyOldGCIs(String expected) throws Exception { + verifyEquals("oldCollector", expected); + } + + private void verifyParallelGCThreadsIs(int expected) throws Exception { + verifyEquals("parallelGCThreads", expected); + } + + private void verifyConcurrentGCThreadsIs(int expected) throws Exception { + verifyEquals("concurrentGCThreads", expected); + } + + private void verifyUsesDynamicGCThreadsIs(boolean expected) throws Exception { + verifyEquals("usesDynamicGCThreads", expected); + } + + private void verifyIsExplicitGCConcurrentIs(boolean expected) throws Exception { + verifyEquals("isExplicitGCConcurrent", expected); + } + + private void verifyIsExplicitGCDisabledIs(boolean expected) throws Exception { + verifyEquals("isExplicitGCDisabled", expected); + } + + private void verifyPauseTargetIs(long expected) throws Exception { + verifyEquals("pauseTarget", expected); + } + + private void verifyGCTimeRatioIs(int expected) throws Exception { + verifyEquals("gcTimeRatio", expected); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/configuration/TestGCConfigurationEventWithDefaultPauseTarget.java 2019-02-08 18:33:17.124730224 +0300 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013, 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.event.gc.configuration; + +import static jdk.test.lib.Asserts.assertGreaterThanOrEqual; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.EventVerifier; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps jdk.jfr.event.gc.configuration.TestGCConfigurationEventWithDefaultPauseTarget + */ +public class TestGCConfigurationEventWithDefaultPauseTarget { + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EventNames.GCConfiguration); + recording.start(); + recording.stop(); + List events = Events.fromRecording(recording); + assertGreaterThanOrEqual(events.size(), 1, "Expected at least 1 event"); + DefaultGCConfigurationVerifier verifier = new DefaultGCConfigurationVerifier(events.get(0)); + verifier.verify(); + } +} + +class DefaultGCConfigurationVerifier extends EventVerifier { + public DefaultGCConfigurationVerifier(RecordedEvent event) { + super(event); + } + + @Override + public void verify() throws Exception { + verifyEquals("pauseTarget", Long.MIN_VALUE); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWith32BitOops.java 2019-02-08 18:33:17.272725065 +0300 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013, 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.event.gc.configuration; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventVerifier; +import jdk.test.lib.jfr.GCHelper; +import sun.hotspot.WhiteBox; + +/* See the shell script wrapper for the flags used when invoking the JVM */ +public class TestGCHeapConfigurationEventWith32BitOops extends GCHeapConfigurationEventTester { + public static void main(String[] args) throws Exception { + GCHeapConfigurationEventTester t = new TestGCHeapConfigurationEventWith32BitOops(); + t.run(); + } + + @Override + protected EventVerifier createVerifier(RecordedEvent e) { + return new ThirtyTwoBitsVerifier(e); + } +} + +class ThirtyTwoBitsVerifier extends GCHeapConfigurationEventVerifier { + public ThirtyTwoBitsVerifier(RecordedEvent event) { + super(event); + } + + @Override + public void verify() throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + long heapAlignment = wb.getHeapAlignment(); + long alignedHeapSize = GCHelper.alignUp(megabytes(100), heapAlignment); + verifyMinHeapSizeIs(megabytes(100)); + verifyInitialHeapSizeIs(alignedHeapSize); + verifyMaxHeapSizeIs(alignedHeapSize); + verifyUsesCompressedOopsIs(true); + verifyObjectAlignmentInBytesIs(8); + verifyHeapAddressBitsIs(32); + verifyCompressedOopModeIs("32-bit"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWith32BitOops.sh 2019-02-08 18:33:17.412720184 +0300 @@ -0,0 +1,61 @@ +# +# Copyright (c) 2013, 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. +# +# 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. +# +# @test TestGCHeapConfigurationEventWith32BitOops +# @key jfr +# +# +# @library /lib / +# @build jdk.jfr.event.gc.configuration.TestGCHeapConfigurationEventWith32BitOops sun.hotspot.WhiteBox +# @run main ClassFileInstaller sun.hotspot.WhiteBox +# @run shell TestGCHeapConfigurationEventWith32BitOops.sh + +uses_64_bit_testjava() { + ${TESTJAVA}/bin/java ${TESTVMOPTS} -version 2>&1 | grep '64-Bit' > /dev/null +} + +uses_windows_or_linux() { + case `uname -s` in + Linux | CYGWIN* | Windows* ) + return 0 + ;; + esac + return 1 +} + +TEST='jdk.jfr.event.gc.configuration.TestGCHeapConfigurationEventWith32BitOops' + +OPTIONS='-XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+UseCompressedOops -Xmx100m -Xms100m -XX:InitialHeapSize=100m -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI' + +if [ -z "${TESTCLASSPATH}" ]; then + echo "Using TESTCLASSES" + MY_CLASSPATH=${TESTCLASSES} +else + echo "Using TESTCLASSPATH" + MY_CLASSPATH=${TESTCLASSPATH} +fi + +if uses_windows_or_linux && uses_64_bit_testjava; then + printenv + echo "${TESTJAVA}/bin/java ${TESTVMOPTS} ${OPTIONS} -cp ${MY_CLASSPATH} ${TEST}" + ${TESTJAVA}/bin/java ${TESTVMOPTS} ${OPTIONS} -cp ${MY_CLASSPATH} ${TEST} +fi --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWithHeapBasedOops.java 2019-02-08 18:33:17.556715164 +0300 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013, 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.event.gc.configuration; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventVerifier; + +/* See the shell script wrapper for the flags used when invoking the JVM */ +public class TestGCHeapConfigurationEventWithHeapBasedOops extends GCHeapConfigurationEventTester { + public static void main(String[] args) throws Exception { + GCHeapConfigurationEventTester t = new TestGCHeapConfigurationEventWithHeapBasedOops(); + t.run(); + } + + @Override + protected EventVerifier createVerifier(RecordedEvent e) { + return new HeapBasedOopsVerifier(e); + } +} + +class HeapBasedOopsVerifier extends GCHeapConfigurationEventVerifier { + public HeapBasedOopsVerifier(RecordedEvent e) { + super(e); + } + + @Override + public void verify() throws Exception { + // Can't verify min and initial heap size due to constraints on + // physical memory on tests machines + verifyMaxHeapSizeIs(gigabytes(31)); + verifyUsesCompressedOopsIs(true); + verifyObjectAlignmentInBytesIs(8); + verifyHeapAddressBitsIs(32); + verifyCompressedOopModeContains("Non-zero"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWithHeapBasedOops.sh 2019-02-08 18:33:17.704710005 +0300 @@ -0,0 +1,63 @@ +# +# Copyright (c) 2013, 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. +# +# 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. +# +# @test TestGCHeapConfigurationEventWithHeapBasedOops +# @key jfr +# +# +# @library /lib / +# @build jdk.jfr.event.gc.configuration.TestGCHeapConfigurationEventWithHeapBasedOops +# @run shell TestGCHeapConfigurationEventWithHeapBasedOops.sh + +uses_64_bit_testjava() { + ${TESTJAVA}/bin/java ${TESTVMOPTS} -version 2>&1 | grep '64-Bit' > /dev/null +} + +uses_windows_or_linux() { + case `uname -s` in + Linux | CYGWIN* | Windows* ) + return 0 + ;; + esac + return 1 +} + +TEST='jdk.jfr.event.gc.configuration.TestGCHeapConfigurationEventWithHeapBasedOops' + +# NOTE: Can't use 32g heap with UseCompressedOops. Hopefully the 31g heap will +# force HeapBased compressed oops to be enalbed by hoping that there isn't +# enough space in the lowest 1 GB of the virtual address space. +OPTIONS='-XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC -XX:+UseParallelOldGC -Xmx31g -XX:+UseCompressedOops' + +if [ -z "${TESTCLASSPATH}" ]; then + echo "Using TESTCLASSES" + MY_CLASSPATH=${TESTCLASSES} +else + echo "Using TESTCLASSPATH" + MY_CLASSPATH=${TESTCLASSPATH} +fi + +if uses_windows_or_linux && uses_64_bit_testjava; then + printenv + echo "${TESTJAVA}/bin/java ${TESTVMOPTS} ${OPTIONS} -cp ${MY_CLASSPATH} ${TEST}" + ${TESTJAVA}/bin/java ${TESTVMOPTS} ${OPTIONS} -cp ${MY_CLASSPATH} ${TEST} +fi --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWithZeroBasedOops.java 2019-02-08 18:33:17.848704985 +0300 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013, 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.event.gc.configuration; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventVerifier; + +/* See the shell script wrapper for the flags used when invoking the JVM */ +public class TestGCHeapConfigurationEventWithZeroBasedOops extends GCHeapConfigurationEventTester { + public static void main(String[] args) throws Exception { + GCHeapConfigurationEventTester t = new TestGCHeapConfigurationEventWithZeroBasedOops(); + t.run(); + } + + @Override + protected EventVerifier createVerifier(RecordedEvent e) { + return new ZeroBasedOopsVerifier(e); + } +} + +class ZeroBasedOopsVerifier extends GCHeapConfigurationEventVerifier { + public ZeroBasedOopsVerifier(RecordedEvent e) { + super(e); + } + + @Override + public void verify() throws Exception { + // Can't verify min and initial heap size due to constraints on + // physical memory on tests machines + + verifyMaxHeapSizeIs(gigabytes(4)); + verifyUsesCompressedOopsIs(true); + verifyObjectAlignmentInBytesIs(8); + verifyHeapAddressBitsIs(32); + verifyCompressedOopModeIs("Zero based"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/configuration/TestGCHeapConfigurationEventWithZeroBasedOops.sh 2019-02-08 18:33:17.992699965 +0300 @@ -0,0 +1,60 @@ +# +# Copyright (c) 2013, 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. +# +# 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. +# +# @test TestGCHeapConfigurationEventWithZeroBasedOops +# @key jfr +# +# +# @library /lib / +# @build jdk.jfr.event.gc.configuration.TestGCHeapConfigurationEventWithZeroBasedOops +# @run shell TestGCHeapConfigurationEventWithZeroBasedOops.sh + +uses_64_bit_testjava() { + ${TESTJAVA}/bin/java ${TESTVMOPTS} -version 2>&1 | grep '64-Bit' > /dev/null +} + +uses_windows_or_linux() { + case `uname -s` in + Linux | CYGWIN* | Windows* ) + return 0 + ;; + esac + return 1 +} + +TEST='jdk.jfr.event.gc.configuration.TestGCHeapConfigurationEventWithZeroBasedOops' + +OPTIONS='-XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+UseCompressedOops -Xmx4g' + +if [ -z "${TESTCLASSPATH}" ]; then + echo "Using TESTCLASSES" + MY_CLASSPATH=${TESTCLASSES} +else + echo "Using TESTCLASSPATH" + MY_CLASSPATH=${TESTCLASSPATH} +fi + +if uses_windows_or_linux && uses_64_bit_testjava; then + printenv + echo "${TESTJAVA}/bin/java ${TESTVMOPTS} ${OPTIONS} -cp ${MY_CLASSPATH} ${TEST}" + ${TESTJAVA}/bin/java ${TESTVMOPTS} ${OPTIONS} -cp ${MY_CLASSPATH} ${TEST} +fi --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/configuration/TestGCSurvivorConfigurationEvent.java 2019-02-08 18:33:18.136694945 +0300 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2013, 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.event.gc.configuration; + +import static jdk.test.lib.Asserts.assertGreaterThanOrEqual; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.EventVerifier; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:MaxTenuringThreshold=13 -XX:InitialTenuringThreshold=9 jdk.jfr.event.gc.configuration.TestGCSurvivorConfigurationEvent + */ +public class TestGCSurvivorConfigurationEvent { + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EventNames.GCSurvivorConfiguration); + recording.start(); + recording.stop(); + List events = Events.fromRecording(recording); + assertGreaterThanOrEqual(events.size(), 1, "Expected at least 1 event"); + GCSurvivorConfigurationEventVerifier verifier = new GCSurvivorConfigurationEventVerifier(events.get(0)); + verifier.verify(); + } +} + +class GCSurvivorConfigurationEventVerifier extends EventVerifier { + public GCSurvivorConfigurationEventVerifier(RecordedEvent event) { + super(event); + } + + public void verify() throws Exception { + verifyMaxTenuringThresholdIs(13); + verifyInitialTenuringThresholdIs(9); + } + + private void verifyMaxTenuringThresholdIs(int expected) throws Exception { + verifyEquals("maxTenuringThreshold", (byte)expected); + } + + private void verifyInitialTenuringThresholdIs(int expected) throws Exception { + verifyEquals("initialTenuringThreshold", (byte)expected); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/configuration/TestGCTLABConfigurationEvent.java 2019-02-08 18:33:18.280689926 +0300 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2013, 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.event.gc.configuration; + +import static jdk.test.lib.Asserts.assertGreaterThanOrEqual; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.EventVerifier; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:+UseTLAB -XX:MinTLABSize=3k -XX:TLABRefillWasteFraction=96 jdk.jfr.event.gc.configuration.TestGCTLABConfigurationEvent + */ +public class TestGCTLABConfigurationEvent { + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EventNames.GCTLABConfiguration); + recording.start(); + recording.stop(); + List events = Events.fromRecording(recording); + assertGreaterThanOrEqual(events.size(), 1, "Expected at least 1 event"); + GCTLABConfigurationEventVerifier verifier = new GCTLABConfigurationEventVerifier(events.get(0)); + verifier.verify(); + } +} + +class GCTLABConfigurationEventVerifier extends EventVerifier { + public GCTLABConfigurationEventVerifier(RecordedEvent event) { + super(event); + } + + @Override + public void verify() throws Exception { + verifyUsesTLABsIs(true); + verifyMinTLABSizeIs(kilobyte(3)); + verifyTLABRefillWasteLimitIs(96); + } + + void verifyUsesTLABsIs(boolean expected) throws Exception { + verifyEquals("usesTLABs", expected); + } + + void verifyMinTLABSizeIs(long expected) throws Exception { + verifyEquals("minTLABSize", expected); + } + + void verifyTLABRefillWasteLimitIs(long expected) throws Exception { + verifyEquals("tlabRefillWasteLimit", expected); + } + + private int kilobyte(int num) { + return 1024 * num; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/configuration/TestGCYoungGenerationConfigurationEventWithMinAndMaxSize.java 2019-02-08 18:33:18.420685046 +0300 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2013, 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.event.gc.configuration; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventVerifier; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run driver jdk.jfr.event.gc.configuration.TestGCYoungGenerationConfigurationEventWithMinAndMaxSize + */ +public class TestGCYoungGenerationConfigurationEventWithMinAndMaxSize { + public static void main(String[] args) throws Exception { + String[] jvm_args = {"-XX:+UnlockExperimentalVMOptions", + "-XX:-UseFastUnorderedTimeStamps", + "-XX:NewSize=12m", + "-cp", + System.getProperty("java.class.path"), + "-XX:MaxNewSize=16m", + "-Xms32m", + "-Xmx64m", + Tester.class.getName()}; + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(jvm_args); + OutputAnalyzer analyzer = ProcessTools.executeProcess(pb); + analyzer.shouldHaveExitValue(0); + } +} + +class Tester extends GCYoungGenerationConfigurationEventTester { + public static void main(String[] args) throws Exception { + new Tester().run(); + } + + @Override protected EventVerifier createVerifier(RecordedEvent e) { + return new MinAndMaxSizeVerifier(e); + } +} + +class MinAndMaxSizeVerifier extends EventVerifier { + public MinAndMaxSizeVerifier(RecordedEvent e) { + super(e); + } + + @Override public void verify() throws Exception { + verifyMinSizeIs(megabytes(12)); + verifyMaxSizeIs(megabytes(16)); + + // Can't test newRatio at the same time as minSize and maxSize, + // because the NewRatio flag can't be set when the flags NewSize and + // MaxNewSize are set. + } + + private void verifyMinSizeIs(long expected) throws Exception { + verifyEquals("minSize", expected); + } + + private void verifyMaxSizeIs(long expected) throws Exception { + verifyEquals("maxSize", expected); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/configuration/TestGCYoungGenerationConfigurationEventWithNewRatio.java 2019-02-08 18:33:18.564680026 +0300 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013, 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.event.gc.configuration; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventVerifier; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:NewRatio=4 jdk.jfr.event.gc.configuration.TestGCYoungGenerationConfigurationEventWithNewRatio + */ +public class TestGCYoungGenerationConfigurationEventWithNewRatio + extends GCYoungGenerationConfigurationEventTester { + public static void main(String[] args) throws Exception { + GCYoungGenerationConfigurationEventTester t = new TestGCYoungGenerationConfigurationEventWithNewRatio(); + t.run(); + } + + @Override protected EventVerifier createVerifier(RecordedEvent e) { + return new NewRatioVerifier(e); + } +} + +class NewRatioVerifier extends EventVerifier { + public NewRatioVerifier(RecordedEvent event) { + super(event); + } + + @Override public void verify() throws Exception { + verifyNewRatioIs(4); + + // Can't test minSize and maxSize at the same time as newRatio, + // because the NewSize and MaxNewSize flags can't be set when the flag + // MaxNewSize is set. + } + + private void verifyNewRatioIs(int expected) throws Exception { + verifyEquals("newRatio", expected); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/ExecuteOOMApp.java 2019-02-08 18:33:18.708675006 +0300 @@ -0,0 +1,63 @@ +/* + * 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.event.gc.detailed; + +import jdk.test.lib.jfr.AppExecutorHelper; +import jdk.test.lib.process.OutputAnalyzer; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class ExecuteOOMApp { + /** + * Executes OOM application with JFR recording and returns true if OOM error happened in the + * test thread. Adds -XX:-UseGCOverheadLimit option to avoid "java.lang.OutOfMemoryError: GC overhead limit exceeded". + * + * @param settings JFR settings file + * @param jfrFilename JFR resulting recording filename + * @param additionalVmFlags additional VM flags passed to the java + * @param bytesToAllocate number of bytes to allocate in new object every cycle in OOM application + * @return true - OOM application is finished as expected,i.e. OOM happened in the test thead + * false - OOM application is finished with OOM error which happened in the non test thread + */ + public static boolean execute(String settings, String jfrFilename, String[] additionalVmFlags, int bytesToAllocate) throws Exception { + List additionalVmFlagsList = new ArrayList<>(Arrays.asList(additionalVmFlags)); + additionalVmFlagsList.add("-XX:-UseGCOverheadLimit"); + + OutputAnalyzer out = AppExecutorHelper.executeAndRecord(settings, jfrFilename, additionalVmFlagsList.toArray(new String[0]), + OOMApp.class.getName(), String.valueOf(bytesToAllocate)); + + if ((out.getExitValue() == 1 && out.getOutput().contains("Exception: java.lang.OutOfMemoryError"))) { + return false; + } + + out.shouldHaveExitValue(0); + System.out.println(out.getOutput()); + + return true; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/OOMApp.java 2019-02-08 18:33:18.852669986 +0300 @@ -0,0 +1,68 @@ +/* + * 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.event.gc.detailed; + +import java.util.List; +import java.util.LinkedList; + +/** + * Helper class which triggers and handles out of memory error to generate + * JFR events + */ +public class OOMApp { + + public static List dummyList; + + public static void main(String[] args) { + int bytesToAllocate; + + if (args.length > 0) { + bytesToAllocate = Integer.parseInt(args[0]); + } else { + bytesToAllocate = 1024; + } + System.gc(); + dummyList = new LinkedList(); + System.out.println("## Initiate OOM ##"); + try { + while (true) { + dummyList.add(new DummyObject(bytesToAllocate)); + } + } catch (OutOfMemoryError e) { + System.out.println("## Got OOM ##"); + dummyList = null; + } + System.gc(); + } + + public static class DummyObject { + public byte[] payload; + + DummyObject(int bytesToAllocate) { + payload = new byte[bytesToAllocate]; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/PromotionEvent.java 2019-02-08 18:33:18.992665106 +0300 @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2014, 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.event.gc.detailed; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertNotEquals; +import static jdk.test.lib.Asserts.assertNotNull; +import static jdk.test.lib.Asserts.assertTrue; + +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * This is a base class for testing Promotion Events + * + * See TestPromotionEventWith* for actual test classes. Tests must set + * -XX:MaxTenuringThreshold=5 -XX:InitialTenuringThreshold=5 + * + * @author Staffan Friberg + */ +public class PromotionEvent { + + private final static String PROMOTION_IN_NEW_PLAB_NAME = EventNames.PromoteObjectInNewPLAB; + private final static String PROMOTION_OUTSIDE_PLAB_NAME = EventNames.PromoteObjectOutsidePLAB; + + // This value needs to match the command line option set above + private final static int MAX_TENURING_THRESHOLD = 5; + + // Keep track of the collection count just before and after JFR recording + private static int startGCCount = 0; + + // Dummy objects to keep things alive and assure allocation happens + public static Object dummy; + public static Object[] keepAlive = new Object[128]; + public static Object[] age = new Object[128]; + + public static void test() throws Exception { + GarbageCollectorMXBean ycBean = null; + + List gcBeans = ManagementFactory.getGarbageCollectorMXBeans(); + for (GarbageCollectorMXBean gcBean : gcBeans) { + if ("PS Scavenge".equals(gcBean.getName()) + || "G1 Young Generation".equals(gcBean.getName()) + || ("ParNew".equals(gcBean.getName()))) { + ycBean = gcBean; + } + + if (ycBean != null) { + break; + } + } + + if (ycBean == null) { + assertNotNull(ycBean, "Test failed since the MXBean for the Young Collector could not be found."); + return; // To remove IDE warning + } + + System.gc(); // Clear nusery before recording + + // Get total GC count before recording + for (GarbageCollectorMXBean gcBean : gcBeans) { + startGCCount += gcBean.getCollectionCount(); + } + + Recording recording = new Recording(); + recording.enable(PROMOTION_IN_NEW_PLAB_NAME); + recording.enable(PROMOTION_OUTSIDE_PLAB_NAME); + recording.start(); + + byte[] largeBytes = new byte[1024 * 10]; + byte[] smallBytes = new byte[64]; + + // Some large strings to keep alive for tenuring + for (int i = 0; i < keepAlive.length / 2; i++) { + ThreadLocalRandom.current().nextBytes(largeBytes); + keepAlive[i] = new String(largeBytes); + } + + // Some small strings to keep alive for tenuring + for (int i = keepAlive.length / 2; i < keepAlive.length; i++) { + ThreadLocalRandom.current().nextBytes(smallBytes); + keepAlive[i] = new String(smallBytes); + } + + // Allocate temp data to force GCs until we have promoted the live data + for (int gcCount = 0; gcCount < MAX_TENURING_THRESHOLD * 2; gcCount++) { + long currentGCCount = ycBean.getCollectionCount(); + + // some large strings to age + for (int i = 0; i < age.length / 2; i++) { + ThreadLocalRandom.current().nextBytes(largeBytes); + age[i] = new String(largeBytes); + } + + // Some small strings to age + for (int i = age.length / 2; i < age.length; i++) { + ThreadLocalRandom.current().nextBytes(smallBytes); + age[i] = new String(smallBytes); + } + + while (ycBean.getCollectionCount() <= currentGCCount + 3) { + ThreadLocalRandom.current().nextBytes(smallBytes); + dummy = new String(smallBytes); + } + } + + recording.stop(); + + List events = Events.fromRecording(recording); + + verifyPromotionSampleEvents(events); + + recording.close(); + } + + private static void verifyPromotionSampleEvents(List events) + throws Exception { + + boolean objectWasPromotedInNewPLAB = false; + boolean objectPromotedInNewPLABWasAged = false; + boolean objectPromotedInNewPLABWasTenured = false; + boolean objectWasPromotedOutsidePLAB = false; + boolean objectPromotedOutsidePLABWasAged = false; + boolean objectPromotedOutsidePLABWasTenured = false; + + Events.hasEvents(events); + + for (RecordedEvent event : events) { + // Read all common fields + Events.assertField(event, "gcId").atLeast(startGCCount).getValue(); + String className = (event.getEventType()).getName().toString(); + Events.assertField(event, "tenuringAge").atLeast(0).atMost(MAX_TENURING_THRESHOLD).getValue(); + Boolean tenured = Events.assertField(event, "tenured").getValue(); + Long objectSize = Events.assertField(event, "objectSize").above(0L).getValue(); + + // Verify Class Name + assertNotNull(className, "Class name is null. Event: " + event); + assertNotEquals(className.length(), 0, "Class name is of zero length. Event: " + event); + + // Verify PLAB size and direct allocation + if (PROMOTION_IN_NEW_PLAB_NAME.equals(event.getEventType().getName())) { + // Read event specific fields + Long plabSize = Events.assertField(event, "plabSize").above(0L).getValue(); + assertTrue(plabSize >= objectSize, "PLAB size is smaller than object size. Event: " + event); + objectWasPromotedInNewPLAB = true; + // Verify tenured is hard to do as objects might be tenured earlier than the max threshold + // but at least verify that we got the field set at least once during the test + if (tenured) { + objectPromotedInNewPLABWasTenured = true; + } else { + objectPromotedInNewPLABWasAged = true; + } + } else if (PROMOTION_OUTSIDE_PLAB_NAME.equals(event.getEventType().getName())) { + objectWasPromotedOutsidePLAB = true; + // Verify tenured is hard to do as objects might be tenured earlier than the max threshold + // but at least verify that we got the field set at least once during the test + if (tenured) { + objectPromotedOutsidePLABWasTenured = true; + } else { + objectPromotedOutsidePLABWasAged = true; + } + } else { + assertEquals(event.getEventType().getName(), "Unreachable...", "Got wrong type of event " + event); + } + + } + + // Verify that at least one event of these types occured during test + assertTrue(objectWasPromotedInNewPLAB, "No object in new plab was promoted in test"); + assertTrue(objectPromotedInNewPLABWasAged, "No object in new plab was aged in test"); + assertTrue(objectPromotedInNewPLABWasTenured, "No object in new plab was tenured in test"); + assertTrue(objectWasPromotedOutsidePLAB, "No object outside plab was promoted in test"); + assertTrue(objectPromotedOutsidePLABWasAged, "No object outside plab was aged in test"); + assertTrue(objectPromotedOutsidePLABWasTenured, "No object outside plab was tenured in test"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/PromotionFailedEvent.java 2019-02-08 18:33:19.132660227 +0300 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013, 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.event.gc.detailed; + +import java.io.File; +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +public class PromotionFailedEvent { + + private final static String EVENT_SETTINGS_FILE = System.getProperty("test.src", ".") + File.separator + "promotionfailed-testsettings.jfc"; + private final static int BYTES_TO_ALLOCATE = 1024; + + public static void test(String testName, String[] vmFlags) throws Throwable { + String jfr_file = testName + ".jfr"; + + if (!ExecuteOOMApp.execute(EVENT_SETTINGS_FILE, jfr_file, vmFlags, BYTES_TO_ALLOCATE)) { + System.out.println("OOM happened in the other thread(not test thread). Skip test."); + // Skip test, process terminates due to the OOME error in the different thread + return; + } + + // This test can not always trigger the expected event. + // Test is ok even if no events found. + List events = RecordingFile.readAllEvents(Paths.get(jfr_file)); + for (RecordedEvent event : events) { + System.out.println("Event: " + event); + long smallestSize = Events.assertField(event, "promotionFailed.smallestSize").atLeast(1L).getValue(); + long firstSize = Events.assertField(event, "promotionFailed.firstSize").atLeast(smallestSize).getValue(); + long totalSize = Events.assertField(event, "promotionFailed.totalSize").atLeast(firstSize).getValue(); + long objectCount = Events.assertField(event, "promotionFailed.objectCount").atLeast(1L).getValue(); + Asserts.assertLessThanOrEqual(smallestSize * objectCount, totalSize, "smallestSize * objectCount <= totalSize"); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/StressAllocationGCEvents.java 2019-02-08 18:33:19.268655486 +0300 @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2015, 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.event.gc.detailed; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertNotEquals; +import static jdk.test.lib.Asserts.assertTrue; + +import java.util.List; +import java.util.Random; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Semaphore; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.jfr.consumer.RecordedThread; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * Starts several threads which allocate a lot of objects that remain in young + * and old generations with defined ratio. Expects event + * vm/gc/detailed/allocation_requiring_gc recorded. + */ +public class StressAllocationGCEvents { + + private Semaphore threadsCompleted; + + public void run(String[] args) throws Exception { + threadsCompleted = new Semaphore(0); + System.out.println("Total memory= " + Runtime.getRuntime().maxMemory() + " bytes"); + + int obj_size = DEFAULT_OBJ_SIZE; + if (args.length > 0) { + obj_size = Integer.parseInt(args[0]); + } + + System.out.println("Objects size= " + obj_size + " bytes"); + ExecutorService executor + = Executors.newFixedThreadPool(THREAD_COUNT, new NamedThreadFactory()); + Recording r = new Recording(); + r.enable(EVENT_NAME_ALLOCATION_REQUIRING_GC); + r.start(); + + System.out.println("Starting " + THREAD_COUNT + " threads"); + + for (int i = 0; i < THREAD_COUNT; i++) { + executor.execute(new Runner(obj_size)); + } + + // Wait for all threads to complete + threadsCompleted.acquire(THREAD_COUNT); + executor.shutdownNow(); + + if (!executor.awaitTermination(10, TimeUnit.SECONDS)) { + System.err.println("Thread pool did not terminate after 10 seconds after shutdown"); + } + + r.stop(); + + List allocationEvents + = Events.fromRecording(r); + + System.out.println(EVENT_NAME_ALLOCATION_REQUIRING_GC + " " + allocationEvents.size()); + + Events.hasEvents(allocationEvents); + + // check stacktrace depth + for (RecordedEvent event : allocationEvents) { + checkEvent(event); + } + } + + class Runner extends Thread { + + public Runner(int obj_size) { + this.startTime = System.currentTimeMillis(); + this.OBJ_SIZE = obj_size; + this.OLD_OBJ_COUNT = Math.max(1, (int) ((float) Runtime.getRuntime().maxMemory() / 2 / THREAD_COUNT / OBJ_SIZE)); + this.old_garbage = new Object[OLD_OBJ_COUNT]; + + System.out.println(String.format("In \"%s\" old objects count = %d, recursion depth = %d", + this.getName(), OLD_OBJ_COUNT, RECURSION_DEPTH)); + } + + @Override + public void run() { + diver(RECURSION_DEPTH); + threadsCompleted.release(); + System.out.println("Completed after " + (System.currentTimeMillis() - startTime) + " ms"); + } + + private void diver(int stack) { + if (stack > 1) { + diver(stack - 1); + } else { + long endTime = startTime + (SECONDS_TO_RUN * 1000); + Random r = new Random(startTime); + while (endTime > System.currentTimeMillis()) { + byte[] garbage = new byte[OBJ_SIZE]; + if (r.nextInt(100) > OLD_GEN_RATE) { + old_garbage[r.nextInt(OLD_OBJ_COUNT)] = garbage; + } + } + } + } + + private final long startTime; + private final Object[] old_garbage; + private final int OBJ_SIZE; + private final int OLD_OBJ_COUNT; + } + + ///< check stacktrace depth + private void checkEvent(RecordedEvent event) throws Exception { + // skip check if allocation failure comes not from diver + + RecordedThread thread = event.getThread(); + String threadName = thread.getJavaName(); + + if (!threadName.contains(THREAD_NAME)) { + System.out.println("Skip event not from pool (from internals)"); + System.out.println(" Thread Id: " + thread.getJavaThreadId() + + " Thread name: " + threadName); + return; + } + + RecordedStackTrace stackTrace = event.getStackTrace(); + + List frames = stackTrace.getFrames(); + //String[] stacktrace = StackTraceHelper.buildStackTraceFromFrames(frames); + + if (!(frames.get(0).getMethod().getName().equals(DIVER_FRAME_NAME))) { + System.out.println("Skip stacktrace check for: \n" + + String.join("\n", threadName)); + return; + } + + assertTrue(frames.size() > RECURSION_DEPTH, + "Stack trace should contain at least one more entry than the ones generated by the test recursion"); + for (int i = 0; i < RECURSION_DEPTH; i++) { + assertEquals(frames.get(i).getMethod().getName(), DIVER_FRAME_NAME, + "Frame " + i + " is wrong: \n" + + String.join("\n", threadName)); + } + assertNotEquals(frames.get(RECURSION_DEPTH).getMethod().getName(), DIVER_FRAME_NAME, + "Too many diver frames: \n" + + String.join("\n", threadName)); + } + + class NamedThreadFactory implements ThreadFactory { + + private int threadNum = 0; + + @Override + public Thread newThread(Runnable r) { + return new Thread(r, THREAD_NAME + (threadNum++)); + } + } + + // Because each thread will keep some number of objects live we need to limit + // the number of threads to ensure we don't run out of heap space. The big + // allocation tests uses 256m heap and 1m allocations, so a 64 thread limit + // should be fine. + private final static int THREAD_COUNT_LIMIT = 64; + private final static int THREAD_COUNT = Math.min(1 + (int) (Runtime.getRuntime().availableProcessors() * 2), THREAD_COUNT_LIMIT); + private final static int SECONDS_TO_RUN = 60; + private final static int DEFAULT_OBJ_SIZE = 1024; + private final static int OLD_GEN_RATE = 60; // from 0 to 100 + private final static int RECURSION_DEPTH = 5; + private final static String EVENT_NAME_ALLOCATION_REQUIRING_GC = EventNames.AllocationRequiringGC; + private static final String THREAD_NAME = "JFRTest-"; + private static final String DIVER_FRAME_NAME = "StressAllocationGCEvents$Runner.diver"; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestCMSConcurrentModeFailureEvent.java 2019-02-08 18:33:19.408650606 +0300 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2013, 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.event.gc.detailed; + +import java.io.IOException; +import java.io.File; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * + * @run main jdk.jfr.event.gc.detailed.TestCMSConcurrentModeFailureEvent + */ +public class TestCMSConcurrentModeFailureEvent { + + private final static String EVENT_NAME = EventNames.ConcurrentModeFailure; + private final static String EVENT_SETTINGS_FILE = System.getProperty("test.src", ".") + File.separator + "concurrentmodefailure-testsettings.jfc"; + private final static String JFR_FILE = "TestCMSConcurrentModeFailureEvent.jfr"; + private final static int BYTES_TO_ALLOCATE = 1024 * 512; + + public static void main(String[] args) throws Exception { + String[] vmFlags = {"-Xmx128m", "-XX:MaxTenuringThreshold=0", "-Xloggc:testCMSGC.log", "-verbose:gc", + "-XX:+UseConcMarkSweepGC", "-XX:+UnlockExperimentalVMOptions", "-XX:-UseFastUnorderedTimeStamps"}; + + if (!ExecuteOOMApp.execute(EVENT_SETTINGS_FILE, JFR_FILE, vmFlags, BYTES_TO_ALLOCATE)) { + System.out.println("OOM happened in the other thread(not test thread). Skip test."); + // Skip test, process terminates due to the OOME error in the different thread + return; + } + + Optional event = RecordingFile.readAllEvents(Paths.get(JFR_FILE)).stream().findFirst(); + if (event.isPresent()) { + Asserts.assertEquals(EVENT_NAME, event.get().getEventType().getName(), "Wrong event type"); + } else { + // No event received. Check if test did trigger the event. + boolean isEventTriggered = fileContainsString("testCMSGC.log", "concurrent mode failure"); + System.out.println("isEventTriggered=" +isEventTriggered); + Asserts.assertFalse(isEventTriggered, "Event found in log, but not in JFR"); + } + } + + private static boolean fileContainsString(String filename, String text) throws IOException { + Path p = Paths.get(filename); + for (String line : Files.readAllLines(p, Charset.defaultCharset())) { + if (line.contains(text)) { + return true; + } + } + return false; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestEvacuationFailedEvent.java 2019-02-08 18:33:19.548645726 +0300 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013, 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.event.gc.detailed; + +import java.io.File; +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + * + * @run main jdk.jfr.event.gc.detailed.TestEvacuationFailedEvent + */ +public class TestEvacuationFailedEvent { + + private final static String EVENT_SETTINGS_FILE = System.getProperty("test.src", ".") + File.separator + "evacuationfailed-testsettings.jfc"; + private final static String JFR_FILE = "TestEvacuationFailedEvent.jfr"; + private final static int BYTES_TO_ALLOCATE = 1024 * 512; + + public static void main(String[] args) throws Exception { + String[] vmFlags = {"-XX:+UnlockExperimentalVMOptions", "-XX:-UseFastUnorderedTimeStamps", + "-Xmx64m", "-Xmn60m", "-XX:-UseDynamicNumberOfGCThreads", "-XX:ParallelGCThreads=3", + "-XX:MaxTenuringThreshold=0", "-verbose:gc", "-XX:+UseG1GC"}; + + if (!ExecuteOOMApp.execute(EVENT_SETTINGS_FILE, JFR_FILE, vmFlags, BYTES_TO_ALLOCATE)) { + System.out.println("OOM happened in the other thread(not test thread). Skip test."); + // Skip test, process terminates due to the OOME error in the different thread + return; + } + + List events = RecordingFile.readAllEvents(Paths.get(JFR_FILE)); + + Events.hasEvents(events); + for (RecordedEvent event : events) { + long objectCount = Events.assertField(event, "evacuationFailed.objectCount").atLeast(1L).getValue(); + long smallestSize = Events.assertField(event, "evacuationFailed.smallestSize").atLeast(1L).getValue(); + long firstSize = Events.assertField(event, "evacuationFailed.firstSize").atLeast(smallestSize).getValue(); + long totalSize = Events.assertField(event, "evacuationFailed.totalSize").atLeast(firstSize).getValue(); + Asserts.assertLessThanOrEqual(smallestSize * objectCount, totalSize, "smallestSize * objectCount <= totalSize"); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestEvacuationInfoEvent.java 2019-02-08 18:33:19.684640986 +0300 @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2013, 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.event.gc.detailed; + +import java.time.Duration; +import java.util.List; +import java.util.Optional; +import java.util.Random; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:G1HeapRegionSize=1m -Xmx64m -Xmn16m -XX:+UseG1GC jdk.jfr.event.gc.detailed.TestEvacuationInfoEvent + */ +public class TestEvacuationInfoEvent { + private final static String EVENT_INFO_NAME = EventNames.EvacuationInfo; + private final static String EVENT_FAILED_NAME = EventNames.EvacuationFailed; + + public static void main(String[] args) throws Throwable { + final long g1HeapRegionSize = 1024 * 1024; + Recording recording = new Recording(); + recording.enable(EVENT_INFO_NAME).withThreshold(Duration.ofMillis(0)); + recording.enable(EVENT_FAILED_NAME).withThreshold(Duration.ofMillis(0)); + recording.start(); + allocate(); + recording.stop(); + + List events = Events.fromRecording(recording); + Asserts.assertFalse(events.isEmpty(), "No events found"); + for (RecordedEvent event : events) { + if (!Events.isEventType(event, EVENT_INFO_NAME)) { + continue; + } + System.out.println("Event: " + event); + + int setRegions = Events.assertField(event, "cSetRegions").atLeast(0).getValue(); + long setUsedAfter = Events.assertField(event, "cSetUsedAfter").atLeast(0L).getValue(); + long setUsedBefore = Events.assertField(event, "cSetUsedBefore").atLeast(setUsedAfter).getValue(); + int allocationRegions = Events.assertField(event, "allocationRegions").atLeast(0).getValue(); + long allocRegionsUsedBefore = Events.assertField(event, "allocRegionsUsedBefore").atLeast(0L).getValue(); + long allocRegionsUsedAfter = Events.assertField(event, "allocRegionsUsedAfter").atLeast(0L).getValue(); + long bytesCopied = Events.assertField(event, "bytesCopied").atLeast(0L).getValue(); + int regionsFreed = Events.assertField(event, "regionsFreed").atLeast(0).getValue(); + + Asserts.assertEquals(allocRegionsUsedBefore + bytesCopied, allocRegionsUsedAfter, "allocRegionsUsedBefore + bytesCopied = allocRegionsUsedAfter"); + Asserts.assertGreaterThanOrEqual(setRegions, regionsFreed, "setRegions >= regionsFreed"); + Asserts.assertGreaterThanOrEqual(g1HeapRegionSize * allocationRegions, allocRegionsUsedAfter, "G1HeapRegionSize * allocationRegions >= allocationRegionsUsedAfter"); + Asserts.assertGreaterThanOrEqual(g1HeapRegionSize * setRegions, setUsedAfter, "G1HeapRegionSize * setRegions >= setUsedAfter"); + Asserts.assertGreaterThanOrEqual(g1HeapRegionSize * setRegions, setUsedBefore, "G1HeapRegionSize * setRegions >= setUsedBefore"); + Asserts.assertGreaterThanOrEqual(g1HeapRegionSize, allocRegionsUsedBefore, "G1HeapRegionSize >= allocRegionsUsedBefore"); + + int gcId = Events.assertField(event, "gcId").getValue(); + boolean isEvacuationFailed = containsEvacuationFailed(events, gcId); + if (isEvacuationFailed) { + Asserts.assertGreaterThan(setUsedAfter, 0L, "EvacuationFailure -> setUsedAfter > 0"); + Asserts.assertGreaterThan(setRegions, regionsFreed, "EvacuationFailure -> setRegions > regionsFreed"); + } else { + Asserts.assertEquals(setUsedAfter, 0L, "No EvacuationFailure -> setUsedAfter = 0"); + Asserts.assertEquals(setRegions, regionsFreed, "No EvacuationFailure -> setRegions = regionsFreed"); + } + } + } + + private static boolean containsEvacuationFailed(List events, int gcId) { + Optional failedEvent = events.stream() + .filter(e -> Events.isEventType(e, EVENT_FAILED_NAME)) + .filter(e -> gcId == (int)Events.assertField(e, "gcId").getValue()) + .findAny(); + System.out.println("Failed event: " + (failedEvent.isPresent() ? failedEvent.get() : "None")); + return failedEvent.isPresent(); + } + + public static DummyObject[] dummys = new DummyObject[6000]; + + /** + * Allocate memory to trigger garbage collections. + * We want the allocated objects to have different life time, because we want both "young" and "old" objects. + * This is done by keeping the objects in an array and step the current index by a small random number in the loop. + * The loop will continue until we have allocated a fixed number of bytes. + */ + private static void allocate() { + Random r = new Random(0); + long bytesToAllocate = 256 * 1024 * 1024; + int currPos = 0; + while (bytesToAllocate > 0) { + int allocSize = 1000 + (r.nextInt(4000)); + bytesToAllocate -= allocSize; + dummys[currPos] = new DummyObject(allocSize); + + // Skip a few positions to get different duration on the objects. + currPos = (currPos + r.nextInt(20)) % dummys.length; + } + for (int c=0; c all = Events.fromRecording(recording); + Events.hasEvents(all); + + for (RecordedEvent e : all) { + Events.assertField(e, "gcId").above(0); + } + + recording.close(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestG1ConcurrentModeFailureEvent.java 2019-02-08 18:33:19.960631366 +0300 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2013, 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.event.gc.detailed; + +import java.io.IOException; +import java.io.File; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main jdk.jfr.event.gc.detailed.TestG1ConcurrentModeFailureEvent + */ + +public class TestG1ConcurrentModeFailureEvent { + + private final static String EVENT_NAME = EventNames.ConcurrentModeFailure; + private final static String EVENT_SETTINGS_FILE = System.getProperty("test.src", ".") + File.separator + "concurrentmodefailure-testsettings.jfc"; + private final static String JFR_FILE = "TestG1ConcurrentModeFailureEvent.jfr"; + private final static int BYTES_TO_ALLOCATE = 1024 * 512; + + public static void main(String[] args) throws Exception { + String[] vmFlags = {"-Xmx512m", "-Xms512m", "-XX:MaxTenuringThreshold=0", "-Xloggc:testG1GC.log", "-verbose:gc", + "-XX:+UseG1GC", "-XX:+UnlockExperimentalVMOptions", "-XX:-UseFastUnorderedTimeStamps"}; + + if (!ExecuteOOMApp.execute(EVENT_SETTINGS_FILE, JFR_FILE, vmFlags, BYTES_TO_ALLOCATE)) { + System.out.println("OOM happened in the other thread(not test thread). Skip test."); + // Skip test, process terminates due to the OOME error in the different thread + return; + } + + Optional event = RecordingFile.readAllEvents(Paths.get(JFR_FILE)).stream().findFirst(); + if (event.isPresent()) { + Asserts.assertEquals(EVENT_NAME, event.get().getEventType().getName(), "Wrong event type"); + } else { + // No event received. Check if test did trigger the event. + boolean isEventTriggered = fileContainsString("testG1GC.log", "concurrent-mark-abort"); + System.out.println("isEventTriggered=" +isEventTriggered); + Asserts.assertFalse(isEventTriggered, "Event found in log, but not in JFR"); + } + } + + private static boolean fileContainsString(String filename, String text) throws IOException { + Path p = Paths.get(filename); + for (String line : Files.readAllLines(p, Charset.defaultCharset())) { + if (line.contains(text)) { + return true; + } + } + return false; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestG1EvacMemoryStatsEvent.java 2019-02-08 18:33:20.096626626 +0300 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2015, 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.event.gc.detailed; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:NewSize=2m -XX:MaxNewSize=2m -Xmx32m -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps jdk.jfr.event.gc.detailed.TestG1EvacMemoryStatsEvent + */ +public class TestG1EvacMemoryStatsEvent { + + public static byte[] bytes; + + public static void runTest(String event_name) throws Exception { + + Recording recording = new Recording(); + + // activate the event we are interested in and start recording + recording.enable(event_name); + recording.start(); + + // Setting NewSize and MaxNewSize will limit eden, so + // allocating 1024 5k byte arrays should trigger at + // least one Young GC. + for (int i = 0; i < 1024; i++) { + bytes = new byte[5 * 1024]; + } + recording.stop(); + + // Verify recording + List all = Events.fromRecording(recording); + for (RecordedEvent e : all) { + Events.assertField(e, "statistics.gcId").above(0); + } + + recording.close(); + } + + public static void main(String[] args) throws Exception { + runTest(EventNames.G1EvacuationYoungStatistics); + runTest(EventNames.G1EvacuationOldStatistics); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestG1HeapRegionInformationEvent.java 2019-02-08 18:33:20.232621886 +0300 @@ -0,0 +1,74 @@ +/* + * 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.event.gc.detailed; + +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.GCHelper; + +public class TestG1HeapRegionInformationEvent { + private final static String EVENT_NAME = EventNames.G1HeapRegionInformation; + public static void main(String[] args) throws Exception { + Recording recording = null; + try { + recording = new Recording(); + // activate the event we are interested in and start recording + for (EventType t : FlightRecorder.getFlightRecorder().getEventTypes()) { + System.out.println(t.getName()); + } + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + + // Verify recording + List events = Events.fromRecording(recording); + Events.hasEvents(events); + + for (RecordedEvent event : events) { + Events.assertField(event, "index").notEqual(-1); + Asserts.assertTrue(GCHelper.isValidG1HeapRegionType(Events.assertField(event, "type").getValue())); + Events.assertField(event, "used").atMost(1L*1024*1024); + } + + } catch (Throwable t) { + if (recording != null) { + recording.dump(Paths.get("TestG1HeapRegionInformationEvent.jfr")); + } + throw t; + } finally { + if (recording != null) { + recording.close(); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestG1HeapRegionTypeChangeEvent.java 2019-02-08 18:33:20.368617146 +0300 @@ -0,0 +1,89 @@ +/* + * 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.event.gc.detailed; + +import java.nio.file.Paths; +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @bug 8149650 + * + * + * @key jfr + * @library /lib / + * @run main/othervm -XX:NewSize=2m -XX:MaxNewSize=2m -Xmx32m -XX:G1HeapRegionSize=1m -XX:+UseG1GC jdk.jfr.event.gc.detailed.TestG1HeapRegionTypeChangeEvent + */ + +public class TestG1HeapRegionTypeChangeEvent { + private final static String EVENT_NAME = EventNames.G1HeapRegionTypeChange; + + public static void main(String[] args) throws Exception { + Recording recording = null; + try { + recording = new Recording(); + // activate the event we are interested in and start recording + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + recording.start(); + + // Setting NewSize and MaxNewSize will limit eden, so + // allocating 1024 20k byte arrays should trigger at + // least a few Young GCs. + byte[][] array = new byte[1024][]; + for (int i = 0; i < array.length; i++) { + array[i] = new byte[20 * 1024]; + } + recording.stop(); + + // Verify recording + List events = Events.fromRecording(recording); + Asserts.assertFalse(events.isEmpty(), "No events found"); + + for (RecordedEvent event : events) { + Events.assertField(event, "index").notEqual(-1); + Asserts.assertTrue(GCHelper.isValidG1HeapRegionType(Events.assertField(event, "from").getValue())); + Asserts.assertTrue(GCHelper.isValidG1HeapRegionType(Events.assertField(event, "to").getValue())); + Events.assertField(event, "used").atMost(1L*1024*1024); + } + } catch (Throwable t) { + if (recording != null) { + recording.dump(Paths.get("TestG1HeapRegionTypeChangeEvent.jfr")); + } + throw t; + } finally { + if (recording != null) { + recording.close(); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestG1IHOPEvent.java 2019-02-08 18:33:20.508612267 +0300 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2015, 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.event.gc.detailed; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:NewSize=2m -XX:MaxNewSize=2m -Xmx32m -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:-G1UseAdaptiveIHOP jdk.jfr.event.gc.detailed.TestG1IHOPEvent + * @run main/othervm -XX:NewSize=2m -XX:MaxNewSize=2m -Xmx32m -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+G1UseAdaptiveIHOP jdk.jfr.event.gc.detailed.TestG1IHOPEvent + */ +public class TestG1IHOPEvent { + + private final static String EVENT_NAME = EventNames.G1BasicIHOP; + public static byte[] bytes; + + public static void main(String[] args) throws Exception { + + Recording recording = new Recording(); + + // activate the event we are interested in and start recording + recording.enable(EVENT_NAME); + recording.start(); + + // Setting NewSize and MaxNewSize will limit eden, so + // allocating 1024 5k byte arrays should trigger at + // least one Young GC. + for (int i = 0; i < 1024; i++) { + bytes= new byte[5 * 1024]; + } + recording.stop(); + + // Verify recording + List all = Events.fromRecording(recording); + Events.hasEvents(all); + + for (RecordedEvent e : all) { + Events.assertField(e, "gcId").above(0); + } + + recording.close(); + + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestG1MMUEvent.java 2019-02-08 18:33:20.644607527 +0300 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015, 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.event.gc.detailed; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:NewSize=2m -XX:MaxNewSize=2m -Xmx32m -XX:+UseG1GC -XX:MaxGCPauseMillis=75 -XX:GCPauseIntervalMillis=150 -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps jdk.jfr.event.gc.detailed.TestG1MMUEvent + */ +public class TestG1MMUEvent { + + // Corresponds to command-line setting -XX:GCPauseIntervalMillis=150 + private final static double TIME_SLICE = 0.15; + + // Corresponds to command-line setting -XX:MaxGCPauseMillis=75 + private final static double MAX_GC_TIME = 0.075; + + private final static String EVENT_NAME = EventNames.G1MMU; + + public static byte[] bytes; + + public static void main(String[] args) throws Exception { + + Recording recording = new Recording(); + + // activate the event we are interested in and start recording + recording.enable(EVENT_NAME); + recording.start(); + + // Setting NewSize and MaxNewSize will limit eden, so + // allocating 1024 5k byte arrays should trigger at + // least one Young GC. + for (int i = 0; i < 1024; i++) { + bytes = new byte[5 * 1024]; + } + recording.stop(); + + // Verify recording + List all = Events.fromRecording(recording); + Events.hasEvents(all); + + for (RecordedEvent e : all) { + Events.assertField(e, "gcId").above(0); + Events.assertField(e, "timeSlice").isEqual(TIME_SLICE); + Events.assertField(e, "pauseTarget").isEqual(MAX_GC_TIME); + } + + recording.close(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestPromotionEventWithG1.java 2019-02-08 18:33:20.784602647 +0300 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, 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.event.gc.detailed; + +/** + * @test + * @key jfr + * @summary Test that events are created when an object is aged or promoted during a GC and the copying of the object requires a new PLAB or direct heap allocation + * + * + * + * & vm.opt.ExplicitGCInvokesConcurrent != true + * @library /lib / + * @run main/othervm -Xmx32m -Xms32m -Xmn12m -XX:+UseG1GC -XX:-UseStringDeduplication -XX:MaxTenuringThreshold=5 -XX:InitialTenuringThreshold=5 + * jdk.jfr.event.gc.detailed.TestPromotionEventWithG1 + */ +public class TestPromotionEventWithG1 { + + public static void main(String[] args) throws Throwable { + PromotionEvent.test(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestPromotionEventWithParallelScavenge.java 2019-02-08 18:33:20.920597907 +0300 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014, 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.event.gc.detailed; + +/** + * @test + * @key jfr + * @summary Test that events are created when an object is aged or promoted during a GC and the copying of the object requires a new PLAB or direct heap allocation + * + * + * + * @library /lib / + * @run main/othervm -Xmx32m -Xms32m -Xmn12m -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:MaxTenuringThreshold=5 -XX:InitialTenuringThreshold=5 jdk.jfr.event.gc.detailed.TestPromotionEventWithParallelScavenge + */ +public class TestPromotionEventWithParallelScavenge { + + public static void main(String[] args) throws Throwable { + PromotionEvent.test(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestPromotionFailedEventWithDefNew.java 2019-02-08 18:33:21.056593167 +0300 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013, 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.event.gc.detailed; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main jdk.jfr.event.gc.detailed.TestPromotionFailedEventWithDefNew + */ +public class TestPromotionFailedEventWithDefNew { + public static void main(String[] args) throws Throwable { + PromotionFailedEvent.test("TestPromotionFailedEventWithDefNew", new String[] {"-XX:+UnlockExperimentalVMOptions", + "-XX:-UseFastUnorderedTimeStamps", "-XX:+PrintGCDetails", "-XX:+PrintGC", "-Xmx32m", "-Xmn30m", + "-XX:-UseDynamicNumberOfGCThreads", "-XX:MaxTenuringThreshold=0", "-XX:+UseSerialGC"}); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestPromotionFailedEventWithParNew.java 2019-02-08 18:33:21.204588009 +0300 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013, 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.event.gc.detailed; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm jdk.jfr.event.gc.detailed.TestPromotionFailedEventWithParNew + */ +public class TestPromotionFailedEventWithParNew { + + public static void main(String[] args) throws Throwable { + PromotionFailedEvent.test("TestPromotionFailedEventWithParNew", + new String[]{"-Xmx32m", "-Xmn30m", "-XX:-UseDynamicNumberOfGCThreads", + "-XX:ParallelGCThreads=3", "-XX:MaxTenuringThreshold=0", + "-XX:+PrintGCDetails", "-XX:+PrintGC", "-XX:+UseConcMarkSweepGC", + "-XX:+UnlockExperimentalVMOptions", "-XX:-UseFastUnorderedTimeStamps"}); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestPromotionFailedEventWithParallelScavenge.java 2019-02-08 18:33:21.348582991 +0300 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013, 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.event.gc.detailed; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main jdk.jfr.event.gc.detailed.TestPromotionFailedEventWithParallelScavenge + */ +public class TestPromotionFailedEventWithParallelScavenge { + public static void main(String[] args) throws Throwable { + PromotionFailedEvent.test("TestPromotionFailedEventWithParallelScavenge", + new String[] {"-XX:+UnlockExperimentalVMOptions", "-XX:-UseFastUnorderedTimeStamps", + "-Xmx32m", "-Xmn30m", "-XX:-UseDynamicNumberOfGCThreads", "-XX:ParallelGCThreads=3", + "-XX:MaxTenuringThreshold=0", "-verbose:gc", "-XX:+UseParallelGC"}); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestStressAllocationGCEventsWithCMS.java 2019-02-08 18:33:21.492577972 +0300 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, 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.event.gc.detailed; + +/** + * @test + * + * + * @library /lib / + * @run main/othervm -XX:+UseConcMarkSweepGC -Xmx64m jdk.jfr.event.gc.detailed.TestStressAllocationGCEventsWithCMS + */ +public class TestStressAllocationGCEventsWithCMS { + + public static void main(String[] args) throws Exception { + new StressAllocationGCEvents().run(args); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestStressAllocationGCEventsWithDefNew.java 2019-02-08 18:33:21.636572954 +0300 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, 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.event.gc.detailed; + +/** + * @test + * + * + * @library /lib / + * @run main/othervm -XX:+UseSerialGC -Xmx64m jdk.jfr.event.gc.detailed.TestStressAllocationGCEventsWithDefNew + */ +public class TestStressAllocationGCEventsWithDefNew { + + public static void main(String[] args) throws Exception { + new StressAllocationGCEvents().run(args); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestStressAllocationGCEventsWithG1.java 2019-02-08 18:33:21.784567796 +0300 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, 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.event.gc.detailed; + +/** + * @test + * + * + * @library /lib / + * @run main/othervm -XX:+UseG1GC -Xmx64m jdk.jfr.event.gc.detailed.TestStressAllocationGCEventsWithG1 + */ +public class TestStressAllocationGCEventsWithG1 { + + public static void main(String[] args) throws Exception { + new StressAllocationGCEvents().run(args); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestStressAllocationGCEventsWithParNew.java 2019-02-08 18:33:21.928562778 +0300 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, 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.event.gc.detailed; + +/** + * @test + * + * + * @library /lib / + * @run main/othervm -XX:+UseConcMarkSweepGC -Xmx64m jdk.jfr.event.gc.detailed.TestStressAllocationGCEventsWithParNew + */ +public class TestStressAllocationGCEventsWithParNew { + + public static void main(String[] args) throws Exception { + new StressAllocationGCEvents().run(args); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestStressAllocationGCEventsWithParallel.java 2019-02-08 18:33:22.072557759 +0300 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, 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.event.gc.detailed; + +/** + * @test + * + * + * @library /lib / + * @run main/othervm -XX:+UseParallelGC -Xmx64m jdk.jfr.event.gc.detailed.TestStressAllocationGCEventsWithParallel + */ +public class TestStressAllocationGCEventsWithParallel { + + public static void main(String[] args) throws Exception { + new StressAllocationGCEvents().run(args); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestStressBigAllocationGCEventsWithCMS.java 2019-02-08 18:33:22.228552323 +0300 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, 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.event.gc.detailed; + +/** + * @test + * + * + * @library /lib / + * @run main/othervm -XX:+UseConcMarkSweepGC -Xmx256m jdk.jfr.event.gc.detailed.TestStressBigAllocationGCEventsWithCMS 1048576 + */ +public class TestStressBigAllocationGCEventsWithCMS { + + public static void main(String[] args) throws Exception { + new StressAllocationGCEvents().run(args); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestStressBigAllocationGCEventsWithDefNew.java 2019-02-08 18:33:22.396546469 +0300 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, 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.event.gc.detailed; + +/** + * @test + * + * + * @library /lib / + * @run main/othervm -XX:+UseSerialGC -Xmx256m jdk.jfr.event.gc.detailed.TestStressBigAllocationGCEventsWithDefNew 1048576 + */ +public class TestStressBigAllocationGCEventsWithDefNew { + + public static void main(String[] args) throws Exception { + new StressAllocationGCEvents().run(args); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestStressBigAllocationGCEventsWithG1.java 2019-02-08 18:33:22.544541311 +0300 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015, 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.event.gc.detailed; + +/** + * @test + * @summary Test allocates humongous objects with G1 GC. Objects + * considered humongous when it allocates equals or more than one region. As + * we're passing the size of byte array we need adjust it that entire structure + * fits exactly to one region, if not - G1 will allocate another almost empty + * region as a continue of humongous. Thus we will exhaust memory very fast and + * test will fail with OOME. + * + * + * @library /lib / + * @run main/othervm -XX:+UseG1GC -XX:MaxNewSize=5m -Xmx256m -XX:G1HeapRegionSize=1048576 jdk.jfr.event.gc.detailed.TestStressBigAllocationGCEventsWithG1 1048544 + */ +public class TestStressBigAllocationGCEventsWithG1 { + + public static void main(String[] args) throws Exception { + new StressAllocationGCEvents().run(args); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestStressBigAllocationGCEventsWithParNew.java 2019-02-08 18:33:22.700535875 +0300 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, 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.event.gc.detailed; + +/** + * @test + * + * + * @library /lib / + * @run main/othervm -XX:+UseConcMarkSweepGC -Xmx256m jdk.jfr.event.gc.detailed.TestStressBigAllocationGCEventsWithParNew 1048576 + */ +public class TestStressBigAllocationGCEventsWithParNew { + + public static void main(String[] args) throws Exception { + new StressAllocationGCEvents().run(args); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestStressBigAllocationGCEventsWithParallel.java 2019-02-08 18:33:22.852530578 +0300 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, 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.event.gc.detailed; + +/** + * @test + * + * + * @library /lib / + * @run main/othervm -XX:+UseParallelGC -Xmx256m jdk.jfr.event.gc.detailed.TestStressBigAllocationGCEventsWithParallel 1048576 + */ +public class TestStressBigAllocationGCEventsWithParallel { + + public static void main(String[] args) throws Exception { + new StressAllocationGCEvents().run(args); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/TestTenuringDistributionEvent.java 2019-02-08 18:33:22.996525560 +0300 @@ -0,0 +1,87 @@ +/* + * 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.event.gc.detailed; + +import java.nio.file.Paths; +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @bug 8009538 + * + * + * @key jfr + * @library /lib / + * @run main/othervm -XX:NewSize=2m -XX:MaxNewSize=2m -Xmx32m -XX:+UseG1GC -XX:+NeverTenure jdk.jfr.event.gc.detailed.TestTenuringDistributionEvent + */ + +public class TestTenuringDistributionEvent { + private final static String EVENT_NAME = EventNames.TenuringDistribution; + + public static void main(String[] args) throws Exception { + Recording recording = null; + try { + recording = new Recording(); + // activate the event we are interested in and start recording + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + recording.start(); + + // Setting NewSize and MaxNewSize will limit eden, so + // allocating 1024 20k byte arrays should trigger at + // least a few Young GCs. + byte[][] array = new byte[1024][]; + for (int i = 0; i < array.length; i++) { + array[i] = new byte[20 * 1024]; + } + recording.stop(); + + // Verify recording + List events = Events.fromRecording(recording); + Asserts.assertFalse(events.isEmpty(), "No events found"); + for (RecordedEvent event : events) { + Events.assertField(event, "gcId").notEqual(-1); + Events.assertField(event, "age").notEqual(0); + Events.assertField(event, "size").atLeast(0L); + } + + } catch (Throwable t) { + if (recording != null) { + recording.dump(Paths.get("TestTenuringDistribution.jfr")); + } + throw t; + } finally { + if (recording != null) { + recording.close(); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/concurrentmodefailure-testsettings.jfc 2019-02-08 18:33:23.144520403 +0300 @@ -0,0 +1,32 @@ + + + + + + true + 0 ms + + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/evacuationfailed-testsettings.jfc 2019-02-08 18:33:23.284515524 +0300 @@ -0,0 +1,32 @@ + + + + + + true + 0 ms + + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/detailed/promotionfailed-testsettings.jfc 2019-02-08 18:33:23.432510368 +0300 @@ -0,0 +1,32 @@ + + + + + + true + 0 ms + + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/heapsummary/HeapSummaryEventAllGcs.java 2019-02-08 18:33:23.576505350 +0300 @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2013, 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.event.gc.heapsummary; + +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.GCHelper; + +public class HeapSummaryEventAllGcs { + + public static void test(String expectedYoungCollector, String expectedOldCollector) throws Exception { + Recording recording = new Recording(); + recording.enable(EventNames.GCConfiguration); + recording.enable(EventNames.GCHeapSummary); + recording.enable(EventNames.PSHeapSummary); + recording.enable(EventNames.MetaspaceSummary).withThreshold(Duration.ofMillis(0)); + + recording.start(); + // To eliminate the risk of being in the middle of a GC when the recording starts/stops, + // we run 5 System.gc() and ignores the first and last GC. + GCHelper.callSystemGc(5, true); + recording.stop(); + + if (!checkCollectors(recording, expectedYoungCollector, expectedOldCollector)) { + return; + } + List events = GCHelper.removeFirstAndLastGC(Events.fromRecording(recording)); + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + } + + Asserts.assertFalse(events.isEmpty(), "Expected at least one event."); + Asserts.assertEquals(events.size() % 2, 0, "Events should come in pairs"); + + int lastHeapGcId = -1; + int lastPSGcId = -1; + int lastMetaspaceGcId = -1; + + for (RecordedEvent event : events) { + final String eventName = event.getEventType().getName(); + switch (eventName) { + case EventNames.GCHeapSummary: + lastHeapGcId = checkGcId(event, lastHeapGcId); + checkHeapEventContent(event); + break; + case EventNames.PSHeapSummary: + lastPSGcId = checkGcId(event, lastPSGcId); + checkPSEventContent(event); + break; + case EventNames.MetaspaceSummary: + lastMetaspaceGcId = checkGcId(event, lastMetaspaceGcId); + checkMetaspaceEventContent(event); + break; + default: + System.out.println("Failed event: " + event); + Asserts.fail("Unknown event type: " + eventName); + } + } + + // Sanity check. Not complete. + Asserts.assertEquals(lastHeapGcId, lastMetaspaceGcId, "Should have gotten perm gen events for all GCs"); + } + + private static void checkMetaspaceEventContent(RecordedEvent event) { + long totalUsed = Events.assertField(event, "metaspace.used").atLeast(0L).getValue(); + long totalCommitted = Events.assertField(event, "metaspace.committed").atLeast(totalUsed).getValue(); + long totalReserved = Events.assertField(event, "metaspace.reserved").atLeast(totalCommitted).getValue(); + + long dataUsed = Events.assertField(event, "dataSpace.used").atLeast(0L).getValue(); + long dataCommitted = Events.assertField(event, "dataSpace.committed").atLeast(dataUsed).getValue(); + long dataReserved = Events.assertField(event, "dataSpace.reserved").atLeast(dataCommitted).getValue(); + + long classUsed = Events.assertField(event, "classSpace.used").atLeast(0L).getValue(); + long classCommitted = Events.assertField(event, "classSpace.committed").atLeast(classUsed).getValue(); + long classReserved = Events.assertField(event, "classSpace.reserved").atLeast(classCommitted).getValue(); + + Asserts.assertEquals(dataCommitted + classCommitted, totalCommitted, "Wrong committed memory"); + Asserts.assertEquals(dataUsed + classUsed, totalUsed, "Wrong used memory"); + Asserts.assertEquals(dataReserved + classReserved, totalReserved, "Wrong reserved memory"); + } + + private static int checkGcId(RecordedEvent event, int currGcId) { + int gcId = Events.assertField(event, "gcId").getValue(); + String when = Events.assertField(event, "when").notEmpty().getValue(); + if ("Before GC".equals(when)) { + Asserts.assertGreaterThan(gcId, currGcId, "gcId should be increasing"); + } else { + Asserts.assertEquals(gcId, currGcId, "After should have same gcId as last Before event"); + } + return gcId; + } + + private static void checkHeapEventContent(RecordedEvent event) { + checkVirtualSpace(event, "heapSpace"); + long heapUsed = Events.assertField(event, "heapUsed").atLeast(0L).getValue(); + long start = Events.assertField(event, "heapSpace.start").atLeast(0L).getValue(); + long committedEnd = Events.assertField(event, "heapSpace.committedEnd").above(start).getValue(); + Asserts.assertLessThanOrEqual(heapUsed, committedEnd- start, "used can not exceed size"); + } + + private static void checkPSEventContent(RecordedEvent event) { + checkVirtualSpace(event, "oldSpace"); + checkVirtualSpace(event, "youngSpace"); + checkSpace(event, "oldObjectSpace"); + checkSpace(event, "edenSpace"); + checkSpace(event, "fromSpace"); + checkSpace(event, "toSpace"); + + checkPSYoungSizes(event); + checkPSYoungStartEnd(event); + } + + private static void checkPSYoungSizes(RecordedEvent event) { + long youngSize = (long)Events.assertField(event, "youngSpace.committedEnd").getValue() - + (long)Events.assertField(event, "youngSpace.start").getValue(); + long edenSize = (long)Events.assertField(event, "edenSpace.end").getValue() - + (long)Events.assertField(event, "edenSpace.start").getValue(); + long fromSize = (long)Events.assertField(event, "fromSpace.end").getValue() - + (long)Events.assertField(event, "fromSpace.start").getValue(); + long toSize = (long)Events.assertField(event, "toSpace.end").getValue() - + (long)Events.assertField(event, "toSpace.start").getValue(); + Asserts.assertGreaterThanOrEqual(youngSize, edenSize + fromSize + toSize, "Young sizes don't match"); + } + + private static void checkPSYoungStartEnd(RecordedEvent event) { + long oldEnd = Events.assertField(event, "oldSpace.reservedEnd").getValue(); + long youngStart = Events.assertField(event, "youngSpace.start").getValue(); + long youngEnd = Events.assertField(event, "youngSpace.committedEnd").getValue(); + long edenStart = Events.assertField(event, "edenSpace.start").getValue(); + long edenEnd = Events.assertField(event, "edenSpace.end").getValue(); + long fromStart = Events.assertField(event, "fromSpace.start").getValue(); + long fromEnd = Events.assertField(event, "fromSpace.end").getValue(); + long toStart = Events.assertField(event, "toSpace.start").getValue(); + long toEnd = Events.assertField(event, "toSpace.end").getValue(); + Asserts.assertEquals(oldEnd, youngStart, "Young should start where old ends"); + Asserts.assertEquals(youngStart, edenStart, "Eden should be placed first in young"); + if (fromStart < toStart) { + // [eden][from][to] + Asserts.assertGreaterThanOrEqual(fromStart, edenEnd, "From should start after eden"); + Asserts.assertLessThanOrEqual(fromEnd, toStart, "To should start after From"); + Asserts.assertLessThanOrEqual(toEnd, youngEnd, "To should start after From"); + } else { + // [eden][to][from] + Asserts.assertGreaterThanOrEqual(toStart, edenEnd, "From should start after eden"); + Asserts.assertLessThanOrEqual(toEnd, fromStart, "To should start after From"); + Asserts.assertLessThanOrEqual(fromEnd, youngEnd, "To should start after From"); + } + } + + private static void checkVirtualSpace(RecordedEvent event, String structName) { + long start = Events.assertField(event, structName + ".start").atLeast(0L).getValue(); + long committedEnd = Events.assertField(event, structName + ".committedEnd").above(start).getValue(); + Events.assertField(event, structName + ".reservedEnd").atLeast(committedEnd); + long committedSize = Events.assertField(event, structName + ".committedSize").atLeast(0L).getValue(); + Events.assertField(event, structName + ".reservedSize").atLeast(committedSize); + } + + private static void checkSpace(RecordedEvent event, String structName) { + long start = Events.assertField(event, structName + ".start").atLeast(0L).getValue(); + long end = Events.assertField(event, structName + ".end").above(start).getValue(); + long used = Events.assertField(event, structName + ".used").atLeast(0L).getValue(); + long size = Events.assertField(event, structName + ".size").atLeast(used).getValue(); + Asserts.assertEquals(size, end - start, "Size mismatch"); + } + + private static boolean checkCollectors(Recording recording, String expectedYoung, String expectedOld) throws Exception { + for (RecordedEvent event : Events.fromRecording(recording)) { + if (Events.isEventType(event, EventNames.GCConfiguration)) { + final String young = Events.assertField(event, "youngCollector").notEmpty().getValue(); + final String old = Events.assertField(event, "oldCollector").notEmpty().getValue(); + if (young.equals(expectedYoung) && old.equals(expectedOld)) { + return true; + } + // TODO: We treat wrong collector types as an error. Old test only warned. Not sure what is correct. + Asserts.fail(String.format("Wrong collector types: got('%s','%s'), expected('%s','%s')", + young, old, expectedYoung, expectedOld)); + } + } + Asserts.fail("Missing event type " + EventNames.GCConfiguration); + return false; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/heapsummary/TestHeapSummaryCommittedSize.java 2019-02-08 18:33:23.728500053 +0300 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2013, 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.event.gc.heapsummary; + +import java.time.Duration; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.EventVerifier; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.GCHelper; +import sun.hotspot.WhiteBox; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -XX:+UnlockExperimentalVMOptions + -XX:-UseFastUnorderedTimeStamps -Xmx16m -XX:+UseParallelGC + -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + jdk.jfr.event.gc.heapsummary.TestHeapSummaryCommittedSize + */ +public class TestHeapSummaryCommittedSize { + private final static String EVENT_NAME = EventNames.GCHeapSummary; + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + + recording.start(); + System.gc(); + recording.stop(); + + boolean isAnyFound = false; + for (RecordedEvent event : Events.fromRecording(recording)) { + System.out.println("Event: " + event); + if (!Events.isEventType(event, EVENT_NAME)) { + continue; + } + isAnyFound = true; + CommittedHeapSizeVerifier verifier = new CommittedHeapSizeVerifier(event); + verifier.verify(); + } + Asserts.assertTrue(isAnyFound, "No matching event"); + } +} + +class CommittedHeapSizeVerifier extends EventVerifier { + private final static long MAX_UNALIGNED_COMMITTED_SIZE = 16 * 1024 * 1024; + private final long MAX_ALIGNED_COMMITTED_SIZE; + + public CommittedHeapSizeVerifier(RecordedEvent event) { + super(event); + WhiteBox wb = WhiteBox.getWhiteBox(); + long heapAlignment = wb.getHeapAlignment(); + MAX_ALIGNED_COMMITTED_SIZE = GCHelper.alignUp( + MAX_UNALIGNED_COMMITTED_SIZE,heapAlignment); + } + + public void verify() throws Exception { + Events.assertField(event, "heapSpace.committedSize").atLeast(0L).atMost(MAX_ALIGNED_COMMITTED_SIZE); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/heapsummary/TestHeapSummaryEventConcurrentCMS.java 2019-02-08 18:33:23.876494896 +0300 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2013, 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.event.gc.heapsummary; + +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseConcMarkSweepGC -XX:+ExplicitGCInvokesConcurrent jdk.jfr.event.gc.heapsummary.TestHeapSummaryEventConcurrentCMS + */ +public class TestHeapSummaryEventConcurrentCMS { + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EventNames.GarbageCollection).withThreshold(Duration.ofMillis(0)); + recording.enable(EventNames.GCHeapSummary).withThreshold(Duration.ofMillis(0)); + + recording.start(); + // Need several GCs to ensure at least one heap summary event from concurrent CMS + GCHelper.callSystemGc(6, true); + recording.stop(); + + // Remove first and last GCs which can be incomplete + List events = GCHelper.removeFirstAndLastGC(Events.fromRecording(recording)); + Asserts.assertFalse(events.isEmpty(), "No events found"); + for (RecordedEvent event : events) { + System.out.println("Event: " + event); + if (!isCmsGcEvent(event)) { + continue; + } + int gcId = Events.assertField(event, "gcId").getValue(); + verifyHeapSummary(events, gcId, "Before GC"); + verifyHeapSummary(events, gcId, "After GC"); + } + } + + private static boolean isCmsGcEvent(RecordedEvent event) { + if (!Events.isEventType(event, EventNames.GarbageCollection)) { + return false; + } + final String gcName = Events.assertField(event, "name").notEmpty().getValue(); + return "ConcurrentMarkSweep".equals(gcName); + } + + private static void verifyHeapSummary(List events, int gcId, String when) { + for (RecordedEvent event : events) { + if (!Events.isEventType(event, EventNames.GCHeapSummary)) { + continue; + } + if (gcId == (int)Events.assertField(event, "gcId").getValue() && + when.equals(Events.assertField(event, "when").getValue())) { + System.out.printf("Found " + EventNames.GCHeapSummary + " for id=%d, when=%s%n", gcId, when); + return; + } + } + Asserts.fail(String.format("No " + EventNames.GCHeapSummary + " for id=%d, when=%s", gcId, when)); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/heapsummary/TestHeapSummaryEventDefNewSerial.java 2019-02-08 18:33:24.032489460 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.heapsummary; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UseSerialGC jdk.jfr.event.gc.heapsummary.TestHeapSummaryEventDefNewSerial + */ +public class TestHeapSummaryEventDefNewSerial { + public static void main(String[] args) throws Exception { + HeapSummaryEventAllGcs.test(GCHelper.gcDefNew, GCHelper.gcSerialOld); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/heapsummary/TestHeapSummaryEventG1.java 2019-02-08 18:33:24.180484304 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.heapsummary; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseG1GC jdk.jfr.event.gc.heapsummary.TestHeapSummaryEventG1 + */ +public class TestHeapSummaryEventG1 { + public static void main(String[] args) throws Exception { + HeapSummaryEventAllGcs.test(GCHelper.gcG1New, GCHelper.gcG1Old); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/heapsummary/TestHeapSummaryEventPSParOld.java 2019-02-08 18:33:24.328479147 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.heapsummary; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC jdk.jfr.event.gc.heapsummary.TestHeapSummaryEventPSParOld + */ +public class TestHeapSummaryEventPSParOld { + public static void main(String[] args) throws Exception { + HeapSummaryEventAllGcs.test(GCHelper.gcParallelScavenge, GCHelper.gcParallelOld); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/heapsummary/TestHeapSummaryEventPSSerial.java 2019-02-08 18:33:24.476473990 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.heapsummary; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:-UseParallelOldGC -XX:+UseParallelGC jdk.jfr.event.gc.heapsummary.TestHeapSummaryEventPSSerial + */ +public class TestHeapSummaryEventPSSerial { + public static void main(String[] args) throws Exception { + HeapSummaryEventAllGcs.test(GCHelper.gcParallelScavenge, GCHelper.gcSerialOld); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/heapsummary/TestHeapSummaryEventParNewCMS.java 2019-02-08 18:33:24.624468833 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.heapsummary; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseConcMarkSweepGC jdk.jfr.event.gc.heapsummary.TestHeapSummaryEventParNewCMS + */ +public class TestHeapSummaryEventParNewCMS { + public static void main(String[] args) throws Exception { + HeapSummaryEventAllGcs.test(GCHelper.gcParNew, GCHelper.gcConcurrentMarkSweep); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/objectcount/ObjectCountAfterGCEvent.java 2019-02-08 18:33:24.780463398 +0300 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2013, 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.event.gc.objectcount; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + + +public class ObjectCountAfterGCEvent { + + private static final String objectCountEventPath = EventNames.ObjectCountAfterGC; + private static final String gcEventPath = EventNames.GarbageCollection; + private static final String heapSummaryEventPath = EventNames.GCHeapSummary; + + public static void test(String gcName) throws Exception { + Recording recording = new Recording(); + recording.enable(objectCountEventPath); + recording.enable(gcEventPath); + recording.enable(heapSummaryEventPath); + + ObjectCountEventVerifier.createTestData(); + recording.start(); + System.gc(); + System.gc(); + recording.stop(); + + System.out.println("gcName=" + gcName); + for (RecordedEvent event : Events.fromRecording(recording)) { + System.out.println("Event: " + event); + } + + List events= Events.fromRecording(recording); + Optional gcEvent = events.stream() + .filter(e -> isMySystemGc(e, gcName)) + .findFirst(); + Asserts.assertTrue(gcEvent.isPresent(), "No event System.gc event of type " + gcEventPath); + System.out.println("Found System.gc event: " + gcEvent.get()); + int gcId = Events.assertField(gcEvent.get(), "gcId").getValue(); + + List objCountEvents = events.stream() + .filter(e -> Events.isEventType(e, objectCountEventPath)) + .filter(e -> isGcId(e, gcId)) + .collect(Collectors.toList()); + Asserts.assertFalse(objCountEvents.isEmpty(), "No objCountEvents for gcId=" + gcId); + + Optional heapSummaryEvent = events.stream() + .filter(e -> Events.isEventType(e, heapSummaryEventPath)) + .filter(e -> isGcId(e, gcId)) + .filter(e -> "After GC".equals(Events.assertField(e, "when").getValue())) + .findFirst(); + Asserts.assertTrue(heapSummaryEvent.isPresent(), "No heapSummary for gcId=" + gcId); + System.out.println("Found heapSummaryEvent: " + heapSummaryEvent.get()); + + Events.assertField(heapSummaryEvent.get(), "heapUsed").atLeast(0L).getValue(); + ObjectCountEventVerifier.verify(objCountEvents); + } + + private static boolean isGcId(RecordedEvent event, int gcId) { + return gcId == (int)Events.assertField(event, "gcId").getValue(); + } + + private static boolean isMySystemGc(RecordedEvent event, String gcName) { + return Events.isEventType(event, gcEventPath) && + gcName.equals(Events.assertField(event, "name").getValue()) && + "System.gc()".equals(Events.assertField(event, "cause").getValue()); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/objectcount/ObjectCountEventVerifier.java 2019-02-08 18:33:24.928458241 +0300 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2013, 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.event.gc.objectcount; + +import java.util.List; +import java.util.HashMap; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +class Foo { +} + +class Constants { + public static final int oneMB = 1048576; +} + +public class ObjectCountEventVerifier { + public static Foo[] foos; + + public static void createTestData() { + foos = new Foo[Constants.oneMB]; + } + + public static void verify(List objectCountEvents) throws Exception { + Asserts.assertFalse(objectCountEvents.isEmpty(), "Expected at least one object count event"); + // Object count events should be sent only for those classes which instances occupy over 0.5% + // of the heap. Therefore there can't be more than 200 events + Asserts.assertLessThanOrEqual(objectCountEvents.size(), 200, "Expected at most 200 object count events"); + + HashMap numInstancesOfClass = new HashMap(); + HashMap sizeOfInstances = new HashMap(); + + for (RecordedEvent event : objectCountEvents) { + String className = Events.assertField(event, "objectClass.name").notEmpty().getValue(); + long count = Events.assertField(event, "count").atLeast(0L).getValue(); + long totalSize = Events.assertField(event, "totalSize").atLeast(1L).getValue(); + System.out.println(className); + numInstancesOfClass.put(className, count); + sizeOfInstances.put(className, totalSize); + } + System.out.println(numInstancesOfClass); + final String fooArrayName = "[Ljdk/jfr/event/gc/objectcount/Foo;"; + Asserts.assertTrue(numInstancesOfClass.containsKey(fooArrayName), "Expected an event for the Foo array"); + Asserts.assertEquals(sizeOfInstances.get(fooArrayName), expectedFooArraySize(Constants.oneMB), "Wrong size of the Foo array"); + } + + private static long expectedFooArraySize(long count) { + boolean runsOn32Bit = System.getProperty("sun.arch.data.model").equals("32"); + int bytesPerWord = runsOn32Bit ? 4 : 8; + int objectHeaderSize = bytesPerWord * 3; // length will be aligned on 64 bits + int alignmentInOopArray = runsOn32Bit ? 4 : 0; + int ptrSize = bytesPerWord; + return objectHeaderSize + alignmentInOopArray + count * ptrSize; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithCMSConcurrent.java 2019-02-08 18:33:25.084452806 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.objectcount; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseConcMarkSweepGC -XX:+ExplicitGCInvokesConcurrent -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithCMSConcurrent + */ +public class TestObjectCountAfterGCEventWithCMSConcurrent { + public static void main(String[] args) throws Exception { + ObjectCountAfterGCEvent.test(GCHelper.gcConcurrentMarkSweep); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithCMSMarkSweep.java 2019-02-08 18:33:25.240447371 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.objectcount; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseConcMarkSweepGC -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithCMSMarkSweep + */ +public class TestObjectCountAfterGCEventWithCMSMarkSweep { + public static void main(String[] args) throws Exception { + ObjectCountAfterGCEvent.test(GCHelper.gcSerialOld); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithG1ConcurrentMark.java 2019-02-08 18:33:25.392442076 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.objectcount; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:+ExplicitGCInvokesConcurrent -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithG1ConcurrentMark + */ +public class TestObjectCountAfterGCEventWithG1ConcurrentMark { + public static void main(String[] args) throws Exception { + ObjectCountAfterGCEvent.test(GCHelper.gcG1Old); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithG1FullCollection.java 2019-02-08 18:33:25.544436780 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.objectcount; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseG1GC -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithG1FullCollection + */ +public class TestObjectCountAfterGCEventWithG1FullCollection { + public static void main(String[] args) throws Exception { + ObjectCountAfterGCEvent.test(GCHelper.gcG1Full); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithPSMarkSweep.java 2019-02-08 18:33:25.688431762 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.objectcount; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UseParallelGC -XX:-UseParallelOldGC -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithPSMarkSweep + */ +public class TestObjectCountAfterGCEventWithPSMarkSweep { + public static void main(String[] args) throws Exception { + ObjectCountAfterGCEvent.test(GCHelper.gcSerialOld); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithParallelOld.java 2019-02-08 18:33:25.840426467 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.objectcount; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithParallelOld + */ +public class TestObjectCountAfterGCEventWithParallelOld { + public static void main(String[] args) throws Exception { + ObjectCountAfterGCEvent.test(GCHelper.gcParallelOld); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/objectcount/TestObjectCountAfterGCEventWithSerial.java 2019-02-08 18:33:26.000420892 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.objectcount; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseSerialGC -XX:MarkSweepDeadRatio=0 -XX:-UseCompressedOops -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountAfterGCEventWithSerial + */ +public class TestObjectCountAfterGCEventWithSerial { + public static void main(String[] args) throws Exception { + ObjectCountAfterGCEvent.test(GCHelper.gcSerialOld); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/objectcount/TestObjectCountEvent.java 2019-02-08 18:33:26.140416015 +0300 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2013, 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.event.gc.objectcount; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseSerialGC -XX:-UseCompressedOops -XX:MarkSweepDeadRatio=0 -XX:+IgnoreUnrecognizedVMOptions jdk.jfr.event.gc.objectcount.TestObjectCountEvent + */ +public class TestObjectCountEvent { + private static final String objectCountEventPath = EventNames.ObjectCount; + private static final String heapSummaryEventPath = EventNames.GCHeapSummary; + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(objectCountEventPath); + recording.enable(heapSummaryEventPath); + + ObjectCountEventVerifier.createTestData(); + System.gc(); + recording.start(); + recording.stop(); + + List events = Events.fromRecording(recording); + for (RecordedEvent event : events) { + System.out.println("Event: " + event); + } + + Optional heapSummaryEvent = events.stream() + .filter(e -> Events.isEventType(e, heapSummaryEventPath)) + .filter(e -> "After GC".equals(Events.assertField(e, "when").getValue())) + .findFirst(); + Asserts.assertTrue(heapSummaryEvent.isPresent(), "No heapSummary with cause='After GC'"); + System.out.println("Found heapSummaryEvent: " + heapSummaryEvent.get()); + Events.assertField(heapSummaryEvent.get(), "heapUsed").atLeast(0L).getValue(); + int gcId = Events.assertField(heapSummaryEvent.get(), "gcId").getValue(); + + List objCountEvents = events.stream() + .filter(e -> Events.isEventType(e, objectCountEventPath)) + .filter(e -> isGcId(e, gcId)) + .collect(Collectors.toList()); + Asserts.assertFalse(objCountEvents.isEmpty(), "No objCountEvents for gcId=" + gcId); + ObjectCountEventVerifier.verify(objCountEvents); + } + + private static boolean isGcId(RecordedEvent event, int gcId) { + return gcId == (int)Events.assertField(event, "gcId").getValue(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/refstat/RefStatEvent.java 2019-02-08 18:33:26.284410998 +0300 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013, 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.event.gc.refstat; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.GCHelper; + + +public class RefStatEvent { + + private static final String gcEventPath = EventNames.GarbageCollection; + private static final String refStatsEventPath = EventNames.GCReferenceStatistics; + public static byte[] garbage; + + public static void test(String gcName) throws Exception { + Recording recording = new Recording(); + recording.enable(refStatsEventPath).withThreshold(Duration.ofMillis(0)); + recording.enable(gcEventPath).withThreshold(Duration.ofMillis(0)); + + recording.start(); + GCHelper.callSystemGc(6, true); + recording.stop(); + + System.out.println("gcName=" + gcName); + List events = GCHelper.removeFirstAndLastGC(Events.fromRecording(recording)); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event: " + event); + } + + for (RecordedEvent event : events) { + if (!Events.isEventType(event, gcEventPath)) { + continue; + } + System.out.println("Event: " + event); + final int gcId = Events.assertField(event, "gcId").getValue(); + final String name = Events.assertField(event, "name").notEmpty().getValue(); + if (gcName.equals(name)) { + verifyRefStatEvents(events, gcId); + } + } + } + + // Check that we have a refStat event for each type for this GC. + private static void verifyRefStatEvents(List events, int gcId) { + List expectedTypes = Arrays.asList("Soft reference", "Weak reference", "Final reference", "Phantom reference"); + List actualTypes = new ArrayList<>(); + try { + for (RecordedEvent event : events) { + if (!Events.isEventType(event, refStatsEventPath)) { + continue; + } + Events.assertField(event, "count").atLeast(0L); + if (Events.assertField(event, "gcId").isEqual(gcId)) { + actualTypes.add(Events.assertField(event, "type").notEmpty().getValue()); + } + } + + Asserts.assertEquals(actualTypes.size(), expectedTypes.size(), "Wrong number of refStat events"); + Asserts.assertTrue(expectedTypes.containsAll(actualTypes), "Found unknown refStat types"); + Asserts.assertTrue(actualTypes.containsAll(expectedTypes), "Missning refStat types"); + } catch (Exception e) { + System.out.println("Expected refStatTypes: " + expectedTypes.stream().collect(Collectors.joining(", "))); + System.out.println("Got refStatTypes: " + actualTypes.stream().collect(Collectors.joining(", "))); + throw e; + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/refstat/TestRefStatEventWithCMSConcurrent.java 2019-02-08 18:33:26.436405703 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.refstat; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+PrintGCDetails -XX:+PrintGC -XX:+UseConcMarkSweepGC -XX:+ExplicitGCInvokesConcurrent jdk.jfr.event.gc.refstat.TestRefStatEventWithCMSConcurrent + */ +public class TestRefStatEventWithCMSConcurrent { + public static void main(String[] args) throws Exception { + RefStatEvent.test(GCHelper.gcConcurrentMarkSweep); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/refstat/TestRefStatEventWithCMSMarkSweep.java 2019-02-08 18:33:26.588400408 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.refstat; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+PrintGCDetails -XX:+PrintGC -XX:+UseConcMarkSweepGC jdk.jfr.event.gc.refstat.TestRefStatEventWithCMSMarkSweep + */ +public class TestRefStatEventWithCMSMarkSweep { + public static void main(String[] args) throws Exception { + RefStatEvent.test(GCHelper.gcSerialOld); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/refstat/TestRefStatEventWithDefNew.java 2019-02-08 18:33:26.748394834 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.refstat; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+PrintGCDetails -XX:+PrintGC -Xmx50m -Xmn2m -XX:+UseSerialGC jdk.jfr.event.gc.refstat.TestRefStatEventWithDefNew + */ +public class TestRefStatEventWithDefNew { + public static void main(String[] args) throws Exception { + RefStatEvent.test(GCHelper.gcDefNew); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/refstat/TestRefStatEventWithG1ConcurrentMark.java 2019-02-08 18:33:26.900389538 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.refstat; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+PrintGCDetails -XX:+PrintGC -XX:+UseG1GC -XX:+ExplicitGCInvokesConcurrent jdk.jfr.event.gc.refstat.TestRefStatEventWithG1ConcurrentMark + */ +public class TestRefStatEventWithG1ConcurrentMark { + public static void main(String[] args) throws Exception { + RefStatEvent.test(GCHelper.gcG1Old); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/refstat/TestRefStatEventWithG1FullCollection.java 2019-02-08 18:33:27.048384383 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.refstat; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+PrintGCDetails -XX:+PrintGC -XX:+UseG1GC jdk.jfr.event.gc.refstat.TestRefStatEventWithG1FullCollection + */ +public class TestRefStatEventWithG1FullCollection { + public static void main(String[] args) throws Exception { + RefStatEvent.test(GCHelper.gcG1Full); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/refstat/TestRefStatEventWithG1New.java 2019-02-08 18:33:27.196379227 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.refstat; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+PrintGCDetails -XX:+PrintGC -Xmx50m -Xmn2m -XX:+UseG1GC jdk.jfr.event.gc.refstat.TestRefStatEventWithG1New + */ +public class TestRefStatEventWithG1New { + public static void main(String[] args) throws Exception { + RefStatEvent.test(GCHelper.gcG1New); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/refstat/TestRefStatEventWithPSMarkSweep.java 2019-02-08 18:33:27.340374211 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.refstat; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+PrintGCDetails -XX:+PrintGC -XX:+UseParallelGC -XX:-UseParallelOldGC jdk.jfr.event.gc.refstat.TestRefStatEventWithPSMarkSweep + */ +public class TestRefStatEventWithPSMarkSweep { + public static void main(String[] args) throws Exception { + RefStatEvent.test(GCHelper.gcSerialOld); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/refstat/TestRefStatEventWithParallelOld.java 2019-02-08 18:33:27.484369195 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.refstat; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+PrintGCDetails -XX:+PrintGC -XX:+UseParallelGC -XX:+UseParallelOldGC jdk.jfr.event.gc.refstat.TestRefStatEventWithParallelOld + */ +public class TestRefStatEventWithParallelOld { + public static void main(String[] args) throws Exception { + RefStatEvent.test(GCHelper.gcParallelOld); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/refstat/TestRefStatEventWithParallelScavenge.java 2019-02-08 18:33:27.628364178 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013, 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.event.gc.refstat; +import jdk.test.lib.jfr.GCHelper; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+PrintGCDetails -XX:+PrintGC -Xmx50m -Xmn2m -XX:-UseLargePages -XX:+UseParallelGC -XX:-UseAdaptiveSizePolicy jdk.jfr.event.gc.refstat.TestRefStatEventWithParallelScavenge + */ +public class TestRefStatEventWithParallelScavenge { + public static void main(String[] args) throws Exception { + RefStatEvent.test(GCHelper.gcParallelScavenge); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/stacktrace/AllocationStackTrace.java 2019-02-08 18:33:27.772359162 +0300 @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2015, 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.event.gc.stacktrace; + +import javax.management.MBeanServer; +import java.lang.management.ManagementFactory; +import com.sun.management.GarbageCollectorMXBean; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedMethod; +import jdk.jfr.consumer.RecordedThread; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +import java.util.List; +import java.util.ArrayList; + +import java.net.URL; +import java.net.URLClassLoader; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +abstract class MemoryAllocator { + + public static final int KB = 1024; + public static final int MB = 1024 * KB; + + public static Object garbage = null; + + abstract public void allocate(); + public void clear() { + garbage = null; + } +} + +class EdenMemoryAllocator extends MemoryAllocator { + + @Override + public void allocate() { + garbage = new byte[10 * KB]; + } +} + +class HumongousMemoryAllocator extends MemoryAllocator { + + @Override + public void allocate() { + garbage = new byte[5 * MB]; + } +} + +/** + * Attempts to fill up young gen and allocate in old gen + */ +class OldGenMemoryAllocator extends MemoryAllocator { + + private List list = new ArrayList(); + private int counter = 6000; + + @Override + public void allocate() { + if (counter-- > 0) { + list.add(new byte[10 * KB]); + } else { + list = new ArrayList(); + counter = 6000; + } + + garbage = list; + } + + @Override + public void clear(){ + list = null; + super.clear(); + } +} + +class MetaspaceMemoryAllocator extends MemoryAllocator { + + private static int counter = 0; + + /** + * Imitates class loading. Each invocation of this method causes a new class + * loader object is created and a new class is loaded by this class loader. + * Method throws OOM when run out of memory. + */ + static protected void loadNewClass() { + try { + String jarUrl = "file:" + (counter++) + ".jar"; + URL[] urls = new URL[]{new URL(jarUrl)}; + URLClassLoader cl = new URLClassLoader(urls); + Proxy.newProxyInstance( + cl, + new Class[]{Foo.class}, + new FooInvocationHandler(new FooBar())); + } catch (java.net.MalformedURLException badThing) { + // should never occur + System.err.println("Unexpected error: " + badThing); + throw new RuntimeException(badThing); + } + } + + @Override + public void allocate() { + try { + loadNewClass(); + } catch (OutOfMemoryError e) { + /* empty */ + } + } + + public static interface Foo { + } + + public static class FooBar implements Foo { + } + + static class FooInvocationHandler implements InvocationHandler { + + private final Foo foo; + + FooInvocationHandler(Foo foo) { + this.foo = foo; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return method.invoke(foo, args); + } + } +} + +/** + * Utility class to peform JFR recording, GC provocation/detection and + * stacktrace verification for related JFR events + */ +public class AllocationStackTrace { + + /** + * Tests event stacktrace for young GC if -XX:+UseSerialGC is used + */ + public static void testDefNewAllocEvent() throws Exception { + GarbageCollectorMXBean bean = garbageCollectorMXBean("Copy"); + MemoryAllocator memory = new EdenMemoryAllocator(); + + String[] expectedStack = new String[]{ + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent", + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testDefNewAllocEvent" + }; + + testAllocEvent(bean, memory, expectedStack); + } + + /** + * Tests event stacktrace for old GC if -XX:+UseSerialGC is used + */ + public static void testMarkSweepCompactAllocEvent() throws Exception { + GarbageCollectorMXBean bean = garbageCollectorMXBean("MarkSweepCompact"); + MemoryAllocator memory = new OldGenMemoryAllocator(); + + String[] expectedStack = new String[]{ + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent", + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testMarkSweepCompactAllocEvent" + }; + + testAllocEvent(bean, memory, expectedStack); + } + + /** + * Tests event stacktrace during metaspace GC threshold if -XX:+UseSerialGC + * is used + */ + public static void testMetaspaceSerialGCAllocEvent() throws Exception { + GarbageCollectorMXBean bean = garbageCollectorMXBean("MarkSweepCompact"); + MemoryAllocator memory = new MetaspaceMemoryAllocator(); + + String[] expectedStack = new String[]{ + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent", + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testMetaspaceSerialGCAllocEvent" + }; + + testAllocEvent(bean, memory, expectedStack); + } + + /** + * Tests event stacktrace for young GC if -XX:+UseConcMarkSweepGC is used + */ + public static void testParNewAllocEvent() throws Exception { + GarbageCollectorMXBean bean = garbageCollectorMXBean("ParNew"); + MemoryAllocator memory = new EdenMemoryAllocator(); + + String[] expectedStack = new String[]{ + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent", + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testParNewAllocEvent" + }; + + testAllocEvent(bean, memory, expectedStack); + } + + /** + * Tests event stacktrace for old GC if -XX:+UseConcMarkSweepGC is used + */ + public static void testConcMarkSweepAllocEvent() throws Exception { + GarbageCollectorMXBean bean = garbageCollectorMXBean("ConcurrentMarkSweep"); + MemoryAllocator memory = new OldGenMemoryAllocator(); + + String[] expectedStack = new String[]{ + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent", + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testConcMarkSweepAllocEvent" + }; + + testAllocEvent(bean, memory, expectedStack); + } + + /** + * Tests event stacktrace during metaspace GC threshold if + * -XX:+UseConcMarkSweepGC is used + */ + public static void testMetaspaceConcMarkSweepGCAllocEvent() throws Exception { + GarbageCollectorMXBean bean = garbageCollectorMXBean("ConcurrentMarkSweep"); + MemoryAllocator memory = new MetaspaceMemoryAllocator(); + + String[] expectedStack = new String[]{ + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent", + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testMetaspaceConcMarkSweepGCAllocEvent" + }; + + testAllocEvent(bean, memory, expectedStack); + } + + /** + * Tests event stacktrace for young GC if -XX:+UseParallelGC is used + */ + public static void testParallelScavengeAllocEvent() throws Exception { + GarbageCollectorMXBean bean = garbageCollectorMXBean("PS Scavenge"); + MemoryAllocator memory = new EdenMemoryAllocator(); + + String[] expectedStack = new String[]{ + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent", + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testParallelScavengeAllocEvent" + }; + + testAllocEvent(bean, memory, expectedStack); + } + + /** + * Tests event stacktrace for old GC if -XX:+UseParallelGC is used + */ + public static void testParallelMarkSweepAllocEvent() throws Exception { + GarbageCollectorMXBean bean = garbageCollectorMXBean("PS MarkSweep"); + MemoryAllocator memory = new OldGenMemoryAllocator(); + + String[] expectedStack = new String[]{ + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent", + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testParallelMarkSweepAllocEvent" + }; + + testAllocEvent(bean, memory, expectedStack); + } + + /** + * Tests event stacktrace during metaspace GC threshold if + * -XX:+UseParallelGC is used + */ + public static void testMetaspaceParallelGCAllocEvent() throws Exception { + GarbageCollectorMXBean bean = garbageCollectorMXBean("PS MarkSweep"); + MemoryAllocator memory = new MetaspaceMemoryAllocator(); + + String[] expectedStack = new String[]{ + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent", + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testMetaspaceParallelGCAllocEvent" + }; + + testAllocEvent(bean, memory, expectedStack); + } + + /** + * Tests event stacktrace for young GC if -XX:+UseG1GC is used + */ + public static void testG1YoungAllocEvent() throws Exception { + GarbageCollectorMXBean bean = garbageCollectorMXBean("G1 Young Generation"); + MemoryAllocator memory = new EdenMemoryAllocator(); + + String[] expectedStack = new String[]{ + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent", + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testG1YoungAllocEvent" + }; + + testAllocEvent(bean, memory, expectedStack); + } + + /** + * Tests event stacktrace for old GC if -XX:+UseG1GC is used + */ + public static void testG1OldAllocEvent() throws Exception { + GarbageCollectorMXBean bean = garbageCollectorMXBean("G1 Old Generation"); + MemoryAllocator memory = new OldGenMemoryAllocator(); + + String[] expectedStack = new String[]{ + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent", + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testG1OldAllocEvent" + }; + + testAllocEvent(bean, memory, expectedStack); + } + + /** + * Tests event stacktrace during metaspace GC threshold if -XX:+UseG1GC is + * used + */ + public static void testMetaspaceG1GCAllocEvent() throws Exception { + GarbageCollectorMXBean bean = garbageCollectorMXBean("G1 Young Generation"); + MemoryAllocator memory = new MetaspaceMemoryAllocator(); + + String[] expectedStack = new String[]{ + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent", + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testMetaspaceG1GCAllocEvent" + }; + + testAllocEvent(bean, memory, expectedStack); + } + + /** + * Tests event stacktrace for GC caused by humongous allocations if + * -XX:+UseG1GC is used + */ + public static void testG1HumonAllocEvent() throws Exception { + // G1 tries to reclaim humongous objects at every young collection + // after doing a conservative estimate of its liveness + GarbageCollectorMXBean bean = garbageCollectorMXBean("G1 Young Generation"); + MemoryAllocator memory = new HumongousMemoryAllocator(); + + String[] expectedStack = new String[]{ + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testAllocEvent", + "jdk.jfr.event.gc.stacktrace.AllocationStackTrace.testG1HumonAllocEvent" + }; + + testAllocEvent(bean, memory, expectedStack); + } + + private static GarbageCollectorMXBean garbageCollectorMXBean(String name) throws Exception { + MBeanServer server = ManagementFactory.getPlatformMBeanServer(); + GarbageCollectorMXBean bean = ManagementFactory.newPlatformMXBeanProxy( + server, "java.lang:type=GarbageCollector,name=" + name, GarbageCollectorMXBean.class); + return bean; + } + + /** + * Performs JFR recording, GC provocation/detection and stacktrace + * verification for JFR event. In case of verification failure + * repeats several times. + * + * @param bean MX bean for desired GC + * @param memory allocator for desired type of allocations + * @param expectedStack array of expected frames + */ + private static void testAllocEvent(GarbageCollectorMXBean bean, MemoryAllocator memory, String[] expectedStack) throws Exception { + // The test checks the stacktrace of events and expects all the events are fired + // in the current thread. But compilation may also trigger GC. + // So to filter out such cases the test performs several iterations and expects + // that the memory allocations made by the test will produce the desired JFR event. + final int iterations = 5; + for (int i = 0; i < iterations; i++) { + if (allocAndCheck(bean, memory, expectedStack)) { + return; + } else { + System.out.println("Attempt: " + i + " out of " + iterations+": no matching stack trace found."); + } + memory.clear(); + } + throw new AssertionError("No matching stack trace found"); + } + + /** + * Performs JFR recording, GC provocation/detection and stacktrace + * verification for JFR event. + * + * @param bean MX bean for desired GC + * @param memory allocator for desired type of allocations + * @param expectedStack array of expected frames + * @throws Exception + */ + private static boolean allocAndCheck(GarbageCollectorMXBean bean, MemoryAllocator memory, + String[] expectedStack) throws Exception { + String threadName = Thread.currentThread().getName(); + String event = EventNames.AllocationRequiringGC; + + Recording r = new Recording(); + r.enable(event).withStackTrace(); + r.start(); + + long prevCollectionCount = bean.getCollectionCount(); + long collectionCount = -1; + + long iterationCount = 0; + + do { + memory.allocate(); + collectionCount = bean.getCollectionCount(); + iterationCount++; + } while (collectionCount == prevCollectionCount); + + System.out.println("Allocation num: " + iterationCount); + System.out.println("GC detected: " + collectionCount); + + r.stop(); + List events = Events.fromRecording(r); + + System.out.println("JFR GC events found: " + events.size()); + + // Find any event that matched the expected stack trace + for (RecordedEvent e : events) { + System.out.println("Event: " + e); + RecordedThread thread = e.getThread(); + String eventThreadName = thread.getJavaName(); + if (!threadName.equals(eventThreadName)) { + continue; + } + if (matchingStackTrace(e.getStackTrace(), expectedStack)) { + return true; + } + } + return false; + } + + private static boolean matchingStackTrace(RecordedStackTrace stack, String[] expectedStack) { + if (stack == null) { + return false; + } + + List frames = stack.getFrames(); + int pos = findFramePos(frames, expectedStack[0]); + + if (pos == -1) { + return false; + } + + for (String expectedFrame : expectedStack) { + RecordedFrame f = frames.get(pos++); + String frame = frameToString(f); + + if (!frame.equals(expectedFrame)) { + return false; + } + } + + return true; + } + + private static int findFramePos(List frames, String frame) { + int pos = 0; + + for (RecordedFrame f : frames) { + if (frame.equals(frameToString(f))) { + return pos; + } + pos++; + } + + return -1; + } + + private static String frameToString(RecordedFrame f) { + RecordedMethod m = f.getMethod(); + String methodName = m.getName(); + String className = m.getType().getName(); + return className + "." + methodName; + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/stacktrace/TEST.properties 2019-02-08 18:33:27.920354007 +0300 @@ -0,0 +1,2 @@ +modules = jdk.management + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/stacktrace/TestConcMarkSweepAllocationPendingStackTrace.java 2019-02-08 18:33:28.068348851 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, 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.event.gc.stacktrace; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main/othervm -XX:MaxNewSize=10M -Xmx64M -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGC jdk.jfr.event.gc.stacktrace.TestConcMarkSweepAllocationPendingStackTrace + */ +public class TestConcMarkSweepAllocationPendingStackTrace { + + public static void main(String[] args) throws Exception { + AllocationStackTrace.testConcMarkSweepAllocEvent(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/stacktrace/TestDefNewAllocationPendingStackTrace.java 2019-02-08 18:33:28.216343696 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, 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.event.gc.stacktrace; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main/othervm -XX:+UseSerialGC -XX:+PrintGCDetails -XX:+PrintGC jdk.jfr.event.gc.stacktrace.TestDefNewAllocationPendingStackTrace + */ +public class TestDefNewAllocationPendingStackTrace { + + public static void main(String[] args) throws Exception { + AllocationStackTrace.testDefNewAllocEvent(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/stacktrace/TestG1HumongousAllocationPendingStackTrace.java 2019-02-08 18:33:28.360338680 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, 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.event.gc.stacktrace; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main/othervm -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGC -Xmx64M -XX:InitiatingHeapOccupancyPercent=100 jdk.jfr.event.gc.stacktrace.TestG1HumongousAllocationPendingStackTrace + */ +public class TestG1HumongousAllocationPendingStackTrace { + + public static void main(String[] args) throws Exception { + AllocationStackTrace.testG1HumonAllocEvent(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/stacktrace/TestG1OldAllocationPendingStackTrace.java 2019-02-08 18:33:28.508333525 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, 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.event.gc.stacktrace; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main/othervm -XX:MaxNewSize=10M -Xmx128M -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGC jdk.jfr.event.gc.stacktrace.TestG1OldAllocationPendingStackTrace + */ +public class TestG1OldAllocationPendingStackTrace { + + public static void main(String[] args) throws Exception { + AllocationStackTrace.testG1OldAllocEvent(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/stacktrace/TestG1YoungAllocationPendingStackTrace.java 2019-02-08 18:33:28.656328370 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, 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.event.gc.stacktrace; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main/othervm -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGC jdk.jfr.event.gc.stacktrace.TestG1YoungAllocationPendingStackTrace + */ +public class TestG1YoungAllocationPendingStackTrace { + + public static void main(String[] args) throws Exception { + AllocationStackTrace.testG1YoungAllocEvent(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/stacktrace/TestMarkSweepCompactAllocationPendingStackTrace.java 2019-02-08 18:33:28.800323354 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, 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.event.gc.stacktrace; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main/othervm -XX:MaxNewSize=10M -Xmx64M -XX:+UseSerialGC -XX:+PrintGCDetails -XX:+PrintGC jdk.jfr.event.gc.stacktrace.TestMarkSweepCompactAllocationPendingStackTrace + */ +public class TestMarkSweepCompactAllocationPendingStackTrace { + + public static void main(String[] args) throws Exception { + AllocationStackTrace.testMarkSweepCompactAllocEvent(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/stacktrace/TestMetaspaceConcMarkSweepGCAllocationPendingStackTrace.java 2019-02-08 18:33:28.948318199 +0300 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015, 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.event.gc.stacktrace; + +/** + * @test + * @key jfr + * + * + * + * + * @library /lib / + * @run main/othervm -XX:+UseConcMarkSweepGC -XX:MaxMetaspaceSize=64M -XX:+PrintGCDetails -XX:+PrintGC jdk.jfr.event.gc.stacktrace.TestMetaspaceConcMarkSweepGCAllocationPendingStackTrace + */ +public class TestMetaspaceConcMarkSweepGCAllocationPendingStackTrace { + + public static void main(String[] args) throws Exception { + AllocationStackTrace.testMetaspaceConcMarkSweepGCAllocEvent(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/stacktrace/TestMetaspaceG1GCAllocationPendingStackTrace.java 2019-02-08 18:33:29.088313322 +0300 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015, 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.event.gc.stacktrace; +/** + * @test + * @key jfr + * + * + * + * + * @library /lib / + * @run main/othervm -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGC -XX:MaxMetaspaceSize=64M jdk.jfr.event.gc.stacktrace.TestMetaspaceG1GCAllocationPendingStackTrace + */ + +public class TestMetaspaceG1GCAllocationPendingStackTrace { + + public static void main(String[] args) throws Exception { + AllocationStackTrace.testMetaspaceG1GCAllocEvent(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/stacktrace/TestMetaspaceParallelGCAllocationPendingStackTrace.java 2019-02-08 18:33:29.232308307 +0300 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015, 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.event.gc.stacktrace; + +/** + * @test + * @key jfr + * + * + * + * + * @library /lib / + * @run main/othervm -XX:+UseParallelGC -XX:+PrintGCDetails -XX:+PrintGC -XX:MaxMetaspaceSize=64M jdk.jfr.event.gc.stacktrace.TestMetaspaceParallelGCAllocationPendingStackTrace + */ +public class TestMetaspaceParallelGCAllocationPendingStackTrace { + + public static void main(String[] args) throws Exception { + AllocationStackTrace.testMetaspaceParallelGCAllocEvent(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/stacktrace/TestMetaspaceSerialGCAllocationPendingStackTrace.java 2019-02-08 18:33:29.376303291 +0300 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015, 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.event.gc.stacktrace; + +/** + * @test + * @key jfr + * + * + * + * + * @library /lib / + * @run main/othervm -XX:+UseSerialGC -XX:+PrintGCDetails -XX:+PrintGC -XX:MaxMetaspaceSize=64M -XX:+FlightRecorder jdk.jfr.event.gc.stacktrace.TestMetaspaceSerialGCAllocationPendingStackTrace + */ +public class TestMetaspaceSerialGCAllocationPendingStackTrace { + + public static void main(String[] args) throws Exception { + AllocationStackTrace.testMetaspaceSerialGCAllocEvent(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/stacktrace/TestParNewAllocationPendingStackTrace.java 2019-02-08 18:33:29.516298415 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, 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.event.gc.stacktrace; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main/othervm -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGC -XX:+FlightRecorder jdk.jfr.event.gc.stacktrace.TestParNewAllocationPendingStackTrace + */ +public class TestParNewAllocationPendingStackTrace { + + public static void main(String[] args) throws Exception { + AllocationStackTrace.testParNewAllocEvent(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/stacktrace/TestParallelMarkSweepAllocationPendingStackTrace.java 2019-02-08 18:33:29.656293539 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, 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.event.gc.stacktrace; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main/othervm -XX:MaxNewSize=10M -Xmx64M -XX:+UseParallelGC -XX:+PrintGCDetails -XX:+PrintGC -XX:+FlightRecorder jdk.jfr.event.gc.stacktrace.TestParallelMarkSweepAllocationPendingStackTrace + */ +public class TestParallelMarkSweepAllocationPendingStackTrace { + + public static void main(String[] args) throws Exception { + AllocationStackTrace.testParallelMarkSweepAllocEvent(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/gc/stacktrace/TestParallelScavengeAllocationPendingStackTrace.java 2019-02-08 18:33:29.804288384 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, 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.event.gc.stacktrace; + +/** + * @test + * @key jfr + * + * + * + * @library /lib / + * @run main/othervm -XX:+UseParallelGC -XX:+PrintGCDetails -XX:+PrintGC -XX:+FlightRecorder jdk.jfr.event.gc.stacktrace.TestParallelScavengeAllocationPendingStackTrace + */ +public class TestParallelScavengeAllocationPendingStackTrace { + + public static void main(String[] args) throws Exception { + AllocationStackTrace.testParallelScavengeAllocEvent(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/io/EvilInstrument.java 2019-02-08 18:33:29.948283368 +0300 @@ -0,0 +1,158 @@ +/* + * Copyright (c) 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.event.io; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; +import java.net.ServerSocket; +import java.net.Socket; +import java.security.ProtectionDomain; +import java.util.concurrent.CountDownLatch; + + +/** + * @test + * @key jfr + * @summary This test runs JFR with a javaagent that reads/writes files and + * sockets during every class definition. This is to verify that the i/o + * instrumentation in JFR does not interfere with javaagents. + * + * + * @library /lib / + * + * + * @run shell MakeJAR.sh EvilInstrument 'Can-Redefine-Classes: true' + * @run main/othervm -javaagent:EvilInstrument.jar jdk.jfr.event.io.EvilInstrument + */ + +public class EvilInstrument { + + CountDownLatch socketEchoReady = new CountDownLatch(1); + ServerSocket ss; + + /** + * Thread that echos everything from a socket. + */ + class SocketEcho extends Thread + { + public SocketEcho() { + setDaemon(true); + } + + public void run() { + try { + Socket s = ss.accept(); + OutputStream os = s.getOutputStream(); + InputStream is = s.getInputStream(); + socketEchoReady.countDown(); + for(;;) { + int b = is.read(); + os.write(b); + } + } catch(Exception ex) { + ex.printStackTrace(); + System.exit(1); + } + } + } + + public static File createScratchFile() throws IOException { + return File.createTempFile("EvilTransformer", null, new File(".")).getAbsoluteFile(); + } + + class EvilTransformer implements ClassFileTransformer { + File scratch; + Socket s; + volatile boolean inited = false; + + public EvilTransformer() throws Exception { + scratch = createScratchFile(); + ss = new ServerSocket(0); + new SocketEcho().start(); + s = new Socket(ss.getInetAddress(), ss.getLocalPort()); + socketEchoReady.await(); + inited = true; + } + + public byte[] transform(ClassLoader loader, String className, + Class classBeingRedefined, + ProtectionDomain protectionDomain, + byte[] classfileBuffer) + { + if (!inited) { + return null; + } + // Do i/o operations during every transform call. + try { + FileOutputStream fos = new FileOutputStream(scratch); + fos.write(31); + fos.close(); + + FileInputStream fis = new FileInputStream(scratch); + fis.read(); + fis.close(); + + RandomAccessFile raf = new RandomAccessFile(scratch, "rw"); + raf.read(); + raf.write(31); + raf.close(); + + s.getOutputStream().write(31); + s.getInputStream().read(); + + } catch(Exception ex) { + ex.printStackTrace(); + System.exit(1); + } + return null; + } + } + + public static void premain(String agentArgs, Instrumentation inst) { + new EvilInstrument().addTransformer(inst); + } + + private void addTransformer(Instrumentation inst) { + try { + inst.addTransformer(new EvilTransformer()); + } catch(Exception ex) { + ex.printStackTrace(); + System.exit(1); + } + } + + public static void main(String... args) throws Exception { + System.out.println("Hello"); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/io/IOEvent.java 2019-02-08 18:33:30.088278493 +0300 @@ -0,0 +1,210 @@ +/* + * Copyright (c) 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.event.io; + +import java.io.File; +import java.io.IOException; +import java.net.Socket; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; +import jdk.test.lib.jfr.EventField; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +// Contains data from a JFR IO event. +public class IOEvent { + public final String thread; + public final EventType eventType; + public final long size; + public final String address; // Name of file or socket address. + public final boolean endOfStream; + + // Constructor is private. Use IOEvent.create... + private IOEvent(String thread, EventType eventType, long size, String address, boolean endOfStream) { + this.thread = thread; + this.eventType = eventType; + this.size = size; + this.address = address; + this.endOfStream = endOfStream; + } + + @Override + public String toString() { + String key = String.format("thread: %s, type: %s, size: %d, address: %s endOfStream: %s", thread, eventType, size, address, endOfStream); + return key; + } + + @Override + public boolean equals(Object object) { + if (object == null || !(object instanceof IOEvent)) { + return false; + } + return toString().equals(object.toString()); + } + + @Override + public int hashCode() { + return toString().hashCode(); + } + + public static final String EVENT_UNKNOWN = "unknown-event??"; + public static final String EVENT_FILE_FORCE = EventNames.FileForce; + public static final String EVENT_FILE_READ = EventNames.FileRead; + public static final String EVENT_FILE_WRITE = EventNames.FileWrite; + public static final String EVENT_SOCKET_READ = EventNames.SocketRead; + public static final String EVENT_SOCKET_WRITE = EventNames.SocketWrite; + + public enum EventType { UnknownEvent, FileForce, FileRead, FileWrite, SocketRead, SocketWrite } + + private static final String[] eventPaths = { + EVENT_UNKNOWN, EVENT_FILE_FORCE, EVENT_FILE_READ, EVENT_FILE_WRITE, EVENT_SOCKET_READ, EVENT_SOCKET_WRITE + }; + + public static boolean isWriteEvent(EventType eventType) { + return (eventType == EventType.SocketWrite || eventType == EventType.FileWrite); + } + + public static boolean isReadEvent(EventType eventType) { + return (eventType == EventType.SocketRead || eventType == EventType.FileRead); + } + + public static boolean isFileEvent(EventType eventType) { + return (eventType == EventType.FileForce || eventType == EventType.FileWrite || eventType == EventType.FileRead); + } + + public static IOEvent createSocketWriteEvent(long size, Socket s) { + if (size < 0) { + size = 0; + } + return new IOEvent(Thread.currentThread().getName(), EventType.SocketWrite, size, getAddress(s), false); + } + + public static IOEvent createSocketReadEvent(long size, Socket s) { + boolean endOfStream = false; + if (size < 0) { + size = 0; + endOfStream = true; + } + return new IOEvent(Thread.currentThread().getName(), EventType.SocketRead, size, getAddress(s), endOfStream); + } + + public static IOEvent createFileForceEvent(File file) { + String address = null; + try { + address = file.getCanonicalPath(); + } catch(IOException ex) { + throw new RuntimeException(); + } + return new IOEvent(Thread.currentThread().getName(), EventType.FileForce, 0, address, false); + } + + public static IOEvent createFileReadEvent(long size, File file) { + boolean endOfStream = false; + if (size < 0) { + endOfStream = true; + size = 0; + } + String address = null; + try { + address = file.getCanonicalPath(); + } catch(IOException ex) { + throw new RuntimeException(); + } + return new IOEvent(Thread.currentThread().getName(), EventType.FileRead, size, address, endOfStream); + } + + public static IOEvent createFileWriteEvent(long size, File file) { + if (size < 0) { + size = 0; + } + String address = null; + try { + address = file.getCanonicalPath(); + } catch(IOException ex) { + throw new RuntimeException(); + } + return new IOEvent(Thread.currentThread().getName(), EventType.FileWrite, size, address, false); + } + + public static EventType getEventType(RecordedEvent event) { + final String path = event.getEventType().getName(); + for (int i = 0; i < eventPaths.length; ++i) { + if (path.endsWith(eventPaths[i])) { + return EventType.values()[i]; + } + } + return EventType.UnknownEvent; + } + + public static IOEvent createTestEvent(RecordedEvent event) { + EventType eventType = getEventType(event); + if (eventType == EventType.UnknownEvent) { + return null; + } + EventField ev = Events.assertField(event, "eventThread"); + RecordedThread t = ev.getValue(); + String thread = t.getJavaName(); + long size = 0L; + if (isWriteEvent(eventType)) { + size = Events.assertField(event, "bytesWritten").getValue(); + } else if (isReadEvent(eventType)) { + size = Events.assertField(event, "bytesRead").getValue(); + } + String address = getEventAddress(event); + boolean endOfStream = false; + if (event.hasField("endOfStream")) { + endOfStream = event.getValue("endOfStream"); + } + if (event.hasField("endOfFile")) { + endOfStream = event.getValue("endOfFile"); + } + return new IOEvent(thread, eventType, size, address, endOfStream); + } + + public static String getEventAddress(RecordedEvent event) { + if (isFileEvent(getEventType(event))) { + String address = Events.assertField(event, "path").getValue(); + // must ensure canonical format + String canonical_path = null; + try { + canonical_path = new File(address).getCanonicalPath(); + } catch (IOException ex) { + throw new RuntimeException(); + } + return canonical_path; + } else { + return String.format("%s/%s:%d", + event.getValue("host"), + event.getValue("address"), + event.getValue("port")); + } + } + + private static String getAddress(Socket s) { + return s.getInetAddress().toString() + ":" + s.getPort(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/io/IOHelper.java 2019-02-08 18:33:30.248272920 +0300 @@ -0,0 +1,129 @@ +/* + * Copyright (c) 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.event.io; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertTrue; + +import java.util.List; +import java.util.stream.Collectors; + +import jdk.jfr.event.io.IOEvent.EventType; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; + + +// Helper class to match actual RecordedEvents to expected events. +public class IOHelper { + + public static void verifyEqualsInOrder(List events, List expectedEvents) throws Throwable { + List actualEvents = getTestEvents(events, expectedEvents); + try { + assertEquals(actualEvents.size(), expectedEvents.size(), "Wrong number of events."); + for (int i = 0; i < actualEvents.size(); ++i) { + assertEquals(actualEvents.get(i), expectedEvents.get(i), "Wrong event at pos " + i); + } + } catch (Throwable t) { + logEvents(actualEvents, expectedEvents); + throw t; + } + } + + + public static void verifyEquals(List events, List expectedEvents) throws Throwable { + List actualEvents = getTestEvents(events, expectedEvents); + try { + assertEquals(actualEvents.size(), expectedEvents.size(), "Wrong number of events"); + assertTrue(actualEvents.containsAll(expectedEvents), "Not all expected events received"); + assertTrue(expectedEvents.containsAll(actualEvents), "Received unexpected events"); + } catch (Throwable t) { + logEvents(actualEvents, expectedEvents); + throw t; + } + } + + + private static List getTestEvents(List events, List expectedEvents) throws Throwable { + // Log all events + for (RecordedEvent event : events) { + String msg = event.getEventType().getName(); + boolean isSocket = IOEvent.EVENT_SOCKET_READ.equals(msg) || IOEvent.EVENT_SOCKET_WRITE.equals(msg); + boolean isFile = IOEvent.EVENT_FILE_FORCE.equals(msg) || IOEvent.EVENT_FILE_READ.equals(msg) || IOEvent.EVENT_FILE_WRITE.equals(msg); + boolean isFileReadOrWrite = IOEvent.EVENT_FILE_READ.equals(msg) || IOEvent.EVENT_FILE_WRITE.equals(msg); + boolean isRead = IOEvent.EVENT_FILE_READ.equals(msg) || IOEvent.EVENT_SOCKET_READ.equals(msg); + if (isFile) { + msg += " : " + Events.assertField(event, "path").getValue(); + } else if (isSocket) { + msg += " - " + Events.assertField(event, "host").getValue(); + msg += "." + Events.assertField(event, "address").getValue(); + msg += "." + Events.assertField(event, "port").getValue(); + } + if (isSocket || isFileReadOrWrite) { + String field = isRead ? "bytesRead" : "bytesWritten"; + msg += " : " + Events.assertField(event, field).getValue(); + } + System.out.println(msg); + } + + return events.stream() + .filter(event -> isTestEvent(event, expectedEvents)) + .map(event -> IOEvent.createTestEvent(event)) + .collect(Collectors.toList()); + } + + // A recording may contain extra events that are not part of the test. + // This function filters out events that not belong to the test. + public static boolean isTestEvent(RecordedEvent event, List testEvents) { + EventType eventType = IOEvent.getEventType(event); + if (eventType == EventType.UnknownEvent) { + return false; + } + + // Only care about threads in the expected threads. + final String threadName = event.getThread().getJavaName(); + if (testEvents.stream().noneMatch(te -> te.thread.equals(threadName))) { + return false; + } + + // Only care about files and sockets in expected events. + final String address = IOEvent.getEventAddress(event); + if (testEvents.stream().noneMatch(te -> te.address.equals(address))) { + return false; + } + return true; + } + + private static void logEvents(List actualEvents, List expectedEvents) { + for (int i = 0; i < actualEvents.size(); ++i) { + System.out.println("actual event[" + i + "] = " + actualEvents.get(i)); + } + for (int i = 0; i < expectedEvents.size(); ++i) { + System.out.println("expected event[" + i + "] = " + expectedEvents.get(i)); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/io/InstrumentationCallback.java 2019-02-08 18:33:30.396267766 +0300 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 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.event.io; +import java.util.Set; +import java.util.HashSet; + +// Will be used by the TestInstrumentation.java as a callback from +// instrumented classes. +public class InstrumentationCallback { + + private static Set keys = new HashSet(); + + public static synchronized void callback(String key) { + if (!keys.contains(key)) { + keys.add(key); + } + } + + public static synchronized void clear() { + keys.clear(); + } + + public static synchronized Set getKeysCopy() { + return new HashSet(keys); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/io/MakeJAR.sh 2019-02-08 18:33:30.548262472 +0300 @@ -0,0 +1,45 @@ +#!/bin/sh + +AGENT="$1" + +if [ "${TESTSRC}" = "" ] +then + echo "TESTSRC not set. Test cannot execute. Failed." + exit 1 +fi +echo "TESTSRC=${TESTSRC}" + +if [ "${TESTJAVA}" = "" ] +then + echo "TESTJAVA not set. Test cannot execute. Failed." + exit 1 +fi +echo "TESTJAVA=${TESTJAVA}" + +if [ "${TESTCLASSES}" = "" ] +then + echo "TESTCLASSES not set. Test cannot execute. Failed." + exit 1 +fi + +if [ -z "${COMPILEJAVA}" ] +then + COMPILEJAVA=${TESTJAVA} +fi + +JAVAC="${COMPILEJAVA}/bin/javac -g" +JAR="${COMPILEJAVA}/bin/jar" + +cp ${TESTSRC}/${AGENT}.java . +${JAVAC} -cp ${TESTCLASSPATH} ${AGENT}.java + +echo "Manifest-Version: 1.0" > ${AGENT}.mf +echo Premain-Class: jdk.jfr.event.io.${AGENT} >> ${AGENT}.mf +shift +while [ $# != 0 ] ; do + echo $1 >> ${AGENT}.mf + shift +done + + +${JAR} cvfm ${AGENT}.jar ${AGENT}.mf ${AGENT}*.class --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/io/TestDisabledEvents.java 2019-02-08 18:33:30.688257596 +0300 @@ -0,0 +1,134 @@ +/* + * Copyright (c) 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.event.io; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertNotEquals; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Test with FlightRecorder enabled but with the events disabled. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.io.TestDisabledEvents + */ + +// Verify that IO operations are correct and that no events are generated. +public class TestDisabledEvents { + + private static final int writeInt = 'A'; + private static final byte[] writeBuf = { 'B', 'C', 'D' }; + + public static void main(String[] args) throws Throwable { + File tmp = File.createTempFile("TestDisabledEvents", ".tmp", new File(".")); + tmp.deleteOnExit(); + Recording recording = new Recording(); + recording.disable(IOEvent.EVENT_FILE_READ); + recording.disable(IOEvent.EVENT_FILE_WRITE); + recording.start(); + + useRandomAccessFile(tmp); + useFileStreams(tmp); + useFileChannel(tmp); + + recording.stop(); + for (RecordedEvent event : Events.fromRecording(recording)) { + final String eventName = event.getEventType().getName(); + System.out.println("Got eventName:" + eventName); + assertNotEquals(eventName, IOEvent.EVENT_FILE_READ, "Got disabled read event"); + assertNotEquals(eventName, IOEvent.EVENT_FILE_WRITE, "Got disabled write event"); + } + } + + private static void useRandomAccessFile(File tmp) throws Throwable { + tmp.delete(); + try (RandomAccessFile ras = new RandomAccessFile(tmp, "rw")) { + ras.write(writeInt); + ras.write(writeBuf); + ras.seek(0); + int readInt = ras.read(); + assertEquals(readInt, writeInt, "Wrong readInt"); + byte[] readBuf = new byte[writeBuf.length]; + int readSize = ras.read(readBuf); + assertEquals(readSize, writeBuf.length, "Wrong readSize"); + // Try to read more which should generate EOF. + readInt = ras.read(); + assertEquals(readInt, -1, "Wrong readInt after EOF"); + } + } + + private static void useFileStreams(File tmp) throws Throwable { + tmp.delete(); + try (FileOutputStream fos = new FileOutputStream(tmp)) { + fos.write(writeInt); + fos.write(writeBuf); + } + + try (FileInputStream fis = new FileInputStream(tmp)) { + int readInt = fis.read(); + assertEquals(readInt, writeInt, "Wrong readInt"); + byte[] readBuf = new byte[writeBuf.length]; + int readSize = fis.read(readBuf); + assertEquals(readSize, writeBuf.length, "Wrong readSize"); + // Try to read more which should generate EOF. + readInt = fis.read(); + assertEquals(readInt, -1, "Wrong readInt after EOF"); + } + } + + private static void useFileChannel(File tmp) throws Throwable { + tmp.delete(); + try (RandomAccessFile rf = new RandomAccessFile(tmp, "rw"); + FileChannel ch = rf.getChannel()) { + final String bufContent = "0123456789"; + final int bufSize = bufContent.length(); + ByteBuffer writeBuf = ByteBuffer.allocateDirect(bufSize); + writeBuf.put(bufContent.getBytes()); + + writeBuf.flip(); + int writeSize = ch.write(writeBuf); + assertEquals(writeSize, bufSize, "Wrong writeSize for FileChannel"); + + ch.position(0); + ByteBuffer readBuf = ByteBuffer.allocateDirect(bufSize); + int readSize = ch.read(readBuf); + assertEquals(readSize, bufSize, "Wrong readSize full for FileChannel"); + assertEquals(0, writeBuf.compareTo(readBuf), "Unexpected readBuf content"); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/io/TestFileChannelEvents.java 2019-02-08 18:33:30.828252720 +0300 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 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.event.io; + +import static jdk.test.lib.Asserts.assertEquals; + +import java.io.File; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.io.TestFileChannelEvents + */ +public class TestFileChannelEvents { + public static void main(String[] args) throws Throwable { + File tmp = File.createTempFile("TestFileChannelEvents", ".tmp", new File(".")); + tmp.deleteOnExit(); + Recording recording = new Recording(); + List expectedEvents = new ArrayList<>(); + + try (RandomAccessFile rf = new RandomAccessFile(tmp, "rw"); FileChannel ch = rf.getChannel();) { + recording.enable(IOEvent.EVENT_FILE_FORCE).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); + recording.start(); + + ByteBuffer bufA = ByteBuffer.allocateDirect(10); + ByteBuffer bufB = ByteBuffer.allocateDirect(20); + bufA.put("1234567890".getBytes()); + bufB.put("1234567890".getBytes()); + + // test write(ByteBuffer) + bufA.flip(); + long size = ch.write(bufA); + expectedEvents.add(IOEvent.createFileWriteEvent(size, tmp)); + + // test write(ByteBuffer, long) + bufA.flip(); + size = ch.write(bufA, bufA.capacity() / 2); + expectedEvents.add(IOEvent.createFileWriteEvent(size, tmp)); + + // test write(ByteBuffer[]) + bufA.flip(); + bufA.limit(5); + bufB.flip(); + bufB.limit(5); + size = ch.write(new ByteBuffer[] { bufA, bufB }); + expectedEvents.add(IOEvent.createFileWriteEvent(size, tmp)); + + // test force(boolean) + ch.force(true); + expectedEvents.add(IOEvent.createFileForceEvent(tmp)); + + // reset file + ch.position(0); + + // test read(ByteBuffer) + bufA.clear(); + size = ch.read(bufA); + expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); + + // test read(ByteBuffer, long) + bufA.clear(); + size = ch.read(bufA, bufA.capacity() / 2); + expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); + + // test read(ByteBuffer[]) + bufA.clear(); + bufA.limit(5); + bufB.clear(); + bufB.limit(5); + size = ch.read(new ByteBuffer[] { bufA, bufB }); + expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); + + // Read at EOF. Should get size -1 in event. + ch.position(ch.size()); + bufA.clear(); + size = ch.read(bufA); + assertEquals(size, -1L, "Expected size -1 when read at EOF"); + expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); + + ch.close(); + recording.stop(); + List events = Events.fromRecording(recording); + IOHelper.verifyEqualsInOrder(events, expectedEvents); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/io/TestFileReadOnly.java 2019-02-08 18:33:30.968247845 +0300 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 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.event.io; + +import static jdk.test.lib.Asserts.fail; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.io.TestFileReadOnly + */ +public class TestFileReadOnly { + + public static void main(String[] args) throws Throwable { + File tmp = File.createTempFile("TestFileReadOnly", ".tmp", new File(".")); + tmp.deleteOnExit(); + Recording recording = new Recording(); + List expectedEvents = new ArrayList<>(); + + recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); + recording.start(); + + final byte[] buf = { 1, 2, 3 }; + + // Create the file. + try (RandomAccessFile f = new RandomAccessFile(tmp, "rw")) { + f.write(buf); + expectedEvents.add(IOEvent.createFileWriteEvent(buf.length, tmp)); + } + + // Reopen the file as ReadOnly and try to write to it. + // Should generate an event with bytesWritten = -1. + try (RandomAccessFile f = new RandomAccessFile(tmp, "r")) { + try { + f.write(buf); + fail("No exception for ReadOnly File"); + } catch (IOException e) { + // Expected exception + expectedEvents.add(IOEvent.createFileWriteEvent(-1, tmp)); + } + } + + // Try to write to read-only FileChannel. + try (RandomAccessFile f = new RandomAccessFile(tmp, "r"); FileChannel ch = f.getChannel()) { + ByteBuffer writeBuf = ByteBuffer.allocateDirect(buf.length); + writeBuf.put(buf); + writeBuf.flip(); + ch.position(0); + try { + ch.write(writeBuf); + fail("No exception for ReadOnly FileChannel"); + } catch (java.nio.channels.NonWritableChannelException e) { + // Expected exception + expectedEvents.add(IOEvent.createFileWriteEvent(-1, tmp)); + } + } + + recording.stop(); + List events = Events.fromRecording(recording); + IOHelper.verifyEqualsInOrder(events, expectedEvents); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/io/TestFileStreamEvents.java 2019-02-08 18:33:31.112242830 +0300 @@ -0,0 +1,96 @@ +/* + * Copyright (c) 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.event.io; + +import static jdk.test.lib.Asserts.assertEquals; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; + +/** + * @test TestFileStreamEvents + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.io.TestFileStreamEvents + */ + +public class TestFileStreamEvents { + public static void main(String[] args) throws Throwable { + File tmp = File.createTempFile("TestFileStreamEvents", ".tmp", new File(".")); + tmp.deleteOnExit(); + Recording recording = new Recording(); + List expectedEvents = new ArrayList<>(); + + try(FileOutputStream fos = new FileOutputStream(tmp); FileInputStream fis = new FileInputStream(tmp);) { + recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); + recording.start(); + + int writeByte = 47; + byte[] writeBuf = {11, 12, 13, 14}; + + // Write + fos.write(writeByte); + expectedEvents.add(IOEvent.createFileWriteEvent(1, tmp)); + fos.write(writeBuf); + expectedEvents.add(IOEvent.createFileWriteEvent(writeBuf.length, tmp)); + fos.write(writeBuf, 0, 2); + expectedEvents.add(IOEvent.createFileWriteEvent(2, tmp)); + + // Read + int readByte = fis.read(); + assertEquals(readByte, writeByte, "Wrong byte read"); + expectedEvents.add(IOEvent.createFileReadEvent(1, tmp)); + + byte[] readBuf = new byte[writeBuf.length]; + long size = fis.read(readBuf); + assertEquals(size, (long)writeBuf.length, "Wrong size when reading byte[]"); + expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); + + size = fis.read(readBuf, 0, 2); + assertEquals(size, 2L, "Wrong size when reading 2 bytes"); + expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); + + // We are at EOF. Read more and verify we get size -1. + size = fis.read(readBuf); + assertEquals(size, -1L, "Size should be -1 at EOF"); + expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); + + recording.stop(); + List events = Events.fromRecording(recording); + IOHelper.verifyEqualsInOrder(events, expectedEvents); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/io/TestInstrumentation.java 2019-02-08 18:33:31.256237815 +0300 @@ -0,0 +1,373 @@ +/* + * Copyright (c) 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.event.io; + +import java.util.Arrays; +import java.util.Set; +import java.util.HashSet; +import java.io.File; +import java.security.ProtectionDomain; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.Instrumentation; +import java.lang.instrument.IllegalClassFormatException; + +import jdk.internal.org.objectweb.asm.ClassReader; +import jdk.internal.org.objectweb.asm.ClassVisitor; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.Opcodes; +import jdk.internal.org.objectweb.asm.Type; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +/* + * @test + * @summary Test that will instrument the same classes that JFR will also instrument. + * @key jfr + * + * + * @library /lib / + * + + * + * @run main/othervm jdk.jfr.event.io.TestInstrumentation + */ + +// Test that will instrument the same classes that JFR will also instrument. +// +// The methods that will be instrumented, for example java.io.RandomAccessFile.write, +// will add the following code at the start of the method: +// InstrumentationCallback.callback("::"); +// +// The class InstrumentationCallback will log all keys added by the callback() function. +// +// With this instrumentation in place, we will run some existing jfr.io tests +// to verify that our instrumentation has not broken the JFR instrumentation. +// +// After the tests have been run, we verify that the callback() function have been +// called from all instrumented classes and methods. This will verify that JFR has not +// broken our instrumentation. +// +// To use instrumentation, the test must be run in a new java process with +// the -javaagent option. +// We must also create two jars: +// TestInstrumentation.jar: The javaagent for the instrumentation. +// InstrumentationCallback.jar: This is a separate jar with the instrumentation +// callback() function. It is in a separate jar because it must be added to +// the bootclasspath to be called from java.io classes. +// +// The test contains 3 parts: +// Setup part that will create jars and launch the new test instance. +// Agent part that contains the instrumentation code. +// The actual test part is in the TestMain class. +// +public class TestInstrumentation implements ClassFileTransformer { + + private static Instrumentation instrumentation = null; + private static TestInstrumentation testTransformer = null; + + // All methods that will be instrumented. + private static final String[] instrMethodKeys = { + "java/io/RandomAccessFile::seek::(J)V", + "java/io/RandomAccessFile::read::()I", + "java/io/RandomAccessFile::read::([B)I", + "java/io/RandomAccessFile::write::([B)V", + "java/io/RandomAccessFile::write::(I)V", + "java/io/RandomAccessFile::close::()V", + "java/io/FileInputStream::read::([BII)I", + "java/io/FileInputStream::read::([B)I", + "java/io/FileInputStream::read::()I", + "java/io/FileOutputStream::write::(I)V", + "java/io/FileOutputStream::write::([B)V", + "java/io/FileOutputStream::write::([BII)V", + "java/net/SocketInputStream::read::()I", + "java/net/SocketInputStream::read::([B)I", + "java/net/SocketInputStream::read::([BII)I", + "java/net/SocketInputStream::close::()V", + "java/net/SocketOutputStream::write::(I)V", + "java/net/SocketOutputStream::write::([B)V", + "java/net/SocketOutputStream::write::([BII)V", + "java/net/SocketOutputStream::close::()V", + "java/nio/channels/FileChannel::read::([Ljava/nio/ByteBuffer;)J", + "java/nio/channels/FileChannel::write::([Ljava/nio/ByteBuffer;)J", + "java/nio/channels/SocketChannel::open::()Ljava/nio/channels/SocketChannel;", + "java/nio/channels/SocketChannel::open::(Ljava/net/SocketAddress;)Ljava/nio/channels/SocketChannel;", + "java/nio/channels/SocketChannel::read::([Ljava/nio/ByteBuffer;)J", + "java/nio/channels/SocketChannel::write::([Ljava/nio/ByteBuffer;)J", + "sun/nio/ch/FileChannelImpl::read::(Ljava/nio/ByteBuffer;)I", + "sun/nio/ch/FileChannelImpl::write::(Ljava/nio/ByteBuffer;)I", + }; + + private static String getInstrMethodKey(String className, String methodName, String signature) { + // This key is used to identify a class and method. It is sent to callback(key) + return className + "::" + methodName + "::" + signature; + } + + private static String getClassFromMethodKey(String methodKey) { + return methodKey.split("::")[0]; + } + + // Set of all classes targeted for instrumentation. + private static Set instrClassesTarget = null; + + // Set of all classes where instrumentation has been completed. + private static Set instrClassesDone = null; + + static { + // Split class names from InstrMethodKeys. + instrClassesTarget = new HashSet(); + instrClassesDone = new HashSet(); + for (String s : instrMethodKeys) { + String className = getClassFromMethodKey(s); + instrClassesTarget.add(className); + } + } + + private static void log(String msg) { + System.out.println("TestTransformation: " + msg); + } + + + //////////////////////////////////////////////////////////////////// + // This is the actual test part. + // A batch of jfr io tests will be run twice with a + // retransfromClasses() in between. After each test batch we verify + // that all callbacks have been called. + //////////////////////////////////////////////////////////////////// + + public static class TestMain { + + private enum TransformStatus { Transformed, Retransformed, Removed } + + public static void main(String[] args) throws Throwable { + runAllTests(TransformStatus.Transformed); + + // Retransform all classes and then repeat tests + Set> classes = new HashSet>(); + for (String className : instrClassesTarget) { + Class clazz = Class.forName(className.replaceAll("/", ".")); + classes.add(clazz); + log("Will retransform " + clazz.getName()); + } + instrumentation.retransformClasses(classes.toArray(new Class[0])); + + // Clear all callback keys so we don't read keys from the previous test run. + InstrumentationCallback.clear(); + runAllTests(TransformStatus.Retransformed); + + // Remove my test transformer and run tests again. Should not get any callbacks. + instrumentation.removeTransformer(testTransformer); + instrumentation.retransformClasses(classes.toArray(new Class[0])); + InstrumentationCallback.clear(); + runAllTests(TransformStatus.Removed); + } + + // This is not all available jfr io tests, but a reasonable selection. + public static void runAllTests(TransformStatus status) throws Throwable { + log("runAllTests, TransformStatus: " + status); + try { + String[] noArgs = new String[0]; + TestRandomAccessFileEvents.main(noArgs); + TestSocketEvents.main(noArgs); + TestSocketChannelEvents.main(noArgs); + TestFileChannelEvents.main(noArgs); + TestFileStreamEvents.main(noArgs); + TestDisabledEvents.main(noArgs); + + // Verify that all expected callbacks have been called. + Set callbackKeys = InstrumentationCallback.getKeysCopy(); + for (String key : instrMethodKeys) { + boolean gotCallback = callbackKeys.contains(key); + boolean expectsCallback = isClassInstrumented(status, key); + String msg = String.format("key:%s, expects:%b", key, expectsCallback); + if (gotCallback != expectsCallback) { + throw new Exception("Wrong callback() for " + msg); + } else { + log("Correct callback() for " + msg); + } + } + } catch (Throwable t) { + log("Test failed in phase " + status); + t.printStackTrace(); + throw t; + } + } + + private static boolean isClassInstrumented(TransformStatus status, String key) throws Throwable { + switch (status) { + case Retransformed: + return true; + case Removed: + return false; + case Transformed: + String className = getClassFromMethodKey(key); + return instrClassesDone.contains(className); + } + throw new Exception("Test error: Unknown TransformStatus: " + status); + } + } + + + //////////////////////////////////////////////////////////////////// + // This is the setup part. It will create needed jars and + // launch a new java instance that will run the internal class TestMain. + // This setup step is needed because we must use a javaagent jar to + // transform classes. + //////////////////////////////////////////////////////////////////// + + public static void main(String[] args) throws Throwable { + buildJar("TestInstrumentation", true); + buildJar("InstrumentationCallback", false); + launchTest(); + } + + private static void buildJar(String jarName, boolean withManifest) throws Throwable { + final String slash = File.separator; + final String packageName = "jdk/jfr/event/io".replace("/", slash); + System.out.println("buildJar packageName: " + packageName); + + String testClasses = System.getProperty("test.classes", "?"); + String testSrc = System.getProperty("test.src", "?"); + String jarPath = testClasses + slash + jarName + ".jar"; + String manifestPath = testSrc + slash + jarName + ".mf"; + String className = packageName + slash + jarName + ".class"; + + String[] args = null; + if (withManifest) { + args = new String[] {"-cfm", jarPath, manifestPath, "-C", testClasses, className}; + } else { + args = new String[] {"-cf", jarPath, "-C", testClasses, className}; + } + + log("Running jar " + Arrays.toString(args)); + sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar"); + if (!jarTool.run(args)) { + throw new Exception("jar failed: args=" + Arrays.toString(args)); + } + } + + // Launch the test instance. Will run the internal class TestMain. + private static void launchTest() throws Throwable { + final String slash = File.separator; + + // Need to add jdk/lib/tools.jar to classpath. + String classpath = + System.getProperty("test.class.path", "") + File.pathSeparator + + System.getProperty("test.jdk", ".") + slash + "lib" + slash + "tools.jar"; + String testClassDir = System.getProperty("test.classes", "") + slash; + + String[] args = { + "-Xbootclasspath/a:" + testClassDir + "InstrumentationCallback.jar", + /* "--add-exports", "java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED",*/ + "-classpath", classpath, + "-javaagent:" + testClassDir + "TestInstrumentation.jar", + "jdk.jfr.event.io.TestInstrumentation$TestMain" }; + OutputAnalyzer output = ProcessTools.executeTestJvm(args); + output.shouldHaveExitValue(0); + } + + + //////////////////////////////////////////////////////////////////// + // This is the java agent part. Used to transform classes. + // + // Each transformed method will add this call: + // InstrumentationCallback.callback("::"); + //////////////////////////////////////////////////////////////////// + + public static void premain(String args, Instrumentation inst) throws Exception { + instrumentation = inst; + testTransformer = new TestInstrumentation(); + inst.addTransformer(testTransformer, true); + } + + public byte[] transform( + ClassLoader classLoader, String className, Class classBeingRedefined, + ProtectionDomain pd, byte[] bytes) throws IllegalClassFormatException { + // Check if this class should be instrumented. + if (!instrClassesTarget.contains(className)) { + return null; + } + + boolean isRedefinition = classBeingRedefined != null; + log("instrument class(" + className + ") " + (isRedefinition ? "redef" : "load")); + + ClassReader reader = new ClassReader(bytes); + ClassWriter writer = new ClassWriter( + reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + CallbackClassVisitor classVisitor = new CallbackClassVisitor(writer); + reader.accept(classVisitor, 0); + instrClassesDone.add(className); + return writer.toByteArray(); + } + + private static class CallbackClassVisitor extends ClassVisitor { + private String className; + + public CallbackClassVisitor(ClassVisitor cv) { + super(Opcodes.ASM5, cv); + } + + @Override + public void visit( + int version, int access, String name, String signature, + String superName, String[] interfaces) { + cv.visit(version, access, name, signature, superName, interfaces); + className = name; + } + + @Override + public MethodVisitor visitMethod( + int access, String methodName, String desc, String signature, String[] exceptions) { + String methodKey = getInstrMethodKey(className, methodName, desc); + boolean isInstrumentedMethod = Arrays.asList(instrMethodKeys).contains(methodKey); + MethodVisitor mv = cv.visitMethod(access, methodName, desc, signature, exceptions); + if (isInstrumentedMethod && mv != null) { + mv = new CallbackMethodVisitor(mv, methodKey); + log("instrumented " + methodKey); + } + return mv; + } + } + + public static class CallbackMethodVisitor extends MethodVisitor { + private String logMessage; + + public CallbackMethodVisitor(MethodVisitor mv, String logMessage) { + super(Opcodes.ASM5, mv); + this.logMessage = logMessage; + } + + @Override + public void visitCode() { + mv.visitCode(); + String methodDescr = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(String.class)); + String className = InstrumentationCallback.class.getName().replace('.', '/'); + mv.visitLdcInsn(logMessage); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, className, "callback", methodDescr); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/io/TestInstrumentation.mf 2019-02-08 18:33:31.416232243 +0300 @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Premain-Class: jdk.jfr.event.io.TestInstrumentation +Can-Redefine-Classes: true +Can-Retransform-Classes: true --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/io/TestRandomAccessFileEvents.java 2019-02-08 18:33:31.560227228 +0300 @@ -0,0 +1,116 @@ +/* + * Copyright (c) 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.event.io; + +import static jdk.test.lib.Asserts.assertEquals; + +import java.io.File; +import java.io.RandomAccessFile; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.io.TestRandomAccessFileEvents + */ +public class TestRandomAccessFileEvents { + + public static void main(String[] args) throws Throwable { + File tmp = File.createTempFile("TestRandomAccessFileEvents", ".tmp", new File(".")); + tmp.deleteOnExit(); + Recording recording = new Recording(); + List expectedEvents = new ArrayList<>(); + + recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); + recording.start(); + + RandomAccessFile ras = new RandomAccessFile(tmp, "rw"); + int writeInt = 47; + byte[] writeBuffer = {10,11,12,13}; + + // Write an int and a buffer. + ras.write(writeInt); + expectedEvents.add(IOEvent.createFileWriteEvent(1, tmp)); + ras.write(writeBuffer); + expectedEvents.add(IOEvent.createFileWriteEvent(writeBuffer.length, tmp)); + + ras.seek(0); + + // Read int and buffer + int readInt = ras.read(); + assertEquals(readInt, writeInt, "wrong int read"); + expectedEvents.add(IOEvent.createFileReadEvent(1, tmp)); + byte[] readBuffer = new byte [writeBuffer.length]; + int size = ras.read(readBuffer); + verifyBufferEquals(readBuffer, writeBuffer); + expectedEvents.add(IOEvent.createFileReadEvent(readBuffer.length, tmp)); + + // Read beyond EOF + readInt = ras.read(); + assertEquals(-1, readInt, "wrong read after EOF"); + expectedEvents.add(IOEvent.createFileReadEvent(-1, tmp)); + + // Seek to beginning and verify we can read after EOF. + ras.seek(0); + readInt = ras.read(); + assertEquals(readInt, writeInt, "wrong int read after seek(0)"); + expectedEvents.add(IOEvent.createFileReadEvent(1, tmp)); + + // seek beyond EOF and verify we get EOF when reading. + ras.seek(10); + readInt = ras.read(); + assertEquals(-1, readInt, "wrong read after seek beyond EOF"); + expectedEvents.add(IOEvent.createFileReadEvent(-1, tmp)); + + // Read partial buffer. + int partialSize = writeBuffer.length - 2; + ras.seek(ras.length()-partialSize); + size = ras.read(readBuffer); + assertEquals(size, partialSize, "Wrong size partial buffer read"); + expectedEvents.add(IOEvent.createFileReadEvent(size, tmp)); + + ras.close(); + recording.stop(); + List events = Events.fromRecording(recording); + IOHelper.verifyEqualsInOrder(events, expectedEvents); + } + + private static void verifyBufferEquals(byte[] a, byte[] b) { + assertEquals(a.length, b.length, "Wrong buffer size"); + for (int i = 0; i < a.length; ++i) { + assertEquals(a[i], b[i], "Wrong buffer content at pos " + i); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/io/TestRandomAccessFileThread.java 2019-02-08 18:33:31.704222213 +0300 @@ -0,0 +1,226 @@ +/* + * Copyright (c) 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.event.io; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.thread.TestThread; +import jdk.test.lib.thread.XRun; + + +/** + * @test + * @summary Verify the event time stamp and thread name + * @key jfr + * + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps jdk.jfr.event.io.TestRandomAccessFileThread + */ + +// TODO: This test should work without -XX:-UseFastUnorderedTimeStamps + +// The test uses 2 threads to read and write to a file. +// The number of bytes in each read/write operation is increased by 1. +// By looking at the number of bytes in each event, we know in what order +// the events should arrive. This is used to verify the event time stamps. +public class TestRandomAccessFileThread { + private static final int OP_COUNT = 100; // Total number of read/write operations. + private static volatile int writeCount = 0; // Number of writes executed. + + public static void main(String[] args) throws Throwable { + File tmp = File.createTempFile("TestRandomAccessFileThread", ".tmp", new File(".")); + tmp.deleteOnExit(); + + Recording recording = new Recording(); + recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); + recording.start(); + + TestThread writerThread = new TestThread(new XRun() { + @Override + public void xrun() throws IOException { + final byte[] buf = new byte[OP_COUNT]; + for (int i = 0; i < buf.length; ++i) { + buf[i] = (byte)((i + 'a') % 255); + } + try (RandomAccessFile raf = new RandomAccessFile(tmp, "rwd")) { + for(int i = 0; i < OP_COUNT; ++i) { + raf.write(buf, 0, i + 1); + writeCount++; + } + } + }}, "TestWriterThread"); + + TestThread readerThread = new TestThread(new XRun() { + @Override + public void xrun() throws IOException { + try (RandomAccessFile raf = new RandomAccessFile(tmp, "r")) { + byte[] buf = new byte[OP_COUNT]; + for(int i = 0; i < OP_COUNT; ++i) { + while (writeCount <= i) { + // No more data to read. Wait for writer thread. + Thread.yield(); + } + int expectedSize = i + 1; + int actualSize = raf.read(buf, 0, expectedSize); + Asserts.assertEquals(actualSize, expectedSize, "Wrong read size. Probably test error."); + } + } + }}, "TestReaderThread"); + + readerThread.start(); + writerThread.start(); + writerThread.joinAndThrow(); + readerThread.joinAndThrow(); + recording.stop(); + + List events = Events.fromRecording(recording); + events.sort(new EventComparator()); + + List readEvents = new ArrayList<>(); + List writeEvents = new ArrayList<>(); + for (RecordedEvent event : events) { + if (!isOurEvent(event, tmp)) { + continue; + } + logEventSummary(event); + if (Events.isEventType(event,IOEvent.EVENT_FILE_READ)) { + readEvents.add(event); + } else { + writeEvents.add(event); + } + } + + verifyThread(readEvents, readerThread); + verifyThread(writeEvents, writerThread); + verifyBytes(readEvents, "bytesRead"); + verifyBytes(writeEvents, "bytesWritten"); + verifyTimes(readEvents); + verifyTimes(writeEvents); + verifyReadWriteTimes(readEvents, writeEvents); + + Asserts.assertEquals(readEvents.size(), OP_COUNT, "Wrong number of read events"); + Asserts.assertEquals(writeEvents.size(), OP_COUNT, "Wrong number of write events"); + } + + private static void logEventSummary(RecordedEvent event) { + boolean isRead = Events.isEventType(event, IOEvent.EVENT_FILE_READ); + String name = isRead ? "read " : "write"; + String bytesField = isRead ? "bytesRead" : "bytesWritten"; + long bytes = Events.assertField(event, bytesField).getValue(); + long commit = Events.assertField(event, "startTime").getValue(); + Instant start = event.getStartTime(); + Instant end = event.getEndTime(); + System.out.printf("%s: bytes=%d, commit=%d, start=%s, end=%s%n", name, bytes, commit, start, end); + } + + private static void verifyThread(List events, Thread thread) { + events.stream().forEach(e -> Events.assertEventThread(e, thread)); + } + + private static void verifyBytes(List events, String fieldName) { + long expectedBytes = 0; + for (RecordedEvent event : events) { + Events.assertField(event, fieldName).equal(++expectedBytes); + } + } + + // Verify that all times are increasing + private static void verifyTimes(List events) { + RecordedEvent prev = null; + for (RecordedEvent curr : events) { + if (prev != null) { + try { + Asserts.assertGreaterThanOrEqual(curr.getStartTime(), prev.getStartTime(), "Wrong startTime"); + Asserts.assertGreaterThanOrEqual(curr.getEndTime(), prev.getEndTime(), "Wrong endTime"); + long commitPrev = Events.assertField(prev, "startTime").getValue(); + long commitCurr = Events.assertField(curr, "startTime").getValue(); + Asserts.assertGreaterThanOrEqual(commitCurr, commitPrev, "Wrong commitTime"); + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + System.out.println("Prev Event: " + prev); + System.out.println("Curr Event: " + curr); + throw e; + } + } + prev = curr; + } + } + + // Verify that all times are increasing + private static void verifyReadWriteTimes(List readEvents, List writeEvents) { + List events = new ArrayList<>(); + events.addAll(readEvents); + events.addAll(writeEvents); + events.sort(new EventComparator()); + + int countRead = 0; + int countWrite = 0; + for (RecordedEvent event : events) { + if (Events.isEventType(event, IOEvent.EVENT_FILE_READ)) { + ++countRead; + } else { + ++countWrite; + } + // We can not read from the file before it has been written. + // This check verifies that times of different threads are correct. + // Since the read and write are from different threads, it is possible that the read + // is committed before the same write. + // But read operation may only be 1 step ahead of the write operation. + Asserts.assertLessThanOrEqual(countRead, countWrite + 1, "read must be after write"); + } + } + + private static boolean isOurEvent(RecordedEvent event, File file) { + if (!Events.isEventType(event, IOEvent.EVENT_FILE_READ) && + !Events.isEventType(event, IOEvent.EVENT_FILE_WRITE)) { + return false; + } + String path = Events.assertField(event, "path").getValue(); + return file.getPath().equals(path); + } + + private static class EventComparator implements Comparator { + @Override + public int compare(RecordedEvent a, RecordedEvent b) { + long commitA = Events.assertField(a, "startTime").getValue(); + long commitB = Events.assertField(b, "startTime").getValue(); + return Long.compare(commitA, commitB); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/io/TestSocketChannelEvents.java 2019-02-08 18:33:31.848217199 +0300 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 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.event.io; + +import static jdk.test.lib.Asserts.assertEquals; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.thread.TestThread; +import jdk.test.lib.thread.XRun; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.io.TestSocketChannelEvents + */ +public class TestSocketChannelEvents { + private static final int bufSizeA = 10; + private static final int bufSizeB = 20; + + private List expectedEvents = new ArrayList<>(); + private synchronized void addExpectedEvent(IOEvent event) { + expectedEvents.add(event); + } + + public static void main(String[] args) throws Throwable { + new TestSocketChannelEvents().test(); + } + + public void test() throws Throwable { + Recording recording = new Recording(); + + try (ServerSocketChannel ss = ServerSocketChannel.open()) { + recording.enable(IOEvent.EVENT_SOCKET_READ).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_SOCKET_WRITE).withThreshold(Duration.ofMillis(0)); + recording.start(); + + ss.socket().setReuseAddress(true); + ss.socket().bind(null); + + TestThread readerThread = new TestThread(new XRun() { + @Override + public void xrun() throws IOException { + ByteBuffer bufA = ByteBuffer.allocate(bufSizeA); + ByteBuffer bufB = ByteBuffer.allocate(bufSizeB); + try (SocketChannel sc = ss.accept()) { + int readSize = sc.read(bufA); + assertEquals(readSize, bufSizeA, "Wrong readSize bufA"); + addExpectedEvent(IOEvent.createSocketReadEvent(bufSizeA, sc.socket())); + + bufA.clear(); + bufA.limit(1); + readSize = (int)sc.read(new ByteBuffer[] { bufA, bufB }); + assertEquals(readSize, 1 + bufSizeB, "Wrong readSize 1+bufB"); + addExpectedEvent(IOEvent.createSocketReadEvent(readSize, sc.socket())); + + // We try to read, but client have closed. Should get EOF. + bufA.clear(); + bufA.limit(1); + readSize = sc.read(bufA); + assertEquals(readSize, -1, "Wrong readSize at EOF"); + addExpectedEvent(IOEvent.createSocketReadEvent(-1, sc.socket())); + } + } + }); + readerThread.start(); + + try (SocketChannel sc = SocketChannel.open(ss.socket().getLocalSocketAddress())) { + ByteBuffer bufA = ByteBuffer.allocateDirect(bufSizeA); + ByteBuffer bufB = ByteBuffer.allocateDirect(bufSizeB); + for (int i = 0; i < bufSizeA; ++i) { + bufA.put((byte)('a' + (i % 20))); + } + for (int i = 0; i < bufSizeB; ++i) { + bufB.put((byte)('A' + (i % 20))); + } + bufA.flip(); + bufB.flip(); + + sc.write(bufA); + addExpectedEvent(IOEvent.createSocketWriteEvent(bufSizeA, sc.socket())); + + bufA.clear(); + bufA.limit(1); + int bytesWritten = (int)sc.write(new ByteBuffer[] { bufA, bufB }); + assertEquals(bytesWritten, 1 + bufSizeB, "Wrong bytesWritten 1+bufB"); + addExpectedEvent(IOEvent.createSocketWriteEvent(bytesWritten, sc.socket())); + } + + readerThread.joinAndThrow(); + recording.stop(); + List events= Events.fromRecording(recording); + IOHelper.verifyEquals(events, expectedEvents); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/io/TestSocketEvents.java 2019-02-08 18:33:31.996212044 +0300 @@ -0,0 +1,121 @@ +/* + * Copyright (c) 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.event.io; + +import static jdk.test.lib.Asserts.assertEquals; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.thread.TestThread; +import jdk.test.lib.thread.XRun; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.io.TestSocketEvents + */ +public class TestSocketEvents { + + private static final int writeInt = 'A'; + private static final byte[] writeBuf = { 'B', 'C', 'D', 'E' }; + + private List expectedEvents = new ArrayList<>(); + private synchronized void addExpectedEvent(IOEvent event) { + expectedEvents.add(event); + } + + public static void main(String[] args) throws Throwable { + new TestSocketEvents().test(); + } + + private void test() throws Throwable { + Recording recording = new Recording(); + + try (ServerSocket ss = new ServerSocket()) { + recording.enable(IOEvent.EVENT_SOCKET_READ).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_SOCKET_WRITE).withThreshold(Duration.ofMillis(0)); + recording.start(); + + ss.setReuseAddress(true); + ss.bind(null); + + TestThread readerThread = new TestThread(new XRun() { + @Override + public void xrun() throws IOException { + byte[] bs = new byte[4]; + try (Socket s = ss.accept(); InputStream is = s.getInputStream()) { + int readInt = is.read(); + assertEquals(readInt, writeInt, "Wrong readInt"); + addExpectedEvent(IOEvent.createSocketReadEvent(1, s)); + + int bytesRead = is.read(bs, 0, 3); + assertEquals(bytesRead, 3, "Wrong bytesRead partial buffer"); + addExpectedEvent(IOEvent.createSocketReadEvent(bytesRead, s)); + + bytesRead = is.read(bs); + assertEquals(bytesRead, writeBuf.length, "Wrong bytesRead full buffer"); + addExpectedEvent(IOEvent.createSocketReadEvent(bytesRead, s)); + + // Try to read more, but writer have closed. Should get EOF. + readInt = is.read(); + assertEquals(readInt, -1, "Wrong readInt at EOF"); + addExpectedEvent(IOEvent.createSocketReadEvent(-1, s)); + } + } + }); + readerThread.start(); + + try (Socket s = new Socket()) { + s.connect(ss.getLocalSocketAddress()); + try (OutputStream os = s.getOutputStream()) { + os.write(writeInt); + addExpectedEvent(IOEvent.createSocketWriteEvent(1, s)); + os.write(writeBuf, 0, 3); + addExpectedEvent(IOEvent.createSocketWriteEvent(3, s)); + os.write(writeBuf); + addExpectedEvent(IOEvent.createSocketWriteEvent(writeBuf.length, s)); + } + } + + readerThread.joinAndThrow(); + recording.stop(); + List events = Events.fromRecording(recording); + IOHelper.verifyEquals(events, expectedEvents); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/metadata/TestDefaultConfigurations.java 2019-02-08 18:33:32.140207030 +0300 @@ -0,0 +1,204 @@ +/* + * Copyright (c) 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.event.metadata; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import jdk.jfr.Configuration; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.SettingDescriptor; +import jdk.test.lib.jfr.EventNames; + +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + + * + * @run main/othervm jdk.jfr.event.metadata.TestDefaultConfigurations + */ +public class TestDefaultConfigurations { + + private static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + public static void main(String[] args) throws Exception { + List errors = new ArrayList<>(); + + errors.addAll(testConfiguration(Configuration.getConfiguration("default"))); + errors.addAll(testConfiguration(Configuration.getConfiguration("profile"))); + + if (!errors.isEmpty()) { + throwExceptionWithErrors(errors); + } + } + + private static List testConfiguration(Configuration config) throws ParserConfigurationException, SAXException, IOException { + List errors = new ArrayList<>(); + + Map eventTypeLookup = new HashMap<>(); + for (EventType t : FlightRecorder.getFlightRecorder().getEventTypes()) { + eventTypeLookup.put(t.getName(), t); + } + String content = config.getContents(); + Document doc = createDocument(content); + Element configuration = doc.getDocumentElement(); + errors.addAll(checkConfiguration(configuration)); + for (Element event : getChildElements(configuration, "event")) { + String name = event.getAttribute("name"); + + EventType cd = eventTypeLookup.get(name); + if (cd != null) { + errors.addAll(checkSettings(config, cd, event)); + } else { + errors.add("Preset '" + config.getName() + "' reference unknown event '" + name + "'"); + } + eventTypeLookup.remove(name); + } + for (String name : eventTypeLookup.keySet()) { + errors.add("Preset '" + config.getName() + "' doesn't configure event '" + name + "'"); + } + + return errors; + } + + private static void throwExceptionWithErrors(List errors) throws Exception { + StringBuilder sb = new StringBuilder(); + for (String error : errors) { + sb.append(error); + sb.append(LINE_SEPARATOR); + } + throw new Exception(sb.toString()); + } + + private static List checkConfiguration(Element configuration) { + List errors = new ArrayList<>(); + if (configuration.getAttribute("description").length() < 2) { + errors.add("Configuration should have a valid description!"); + } + if (configuration.getAttribute("label").length() < 2) { + errors.add("Configuration should have a label!"); + } + if (!configuration.getAttribute("provider").equals("Oracle")) { + errors.add("Configuration should set provider to 'Oracle'!"); + } + return errors; + } + + private static List checkSettings(Configuration config, EventType eventType, Element event) { + List errors = new ArrayList<>(); + + Set requiredSettings = createRequiredSettingNameSet(eventType); + for (Element setting : getChildElements(event, "setting")) { + String settingName = setting.getAttribute("name"); + if (requiredSettings.contains(settingName)) { + requiredSettings.remove(settingName); + } else { + errors.add("Setting '" + settingName + "' for event '" + eventType.getName() + "' should not be part of confirguaration '" + config.getName() + + "' since it won't have an impact on the event."); + } + } + for (String required : requiredSettings) { + errors.add("Setting '" + required + "' in event '" + eventType.getName() + "' was not configured in the configuration '" + config.getName() + "'"); + } + + return errors; + } + + private static Set createRequiredSettingNameSet(EventType cd) { + Set requiredSettings = new HashSet<>(); + for (SettingDescriptor s : cd.getSettingDescriptors()) { + requiredSettings.add(s.getName()); + } + return requiredSettings; + } + + private static Document createDocument(String content) throws ParserConfigurationException, SAXException, IOException { + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + Document doc = dBuilder.parse(new InputSource(new StringReader(content))); + doc.getDocumentElement().normalize(); + // Don't want to add these settings to the jfc-files we ship since they + // are not useful to configure. They are however needed to make the test + // pass. + insertSetting(doc, EventNames.ActiveSetting, "stackTrace", "false"); + insertSetting(doc, EventNames.ActiveSetting, "threshold", "0 ns"); + insertSetting(doc, EventNames.ActiveRecording, "stackTrace", "false"); + insertSetting(doc, EventNames.ActiveRecording, "threshold", "0 ns"); + insertSetting(doc, EventNames.JavaExceptionThrow, "threshold", "0 ns"); + insertSetting(doc, EventNames.JavaErrorThrow, "threshold", "0 ns"); + return doc; + } + + private static void insertSetting(Document doc, String eventName, String settingName, String settingValue) { + for (Element event : getChildElements(doc.getDocumentElement(), "event")) { + Attr attribute = event.getAttributeNode("name"); + if (attribute != null) { + if (eventName.equals(attribute.getValue())) { + Element setting = doc.createElement("setting"); + setting.setAttribute("name", settingName); + setting.setTextContent(settingValue); + event.appendChild(setting); + } + } + } + } + + private static Collection getChildElements(Element parent, String name) { + NodeList elementsByTagName = parent.getElementsByTagName(name); + List elements = new ArrayList<>(); + for (int i = 0; i < elementsByTagName.getLength(); i++) { + Node node = elementsByTagName.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + elements.add((Element) node); + } + } + return elements; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/metadata/TestEventMetadata.java 2019-02-08 18:33:32.284202015 +0300 @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2013, 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.event.metadata; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.ValueDescriptor; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.metadata.TestEventMetadata + */ + +public class TestEventMetadata { + + /* + * Short guide to writing event metadata + * ===================================== + + * Name + * ---- + * + * Symbolic name that is used to identify an event, or a field. Referred to + * as "id" and "field" in trace.xml-files and @Name in the Java API. If it is + * the name of an event, the name should be prefixed "jdk.", which + * happens automatically for native events. + * + * The name should be short, but not so brief that collision is likely with + * future events or fields. It should only consist of letters and numbers. + * Use Java naming convention , i.e. "FileRead" for an event and + * "allocationRate" for a field. Do not use "_" and don't add the word + * "Event" to the event name. + * + * Abbreviations should be avoided, but may be acceptable if the name + * becomes long, or if it is a well established acronym. Write whole words, + * i.e. "allocation" instead of "alloc". The name should not be a reserved + * Java keyword, i.e "void" or "class". + * + * Label + * ----- + * + * Describes a human-readable name, typically 1-3 words. Use headline-style + * capitalization, capitalize the first and last words, and all nouns, + * pronouns, adjectives, verbs and adverbs. Do not include ending + * punctuation. + * + * Description + * ----------- + * + * Describes an event with a sentence or two. It's better to omit the + * description then copying the label. Use sentence-style + * capitalization, capitalize the first letter of the first word, and any + * proper names such as the word Java. If the description is one sentence, + * period should not be included. + * + * + * Do not forget to set proper units for fields, i.e "NANOS", "MILLS", + * "TICKSPAN" ,"BYETS", "PECENTAGE" etc. in native and @Timespan, @Timespan + * etc. in Java. + */ + public static void main(String[] args) throws Exception { + Set types = new HashSet<>(); + List eventTypes = FlightRecorder.getFlightRecorder().getEventTypes(); + Set eventNames= new HashSet<>(); + for (EventType eventType : eventTypes) { + verifyEventType(eventType); + verifyValueDesscriptors(eventType.getFields(), types); + System.out.println(); + String eventName = eventType.getName(); + if (eventNames.contains(eventName)) { + throw new Exception("Event with name " +eventName+ " already exists"); + } + eventNames.add(eventName); + Set fieldNames = new HashSet<>(); + for (ValueDescriptor v : eventType.getFields()) { + String fieldName = v.getName(); + if (fieldNames.contains(fieldName)) { + throw new Exception("Field with name " + fieldName +" is already in use in event name " +eventName); + } + fieldNames.add(fieldName); + } + } + } + + private static void verifyValueDesscriptors(List fields, Set visitedTypes) { + for (ValueDescriptor v : fields) { + if (!visitedTypes.contains(v.getTypeName())) { + visitedTypes.add(v.getTypeName()); + verifyValueDesscriptors(v.getFields(), visitedTypes); + } + verifyValueDescriptor(v); + } + } + + private static void verifyValueDescriptor(ValueDescriptor v) { + verifyName(v.getName()); + verifyLabel(v.getLabel()); + verifyDescription(v.getDescription()); + } + + private static void verifyDescription(String description) { + if (description == null) { + return; + } + Asserts.assertTrue(description.length() > 10, "Description must be at least ten characters"); + Asserts.assertTrue(description.length() < 300, "Description should not exceed 300 characters. Found " + description); + Asserts.assertTrue(description.length() == description.trim().length(), "Description should not have trim character at start or end"); + Asserts.assertFalse(description.endsWith(".") && description.indexOf(".") == description.length() - 1, "Single sentence descriptions should not use end punctuation"); + } + + private static void verifyName(String name) { + System.out.println("Verifying name: " + name); + Asserts.assertNotEquals(name, null, "Name not allowed to be null"); + Asserts.assertTrue(name.length() > 1, "Name must be at least two characters"); + Asserts.assertTrue(name.length() < 32, "Name should not exceed 32 characters"); + Asserts.assertFalse(isReservedKeyword(name),"Name must not be reserved keyword in the Java language (" + name + ")"); + char firstChar = name.charAt(0); + Asserts.assertTrue(Character.isAlphabetic(firstChar), "Name must start with a character"); + Asserts.assertTrue(Character.isLowerCase(firstChar), "Name must start with lower case letter"); + Asserts.assertTrue(Character.isJavaIdentifierStart(firstChar), "Not valid first character for Java identifier"); + for (int i = 1; i < name.length(); i++) { + Asserts.assertTrue(Character.isJavaIdentifierPart(name.charAt(i)), "Not valid character for a Java identifier"); + Asserts.assertTrue(Character.isAlphabetic(name.charAt(i)), "Name must consists of characters, found '" + name.charAt(i) + "'"); + } + Asserts.assertFalse(name.contains("ID"), "'ID' should not be used in name, consider using 'Id'"); + checkCommonAbbreviations(name); + } + + private static void verifyLabel(String label) { + Asserts.assertNotEquals(label, null, "Label not allowed to be null"); + Asserts.assertTrue(label.length() > 1, "Name must be at least two characters"); + Asserts.assertTrue(label.length() < 45, "Label should not exceed 45 characters, use description to explain " + label); + Asserts.assertTrue(label.length() == label.trim().length(), "Label should not have trim character at start and end"); + Asserts.assertTrue(Character.isUpperCase(label.charAt(0)), "Label should start with upper case letter"); + for (int i = 0; i < label.length(); i++) { + char c = label.charAt(i); + Asserts.assertTrue(Character.isDigit(c) || Character.isAlphabetic(label.charAt(i)) || c == ' ' || c == '(' || c == ')' || c == '-', "Label should only consist of letters or space, found '" + label.charAt(i) + + "'"); + } + } + + private static void verifyEventType(EventType eventType) { + System.out.println("Verifying event: " + eventType.getName()); + verifyDescription(eventType.getDescription()); + verifyLabel(eventType.getLabel()); + Asserts.assertNotEquals(eventType.getName(), null, "Name not allowed to be null"); + Asserts.assertTrue(eventType.getName().startsWith(EventNames.PREFIX), "OpenJDK events must start with " + EventNames.PREFIX); + String name = eventType.getName().substring(EventNames.PREFIX.length()); + Asserts.assertFalse(isReservedKeyword(name),"Name must not be reserved keyword in the Java language (" + name + ")"); + checkCommonAbbreviations(name); + char firstChar = name.charAt(0); + Asserts.assertFalse(name.contains("ID"), "'ID' should not be used in name, consider using 'Id'"); + Asserts.assertTrue(Character.isAlphabetic(firstChar), "Name " + name + " must start with a character"); + Asserts.assertTrue(Character.isUpperCase(firstChar), "Name " + name + " must start with upper case letter"); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + Asserts.assertTrue(Character.isAlphabetic(c) || Character.isDigit(c), "Name " + name + " must consists of characters or numbers, found '" + name.charAt(i) + "'"); + } + } + + static boolean isReservedKeyword(String s) { + String[] keywords = new String[] { + // "module", "requires", "exports", "to", "uses", "provides", "with", module-info.java + "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", + "extends", "false", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "null", "package", "private", + "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try", "void", "volatile", "while" }; + for (int i = 0; i < keywords.length; i++) { + if (s.equals(keywords[i])) { + return true; + } + } + return false; + } + + private static void checkCommonAbbreviations(String name) { + String lowerCased = name.toLowerCase(); + Asserts.assertFalse(lowerCased.contains("info") && !lowerCased.contains("information"), "Use 'information' instead 'info' in name"); + Asserts.assertFalse(lowerCased.contains("alloc") && !lowerCased.contains("alloca"), "Use 'allocation' instead 'alloc' in name"); + Asserts.assertFalse(lowerCased.contains("config") && !lowerCased.contains("configuration"), "Use 'configuration' instead of 'config' in name"); + Asserts.assertFalse(lowerCased.contains("evac") && !lowerCased.contains("evacu"), "Use 'evacuation' instead of 'evac' in name"); + Asserts.assertFalse(lowerCased.contains("stat") && !(lowerCased.contains("state") ||lowerCased.contains("statistic")) , "Use 'statistics' instead of 'stat' in name"); + Asserts.assertFalse(name.contains("ID") , "Use 'id' or 'Id' instead of 'ID' in name"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/OldObjects.java 2019-02-08 18:33:32.440196583 +0300 @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2015, 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.event.oldobject; + +import java.io.IOException; +import java.util.List; +import java.util.function.Predicate; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedMethod; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.test.lib.jfr.Events; + +/** + * Utility class to perform Old Object provocation/detection and + * stack trace/object verification for the Old Object Sample JFR event + */ +final public class OldObjects { + + public static final int MIN_SIZE = 99901; // prime number + public final static int LEAK_CONTEXT = 100; // length of chain assiociated with the object sample + public final static int ROOT_CONTEXT = 100; // length of chain assoicated with the root + public final static int MAX_CHAIN_LENGTH = LEAK_CONTEXT + ROOT_CONTEXT; // the VM should not construct chains longer than this + + private static String[] getFrames(String expectedFrame) { + if (expectedFrame != null) { + return new String[] { expectedFrame }; + } else { + return null; + } + } + + /** + * + * @param r + * A recording + * @param expectedFrame + * A frame that must be found on the stack. Null if no check is required. + * @param fieldType + * The object type (of the field). Null if no check is required. + * @param fieldName + * The field name. Null if no check is required. + * @param referrerType + * The class name. Null if no check is required. + * @param minDuration + * The minimum duration of the event, -1 if not applicable. + * @return The count of matching events + * @throws IOException + */ + public static long countMatchingEvents(Recording r, String expectedFrame, Class fieldType, String fieldName, Class referrerType, long minDuration) throws IOException { + return countMatchingEvents(r, getFrames(expectedFrame), fieldType, fieldName, referrerType, minDuration); + } + + /** + * Gets the OldObjectSample events from the provided recording through a dump + * and counts how many events matches the provided parameters. + * + * @param r + * A recording + * @param expectedStack + * Some frames that must be found on the stack. Null if no check is required. + * @param fieldType + * The object type (of the field). Null if no check is required. + * @param fieldName + * The field name. Null if no check is required. + * @param referrerType + * The class name. Null if no check is required. + * @param minDuration + * The minimum duration of the event, -1 if not applicable. + * @return The count of matching events + * @throws IOException + */ + public static long countMatchingEvents(Recording r, String[] expectedStack, Class fieldType, String fieldName, Class referrerType, long minDuration) throws IOException { + return countMatchingEvents(Events.fromRecording(r), fieldType, fieldName, referrerType, minDuration, expectedStack); + } + + /** + * + * @param events + * A list of RecordedEvent. + * @param expectedFrame + * A frame that must be found on the stack. Null if no check is required. + * @param fieldType + * The object type (of the field). Null if no check is required. + * @param fieldName + * The field name. Null if no check is required. + * @param referrerType + * The class name. Null if no check is required. + * @param minDuration + * The minimum duration of the event, -1 if not applicable. + * @return The count of matching events + * @throws IOException + */ + public static long countMatchingEvents(List events, String expectedFrame, Class fieldType, String fieldName, Class referrerType, long minDuration) throws IOException { + return countMatchingEvents(events, fieldType, fieldName, referrerType, minDuration, getFrames(expectedFrame)); + } + + /** + * + * @param events + * The list of events to find matching events in + * @param expectedStack + * Some frames that must be found on the stack. Null if no check is required. + * @param fieldType + * The object type (of the field). Null if no check is required. + * @param fieldName + * The field name. Null if no check is required. + * @param referrerType + * The class name. Null if no check is required. + * @param minDuration + * The minimum duration of the event, -1 if not applicable. + * @return The count of matching events + * @throws IOException + */ + public static long countMatchingEvents(List events, Class fieldType, String fieldName, Class referrerType, long minDuration, String... expectedStack) throws IOException { + String currentThread = Thread.currentThread().getName(); + return events.stream() + .filter(hasJavaThread(currentThread)) + .filter(fieldIsType(fieldType)) + .filter(hasFieldName(fieldName)) + .filter(isReferrerType(referrerType)) + .filter(durationAtLeast(minDuration)) + .filter(hasStackTrace(expectedStack)) + .count(); + } + + private static Predicate hasJavaThread(String expectedThread) { + if (expectedThread != null) { + return e -> e.getThread() != null && expectedThread.equals(e.getThread().getJavaName()); + } else { + return e -> true; + } + } + + private static Predicate hasStackTrace(String[] expectedStack) { + if (expectedStack != null) { + return e -> matchingStackTrace(e.getStackTrace(), expectedStack); + } else { + return e -> true; + } + } + + private static Predicate fieldIsType(Class fieldType) { + if (fieldType != null) { + return e -> e.hasField("object.type") && ((RecordedClass) e.getValue("object.type")).getName().equals(fieldType.getName()); + } else { + return e -> true; + } + } + + private static Predicate hasFieldName(String fieldName) { + if (fieldName != null) { + return e -> { + RecordedObject referrer = e.getValue("object.referrer"); + return referrer != null ? referrer.hasField("field.name") && referrer.getValue("field.name").equals(fieldName) : false; + }; + } else { + return e -> true; + } + } + + private static Predicate isReferrerType(Class referrerType) { + if (referrerType != null) { + return e -> { + RecordedObject referrer = e.getValue("object.referrer"); + return referrer != null ? referrer.hasField("object.type") && + ((RecordedClass) referrer.getValue("object.type")).getName().equals(referrerType.getName()) : false; + }; + } else { + return e -> true; + } + } + + private static Predicate durationAtLeast(long minDurationMs) { + if (minDurationMs > 0) { + return e -> e.getDuration().toMillis() >= minDurationMs; + } else { + return e -> true; + } + } + + public static boolean matchingReferrerClass(RecordedEvent event, String className) { + RecordedObject referrer = event.getValue("object.referrer"); + if (referrer != null) { + if (!referrer.hasField("object.type")) { + return false; + } + + String reportedClass = ((RecordedClass) referrer.getValue("object.type")).getName(); + if (reportedClass.equals(className)) { + return true; + } + } + return false; + } + + public static String getReferrerFieldName(RecordedEvent event) { + RecordedObject referrer = event.getValue("object.referrer"); + return referrer != null && referrer.hasField("field.name") ? referrer.getValue("field.name") : null; + } + + public static boolean matchingStackTrace(RecordedStackTrace stack, String[] expectedStack) { + if (stack == null) { + return false; + } + + List frames = stack.getFrames(); + int pos = findFramePos(frames, expectedStack[0]); + + if (pos == -1) { + return false; + } + + for (String expectedFrame : expectedStack) { + RecordedFrame f = frames.get(pos++); + String frame = frameToString(f); + + if (!frame.contains(expectedFrame)) { + return false; + } + } + return true; + } + + private static int findFramePos(List frames, String frame) { + int pos = 0; + for (RecordedFrame f : frames) { + if (frameToString(f).contains(frame)) { + return pos; + } + pos++; + } + return -1; + } + + private static String frameToString(RecordedFrame f) { + RecordedMethod m = f.getMethod(); + String methodName = m.getName(); + String className = m.getType().getName(); + return className + "." + methodName; + } + + public static void validateReferenceChainLimit(RecordedEvent e, int maxLength) { + int length = 0; + RecordedObject object = e.getValue("object"); + while (object != null) { + ++length; + RecordedObject referrer = object.getValue("referrer"); + object = referrer != null ? referrer.getValue("object") : null; + } + if (length > maxLength) { + throw new RuntimeException("Reference chain max length not respected. Found a chain of length " + length); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestAllocationTime.java 2019-02-08 18:33:32.592191290 +0300 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 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.event.oldobject; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k -XX:-FastTLABRefill jdk.jfr.event.oldobject.TestAllocationTime + */ +public class TestAllocationTime { + + private static class BeforeLeakEvent extends Event { + } + private static class AfterLeakEvent extends Event { + } + private static class Leak { + } + + public static List leak = new ArrayList<>(OldObjects.MIN_SIZE); + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + while(true) { + try (Recording recording = new Recording()) { + leak.clear(); + recording.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "1 h"); + recording.start(); + + BeforeLeakEvent be = new BeforeLeakEvent(); + be.commit(); + + // Allocate array to trigger sampling code path for interpreter / c1 + for (int i = 0; i < OldObjects.MIN_SIZE; i++) { + leak.add(new Leak[0]); + } + + AfterLeakEvent ae = new AfterLeakEvent(); + ae.commit(); + + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + RecordedObject sample = findLeak(events, BeforeLeakEvent.class.getName()); + if (sample != null) { + long beforeTime = find(events, BeforeLeakEvent.class.getName()).getValue("startTime"); + long allocationTime = sample.getValue("allocationTime"); + long afterTime = find(events, AfterLeakEvent.class.getName()).getValue("startTime"); + System.out.println("Before time : " + beforeTime); + System.out.println("Allocation time : " + allocationTime); + System.out.println("After time : " + afterTime); + + if (allocationTime < beforeTime) { + throw new Exception("Allocation should not happen this early"); + } + if (allocationTime > afterTime) { + throw new Exception("Allocation should not happen this late"); + } + return; // sample ok + } + } + } + } + + private static RecordedObject findLeak(List events, String name) throws Exception { + for (RecordedEvent e : events) { + if (e.getEventType().getName().equals(EventNames.OldObjectSample)) { + RecordedObject object = e.getValue("object"); + RecordedClass rc = object.getValue("type"); + if (rc.getName().equals(Leak[].class.getName())) { + return e; + } + } + } + return null; + } + + private static RecordedEvent find(List events, String name) throws Exception { + for (RecordedEvent e : events) { + if (e.getEventType().getName().equals(name)) { + return e; + } + } + throw new Exception("Could not find event with name " + name); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestArrayInformation.java 2019-02-08 18:33:32.744185997 +0300 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 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.event.oldobject; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k jdk.jfr.event.oldobject.TestArrayInformation + */ +public class TestArrayInformation { + + private static class ArrayLeak { + } + + private static final int CHAIN_DEPTH = 50; + private static final int ARRAY_SIZE = 52; + private static final int ARRAY_INDEX = 26; + + public static List leak = new ArrayList<>(25); + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + try (Recording recording = new Recording()) { + recording.enable(EventNames.OldObjectSample).withoutStackTrace().with("cutoff", "infinity"); + recording.start(); + for(int i = 0; i < 25; i++) { + leak.add( buildNestedArray(CHAIN_DEPTH)); + } + recording.stop(); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + verifyObjectArray(events); + } + } + + private static void verifyObjectArray(List events) throws Exception { + for (RecordedEvent e : events) { + RecordedObject object = e.getValue("object"); + RecordedClass objectType = object.getValue("type"); + RecordedObject referrer = object.getValue("referrer"); + System.out.println(objectType.getName()); + if (objectType.getName().equals(ArrayLeak[].class.getName())) { + for (int i = 0; i < CHAIN_DEPTH; i++) { + object = referrer.getValue("object"); + if (object == null) { + throw new Exception("Expected referrer object"); + } + objectType = object.getValue("type"); + if (!objectType.getName().equals(Object[].class.getName())) { + throw new Exception("Expect array class to be named " + Object[].class + " but found " + objectType.getName()); + } + RecordedObject field = referrer.getValue("field"); + if (field != null) { + throw new Exception("Didn't expect to find field"); + } + RecordedObject array = referrer.getValue("array"); + if (array == null) { + throw new Exception("Expected array object, but got null"); + } + int index = referrer.getValue("array.index"); + if (index != ARRAY_INDEX) { + throw new Exception("Expected array index: " + ARRAY_INDEX + ", but got " + index); + } + int size = referrer.getValue("array.size"); + if (size != ARRAY_SIZE) { + throw new Exception("Expected array size: " + ARRAY_SIZE + ", but got " + size); + } + referrer = object.getValue("referrer"); + } + return; + } + } + throw new Exception("Could not find event with " + ArrayLeak[].class + " as (leak) object"); + } + + private static Object buildNestedArray(int depth) { + if (depth > 0) { + Object[] array = new Object[ARRAY_SIZE]; + array[ARRAY_INDEX] = buildNestedArray(depth - 1); + return array; + } else { + return new ArrayLeak[OldObjects.MIN_SIZE]; + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestCMS.java 2019-02-08 18:33:32.896180705 +0300 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 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.event.oldobject; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @summary Test leak profiler with CMS GC + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k -XX:+UseConcMarkSweepGC jdk.jfr.event.oldobject.TestCMS + */ +public class TestCMS { + + static private class FindMe { + } + + public static List list = new ArrayList<>(OldObjects.MIN_SIZE); + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); + r.start(); + allocateFindMe(); + System.gc(); + r.stop(); + List events = Events.fromRecording(r); + System.out.println(events); + if (OldObjects.countMatchingEvents(events, FindMe[].class, null, null, -1, "allocateFindMe") == 0) { + throw new Exception("Could not find leak with " + FindMe[].class); + } + } + } + + public static void allocateFindMe() { + for (int i = 0; i < OldObjects.MIN_SIZE; i++) { + // Allocate array to trigger sampling code path for interpreter / c1 + list.add(new FindMe[0]); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestCircularReference.java 2019-02-08 18:33:33.040175690 +0300 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015, 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.event.oldobject; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k jdk.jfr.event.oldobject.TestCircularReference + */ +public class TestCircularReference { + + static class TestCircularReferrer { + // Allocate array to trigger sampling code path for interpreter / c1 + final byte[] leak = new byte[1_0000_000]; + TestCircularReferrer reference; + + public void setReference(TestCircularReferrer reference) { + this.reference = reference; + } + } + + public final static List referenceHolder = new ArrayList<>(OldObjects.MIN_SIZE); + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).with("cutoff", "infinity").withStackTrace(); + r.start(); + + TestCircularReferrer a = new TestCircularReferrer(); + TestCircularReferrer b = new TestCircularReferrer(); + TestCircularReferrer c = new TestCircularReferrer(); + a.setReference(b); + b.setReference(c); + c.setReference(a); + referenceHolder.add(a); + + TestCircularReferrer selfReflector = new TestCircularReferrer(); + selfReflector.setReference(selfReflector); + referenceHolder.add(selfReflector); + + r.stop(); + + List events = Events.fromRecording(r); + if (events.isEmpty()) { + throw new AssertionError("Expected Old Object Sample events!"); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestClassLoader.java 2019-02-08 18:33:33.184170676 +0300 @@ -0,0 +1,127 @@ +/* + * Copyright (c) 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.event.oldobject; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.io.ByteArrayOutputStream; + +/* + * A special class loader that will, for our test class, make sure that each + * load will become a unique class + */ +public final class TestClassLoader extends ClassLoader { + + public static final class TestClass0000000 { + public static final byte[] oneByte = new byte[1]; + } + + static byte[] classByteCode = readTestClassBytes(); + private static int classIdCounter; + + TestClassLoader() { + super(TestClassLoader.class.getClassLoader()); + } + + public List> loadClasses(int count) throws Exception { + List> classes = new ArrayList<>(); + for (int i = 0; i < count; i++) { + String className = "jdk.jfr.event.oldobject.TestClassLoader$"; + className += "TestClass" + String.format("%07d", classIdCounter++); + Class clazz = Class.forName(className, true, this); + classes.add(clazz); + } + return classes; + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + // If not loading the test class, just go on with the normal class loader + if (!name.contains("TestClass")) { + return super.loadClass(name, resolve); + } + String[] classNameParts = name.split("\\$"); + String newName = classNameParts[1]; + String oldName = "TestClass0000000"; + if (oldName.length() != newName.length()) { + throw new AssertionError("String lengths don't match. Unable to replace class name"); + } + byte[] newBytes = classByteCode.clone(); + replaceBytes(newBytes, oldName.getBytes(), newName.getBytes()); + Class c = defineClass(name, newBytes, 0, newBytes.length); + if (resolve) { + resolveClass(c); + } + return c; + } + + static void replaceBytes(byte[] bytes, byte[] find, byte[] replacement) { + for (int index = 0; index < bytes.length - find.length; index++) { + if (matches(bytes, index, find)) { + replace(bytes, index, replacement); + } + } + } + + private static void replace(byte[] bytes, int startIndex, byte[] replacement) { + for (int index = 0; index < replacement.length; index++) { + bytes[startIndex + index] = replacement[index]; + } + } + + private static boolean matches(byte[] bytes, int startIndex, byte[] matching) { + for (int i = 0; i < matching.length; i++) { + if (bytes[startIndex + i] != matching[i]) { + return false; + } + } + return true; + } + + private static byte[] readTestClassBytes() { + try { + String classFileName = "jdk/jfr/event/oldobject/TestClassLoader$TestClass0000000.class"; + InputStream is = TestClassLoader.class.getClassLoader().getResourceAsStream(classFileName); + if (is == null) { + throw new RuntimeException("Culd not find class file " + classFileName); + } + //byte[] b;= is.readAllBytes(); + //is.read(b); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + byte[] buffer = new byte[0xFFFF]; + for (int len = is.read(buffer); len != -1; len = is.read(buffer)) { + os.write(buffer, 0, len); + } + is.close(); + return os.toByteArray(); + } catch (IOException ioe) { + ioe.printStackTrace(); + throw new RuntimeException(ioe); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestClassLoaderLeak.java 2019-02-08 18:33:33.328165662 +0300 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 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.event.oldobject; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k jdk.jfr.event.oldobject.TestClassLoaderLeak + */ +public class TestClassLoaderLeak { + + public static List classObjects = new ArrayList<>(OldObjects.MIN_SIZE); + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); + r.start(); + TestClassLoader testClassLoader = new TestClassLoader(); + for (Class clazz : testClassLoader.loadClasses(OldObjects.MIN_SIZE / 20)) { + // Allocate array to trigger sampling code path for interpreter / c1 + for (int i = 0; i < 20; i++) { + Object classArray = Array.newInstance(clazz, 20); + Array.set(classArray, i, clazz.newInstance()); + classObjects.add(classArray); + } + } + r.stop(); + List events = Events.fromRecording(r); + Events.hasEvents(events); + for (RecordedEvent e : events) { + RecordedObject object = e.getValue("object"); + RecordedClass rc = object.getValue("type"); + if (rc.getName().contains("TestClass")) { + return; + } + } + Asserts.fail("Could not find class leak"); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestFieldInformation.java 2019-02-08 18:33:33.480160369 +0300 @@ -0,0 +1,121 @@ +/* + * Copyright (c) 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.event.oldobject; + +import java.lang.reflect.Modifier; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k -XX:+PrintGCDetails -XX:+PrintGC -verbose:class jdk.jfr.event.oldobject.TestFieldInformation + */ +public class TestFieldInformation { + + public static final Object[] testField = new Object[50]; + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + try (Recording recording = new Recording()) { + recording.enable(EventNames.OldObjectSample).withoutStackTrace().with("cutoff", "infinity"); + recording.start(); + + addToTestField(); + + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent e : events) { + if (hasValidField(e)) { + return; + } + } + System.out.println(events); + Asserts.fail("Could not find old object with field 'testField'"); + } + } + + private static boolean hasValidField(RecordedEvent e) throws Exception { + RecordedObject object = e.getValue("object"); + Set visited = new HashSet<>(); + while (object != null) { + Long address = object.getValue("address"); + if (visited.contains(address)) { + return false; + } + visited.add(address); + RecordedObject referrer = object.getValue("referrer"); + RecordedObject fieldObject = referrer != null ? referrer.getValue("field") : null; + if (fieldObject != null) { + String name = fieldObject.getValue("name"); + if (name != null && name.equals("testField")) { + int modifiers = (short) fieldObject.getValue("modifiers"); + if (!Modifier.isStatic(modifiers)) { + throw new Exception("Field should be static"); + } + if (!Modifier.isPublic(modifiers)) { + throw new Exception("Field should be private"); + } + if (!Modifier.isFinal(modifiers)) { + throw new Exception("Field should be final"); + } + if (Modifier.isTransient(modifiers)) { + throw new Exception("Field should not be transient"); + } + if (Modifier.isVolatile(modifiers)) { + throw new Exception("Field should not be volatile"); + } + return true; + } + } + object = referrer != null ? referrer.getValue("object") : null; + } + return false; + } + + private static void addToTestField() { + for (int i = 0; i < testField.length; i++) { + // Allocate array to trigger sampling code path for interpreter / c1 + testField[i] = new Object[1_000_000]; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestG1.java 2019-02-08 18:33:33.624155356 +0300 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 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.event.oldobject; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @summary Test leak profiler with G1 GC + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k -XX:+UseG1GC jdk.jfr.event.oldobject.TestG1 + */ +public class TestG1 { + + static private class FindMe { + } + + public static List list = new ArrayList<>(OldObjects.MIN_SIZE); + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); + r.start(); + allocateFindMe(); + System.gc(); + r.stop(); + List events = Events.fromRecording(r); + System.out.println(events); + if (OldObjects.countMatchingEvents(events, FindMe[].class, null, null, -1, "allocateFindMe") == 0) { + throw new Exception("Could not find leak with " + FindMe[].class); + } + } + } + + public static void allocateFindMe() { + for (int i = 0; i < OldObjects.MIN_SIZE; i++) { + // Allocate array to trigger sampling code path for interpreter / c1 + list.add(new FindMe[0]); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestHeapDeep.java 2019-02-08 18:33:33.768150342 +0300 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015, 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.event.oldobject; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k jdk.jfr.event.oldobject.TestHeapDeep + */ +public class TestHeapDeep { + + final static class ChainNode { + final ChainNode next; + List leaks; + + public ChainNode(ChainNode node) { + next = node; + } + } + + public static ChainNode leak; + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); + r.start(); + leak = createChain(); + List events = Events.fromRecording(r); + if (OldObjects.countMatchingEvents(events, byte[].class, null, null, -1, "createChain") == 0) { + throw new Exception("Could not find ChainNode"); + } + for (RecordedEvent e : events) { + OldObjects.validateReferenceChainLimit(e, OldObjects.MAX_CHAIN_LENGTH); + } + } + } + + private static ChainNode createChain() { + ChainNode node = new ChainNode(null); + node.leaks = new ArrayList<>(); + for (int i = 0; i < 100; i++) { + // Allocate arrays to trigger sampling code path for interpreter / c1 + node.leaks.add(new byte[10_000]); + } + for (int i = 0; i < OldObjects.MIN_SIZE; i++) { + node = new ChainNode(node); + } + return node; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestHeapShallow.java 2019-02-08 18:33:33.916145188 +0300 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 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.event.oldobject; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k jdk.jfr.event.oldobject.TestHeapShallow + */ +public class TestHeapShallow { + private final static long LARGE_OBJECT_FACTOR = 509; // prime number + + static class LeakObject { + Object object = null; + + public LeakObject(Object object) { + this.object = object; + } + } + + public static ArrayList leak = new ArrayList<>(OldObjects.MIN_SIZE); + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + try (Recording recording = new Recording()) { + recording.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); + recording.start(); + + addObjectsToShallowArray(leak); + recording.stop(); + if (OldObjects.countMatchingEvents(recording, "addObjectsToShallowArray", byte[].class, "object", LeakObject.class, -1) < 1) { + throw new Exception("Could not find shallow leak object"); + } + } + } + + private static void addObjectsToShallowArray(List list) { + for (int i = 0; i < OldObjects.MIN_SIZE; i++) { + if (i % LARGE_OBJECT_FACTOR == 0) { + // Triggers allocation outside TLAB path + list.add(new LeakObject(new byte[16384])); + } else { + // Triggers allocation in TLAB path + list.add(new LeakObject(new byte[10])); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestLargeRootSet.java 2019-02-08 18:33:34.060140174 +0300 @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2015, 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.event.oldobject; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k -XX:-FastTLABRefill jdk.jfr.event.oldobject.TestLargeRootSet + */ +public class TestLargeRootSet { + + private static final int THREAD_COUNT = 50; + + private static class RootThread extends Thread { + private final CyclicBarrier barrier; + private int maxDepth = OldObjects.MIN_SIZE / THREAD_COUNT; + + public List temporaries = new ArrayList<>(maxDepth); + + RootThread(CyclicBarrier cb) { + this.barrier = cb; + } + + public void run() { + buildRootObjects(); + } + + private void buildRootObjects() { + if (maxDepth-- > 0) { + // Allocate array to trigger sampling code path for interpreter / c1 + StackObject[] stackObject = new StackObject[0]; + temporaries.add(stackObject); // make sure object escapes + buildRootObjects(); + } else { + temporaries.clear(); + try { + barrier.await(); // wait for gc + barrier.await(); // wait for recording to be stopped + } catch (InterruptedException e) { + System.err.println("Thread was unexpected interrupted: " + e.getMessage()); + } catch (BrokenBarrierException e) { + System.err.println("Unexpected barrier exception: " + e.getMessage()); + } + return; + } + } + } + + private static class StackObject { + } + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + List threads = new ArrayList<>(); + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); + r.start(); + CyclicBarrier cb = new CyclicBarrier(THREAD_COUNT + 1); + for (int i = 0; i < THREAD_COUNT; i++) { + RootThread t = new RootThread(cb); + t.start(); + if (i % 10 == 0) { + // Give threads some breathing room before starting next batch + Thread.sleep(100); + } + threads.add(t); + } + cb.await(); + System.gc(); + r.stop(); + cb.await(); + List events = Events.fromRecording(r); + Events.hasEvents(events); + for (RecordedEvent e : events) { + RecordedObject ro = e.getValue("object"); + RecordedClass rc = ro.getValue("type"); + System.out.println(rc.getName()); + if (rc.getName().equals(StackObject[].class.getName())) { + return; // ok + } + } + Asserts.fail("Could not find root object"); + } + } +} + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestLastKnownHeapUsage.java 2019-02-08 18:33:34.212134882 +0300 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015, 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.event.oldobject; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k -XX:-FastTLABRefill jdk.jfr.event.oldobject.TestLastKnownHeapUsage + */ +public class TestLastKnownHeapUsage { + + public final static List heap = new ArrayList<>(); + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + long last = 0; + for (int i = 1; i <= 5; i++) { + long heapUsage = recordOldObjects(i); + System.out.println("Recording: " + i + " heapUsage=" + heapUsage); + if (heapUsage < last) { + throw new Exception("Unexpect decrease of heap usage"); + } + } + } + + private static long recordOldObjects(int index) throws IOException { + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); + r.start(); + System.gc(); + for (int i = 0; i < 20; i++) { + heap.add(new byte[1_000_000]); + } + System.gc(); + r.stop(); + Path p = Paths.get("recording-" + index + ".jfr"); + r.dump(p); + List events = RecordingFile.readAllEvents(p); + Events.hasEvents(events); + long max = 0; + for (RecordedEvent e : RecordingFile.readAllEvents(p)) { + long heapUsage = Events.assertField(e, "lastKnownHeapUsage").atLeast(0L).below(1_000_000_000L).getValue(); + max = Math.max(max, heapUsage); + } + return max; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestListenerLeak.java 2019-02-08 18:33:34.352130007 +0300 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 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.event.oldobject; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k jdk.jfr.event.oldobject.TestListenerLeak + */ +public class TestListenerLeak { + + private interface TestListener { + void onListen(); + } + + static class Stuff { + } + + static class ListenerThread extends Thread { + + private List stuff; + + public ListenerThread(List stuff) { + this.stuff = stuff; + } + + public void run() { + listener.add(new TestListener() { + @Override + public void onListen() { + System.out.println(stuff); + } + }); + } + } + + private static List listener = new ArrayList<>(); + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); + r.start(); + listenerLeak(); + r.stop(); + List events = Events.fromRecording(r); + if (OldObjects.countMatchingEvents(events, Stuff[].class, null, null, -1, "listenerLeak") == 0) { + throw new Exception("Could not find leak with " + Stuff[].class); + } + } + } + + private static void listenerLeak() throws InterruptedException { + List stuff = new ArrayList<>(OldObjects.MIN_SIZE); + for (int i = 0; i < OldObjects.MIN_SIZE; i++) { + // Allocate array to trigger sampling code path for interpreter / c1 + stuff.add(new Stuff[0]); + } + + ListenerThread t = new ListenerThread(stuff); + t.start(); + t.join(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestMetadataObject.java 2019-02-08 18:33:34.492125133 +0300 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 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.event.oldobject; + +/** + * Purpose of this class is to generate a stack trace + * that can be used in a different class loader + * to test metadata retention + * + */ +public final class TestMetadataObject { + public static Object startRecurse() { + return recurse(50); + } + + public static Object recurse(int depth) { + if (depth > 0) { + return recurse(depth - 1); + } else { + return makeMemoryLeak(); + } + } + + public static byte[] makeMemoryLeak() { + return new byte[OldObjects.MIN_SIZE]; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestMetadataRetention.java 2019-02-08 18:33:34.628120398 +0300 @@ -0,0 +1,189 @@ +/* + * 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.event.oldobject; + +import java.time.Instant; +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedClassLoader; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.jfr.consumer.RecordedThread; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.TestClassLoader; + +/** + * @test + * @summary The test verifies that an old object sample maintains references to "stale" metadata + * + * + * @key jfr + * + * @library /lib / + * @build jdk.jfr.event.oldobject.TestMetadataObject + * @run main/othervm -XX:TLABSize=2k -Xmx16m jdk.jfr.event.oldobject.TestMetadataRetention + */ +public final class TestMetadataRetention { + private final static String TEST_PACKAGE = TestMetadataRetention.class.getPackage().getName(); + private final static String TEST_CLASS_LOADER_NAME = "jdk/test/lib/jfr/TestClassLoader"; // see TestClassLoader.java, was "JFR TestClassLoader"; + private final static String TEST_CLASS_NAME = TEST_PACKAGE + ".TestMetadataObject"; + private final static String ALLOCATOR_THREAD_NAME = "TestAllocationThread"; + + public static ClassLoader testClassLoader; + public static Object leakObject; + public static Thread allocatorThread; + public static Class testClass; + + static class ChunkRotation extends Event { + } + + public static void main(String[] args) throws Throwable { + WhiteBox.setWriteAllObjectSamples(true); + int failedAttempts = 0; + while (true) { + try (Recording recording = new Recording()) { + recording.enable(EventNames.OldObjectSample).withStackTrace(); + recording.enable(EventNames.ClassUnload); + recording.start(); + + // Setup metadata on the Java heap (class, stack trace and thread) + testClassLoader = new TestClassLoader(); + testClass = testClassLoader.loadClass(TEST_CLASS_NAME); + allocatorThread = new Thread(TestMetadataRetention::allocateLeak, ALLOCATOR_THREAD_NAME); + allocatorThread.start(); + allocatorThread.join(); + + // Clear out metadata on the heap + testClassLoader = null; + testClass = null; + allocatorThread = null; + + // System.gc() will trigger class unloading if -XX:+ExplicitGCInvokesConcurrent + // is NOT set. If this flag is set G1 will never unload classes on System.gc() + // and CMS will not guarantee that all semantically dead classes will be + // unloaded. As far as the "jfr" key guarantees no VM flags are set from the + // outside it should be enough with System.gc(). + System.gc(); + + // Provoke a chunk rotation, which will flush out ordinary metadata. + provokeChunkRotation(); + ChunkRotation cr = new ChunkRotation(); + cr.commit(); + recording.stop(); + + List events = Events.fromRecording(recording); + RecordedEvent chunkRotation = findChunkRotationEvent(events); + try { + // Sanity check that class was unloaded + Events.hasEvent(recording, EventNames.ClassUnload); + validateClassUnloadEvent(events); + // Validate that metadata for old object event has survived chunk rotation + Events.hasEvent(recording, EventNames.OldObjectSample); + validateOldObjectEvent(events, chunkRotation.getStartTime()); + } catch (Throwable t) { + t.printStackTrace(); + System.err.println("Number of failed attempts " + ++failedAttempts); + continue; + } + break; + } + } + } + + private static RecordedEvent findChunkRotationEvent(List events) { + for (RecordedEvent e : events) { + if (e.getEventType().getName().equals(ChunkRotation.class.getName())) { + return e; + } + } + Asserts.fail("Could not find chunk rotation event"); + return null; // Can't happen; + } + + private static void allocateLeak() { + try { + leakObject = testClass.getMethod("startRecurse").invoke(null); + } catch (Exception e) { + System.out.println("Could not allocate memory leak!"); + e.printStackTrace(); + } + } + + private static void provokeChunkRotation() { + try (Recording r = new Recording()) { + r.start(); + r.stop(); + } + } + + private static void validateClassUnloadEvent(List events) throws Throwable { + for (RecordedEvent event : events) { + if (event.getEventType().getName().equals(EventNames.ClassUnload)) { + RecordedClass unloadedClass = event.getValue("unloadedClass"); + if (TEST_CLASS_NAME.equals(unloadedClass.getName())) { + RecordedClassLoader definingClassLoader = unloadedClass.getClassLoader(); + Asserts.assertEquals(TEST_CLASS_LOADER_NAME, definingClassLoader.getName(), "Expected " + TEST_CLASS_LOADER_NAME + ", got " + definingClassLoader.getType().getName()); + return; + } + } + } + } + + private static void validateOldObjectEvent(List events, Instant chunkRotation) throws Throwable { + for (RecordedEvent event : events) { + if (event.getEventType().getName().equals(EventNames.OldObjectSample)) { + // Only check event after the rotation + if (!event.getStartTime().isBefore(chunkRotation)) { + System.out.println(event); + RecordedThread rt = event.getThread(); + if (rt.getJavaName().equals(ALLOCATOR_THREAD_NAME)) { + RecordedStackTrace s = event.getStackTrace(); + assertStackTrace(s, "startRecurse"); + assertStackTrace(s, "recurse"); + return; + } + } + } + } + + Asserts.fail("Did not find an old object event with thread " + ALLOCATOR_THREAD_NAME); + } + + private static void assertStackTrace(RecordedStackTrace stacktrace, final String methodName) { + for (RecordedFrame f : stacktrace.getFrames()) { + if (f.getMethod().getName().equals(methodName)) { + return; + } + } + Asserts.fail("Could not find class " + methodName + " in stack trace " + stacktrace); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestObjectDescription.java 2019-02-08 18:33:34.768115523 +0300 @@ -0,0 +1,216 @@ +/* + * Copyright (c) 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.event.oldobject; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Callable; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k jdk.jfr.event.oldobject.TestObjectDescription + */ +public class TestObjectDescription { + + private static final int OBJECT_DESCRIPTION_MAX_SIZE = 100; + private static final String CLASS_NAME = TestClassLoader.class.getName() + "$TestClass"; + public static List leaks; + + public final static class MyThreadGroup extends ThreadGroup { + public final static String NAME = "My Thread Group"; + + public MyThreadGroup(String name) { + super(name); + } + + // Allocate array to trigger sampling code path for interpreter / c1 + byte[] bytes = new byte[10]; + } + + public final static class MyThread extends Thread { + public final static String NAME = "My Thread"; + + public MyThread() { + super(NAME); + } + + // Allocate array to trigger sampling code path for interpreter / c1 + byte[] bytes = new byte[10]; + } + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + testThreadGroupName(); + testThreadName(); + testClassName(); + testSize(); + testEllipsis(); + } + + private static void testThreadName() throws Exception { + asseertObjectDescription(() -> { + List threads = new ArrayList<>(OldObjects.MIN_SIZE); + for (int i = 0; i < OldObjects.MIN_SIZE; i++) { + threads.add(new MyThread()); + } + return threads; + }, "Thread Name: " + MyThread.NAME); + } + + private static void testThreadGroupName() throws Exception { + asseertObjectDescription(() -> { + List groups = new ArrayList<>(OldObjects.MIN_SIZE); + for (int i = 0; i < OldObjects.MIN_SIZE; i++) { + groups.add(new MyThreadGroup("My Thread Group")); + } + return groups; + }, "Thread Group: " + "My Thread Group"); + } + + private static void testClassName() throws Exception { + asseertObjectDescription(() -> { + TestClassLoader testClassLoader = new TestClassLoader(); + List classObjects = new ArrayList<>(OldObjects.MIN_SIZE); + for (Class clazz : testClassLoader.loadClasses(OldObjects.MIN_SIZE / 20)) { + // Allocate array to trigger sampling code path for interpreter / c1 + for (int i = 0; i < 20; i++) { + Object classArray = Array.newInstance(clazz, 20); + Array.set(classArray, i, clazz.newInstance()); + classObjects.add(classArray); + } + } + return classObjects; + }, "Class Name: " + CLASS_NAME); + } + + private static void testSize() throws Exception { + asseertObjectDescription(() -> { + List arrayLists = new ArrayList<>(OldObjects.MIN_SIZE); + for (int i = 0; i < OldObjects.MIN_SIZE; i++) { + List arrayList = new ArrayList<>(); + arrayList.add(new Object()); + arrayList.add(new Object()); + arrayList.add(new Object()); + arrayLists.add(arrayList); + } + return arrayLists; + }, "Size: 3"); + } + + private static void testEllipsis() throws Exception { + asseertObjectDescription(() -> { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 2 * OBJECT_DESCRIPTION_MAX_SIZE; i++) { + sb.append("x"); + } + String threadName = sb.toString(); + List threads = new ArrayList<>(); + for (int i = 0; i < OldObjects.MIN_SIZE; i++) { + threads.add(new Thread(threadName)); + } + return threads; + }, "xxx..."); + } + + private static void asseertObjectDescription(Callable> callable, String text) throws Exception { + int iteration = 1; + while (true) { + try (Recording recording = new Recording()) { + System.out.println("Iteration: " + iteration); + recording.enable(EventNames.OldObjectSample).withoutStackTrace().with("cutoff", "infinity"); + recording.start(); + leaks = null; + System.gc(); + leaks = callable.call(); + + recording.stop(); + + List events = Events.fromRecording(recording); + Set objectDescriptions = extractObjectDecriptions(events); + for (String s : objectDescriptions) { + if (s.contains(text)) { + printDescriptions(objectDescriptions); + return; + } + } + System.out.println("Could not find object description containing text \"" + text + "\""); + printDescriptions(objectDescriptions); + System.out.println(); + iteration++; + } + } + } + + private static void printDescriptions(Set objectDescriptions) { + System.out.println("Found descriptions:"); + for (String t : objectDescriptions) { + System.out.println(t); + } + } + + private static Set extractObjectDecriptions(List events) { + Set objectDescriptions = new HashSet<>(); + for (RecordedEvent e : events) { + objectDescriptions.addAll(extractObjectDescriptions(e.getValue("object"))); + } + return objectDescriptions; + } + + private static Set extractObjectDescriptions(RecordedObject o) { + Set visited = new HashSet<>(); + Set descriptions = new HashSet<>(); + while (o != null) { + Long memoryAddress = o.getValue("address"); + if (visited.contains(memoryAddress)) { + return descriptions; + } + visited.add(memoryAddress); + String od = o.getValue("description"); + if (od != null) { + descriptions.add(od); + } + RecordedObject referrer = o.getValue("referrer"); + o = referrer != null ? referrer.getValue("object") : null; + } + return descriptions; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestParallel.java 2019-02-08 18:33:34.904110789 +0300 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 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.event.oldobject; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @summary Test leak profiler with Parallel GC + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k -XX:+UseParallelGC jdk.jfr.event.oldobject.TestParallel + */ +public class TestParallel { + + static private class FindMe { + } + + public static List list = new ArrayList<>(OldObjects.MIN_SIZE); + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); + r.start(); + allocateFindMe(); + System.gc(); + r.stop(); + List events = Events.fromRecording(r); + System.out.println(events); + if (OldObjects.countMatchingEvents(events, FindMe[].class, null, null, -1, "allocateFindMe") == 0) { + throw new Exception("Could not find leak with " + FindMe[].class); + } + } + } + + public static void allocateFindMe() { + for (int i = 0; i < OldObjects.MIN_SIZE; i++) { + // Allocate array to trigger sampling code path for interpreter / c1 + list.add(new FindMe[0]); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestParallelOld.java 2019-02-08 18:33:35.044105914 +0300 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 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.event.oldobject; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @summary Test leak profiler with Parallel Old GC + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k -XX:+UseParallelOldGC jdk.jfr.event.oldobject.TestParallelOld + */ +public class TestParallelOld { + + static private class FindMe { + } + + public static List list = new ArrayList<>(OldObjects.MIN_SIZE); + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); + r.start(); + allocateFindMe(); + System.gc(); + r.stop(); + List events = Events.fromRecording(r); + System.out.println(events); + if (OldObjects.countMatchingEvents(events, FindMe[].class, null, null, -1, "allocateFindMe") == 0) { + throw new Exception("Could not find leak with " + FindMe[].class); + } + } + } + + public static void allocateFindMe() { + for (int i = 0; i < OldObjects.MIN_SIZE; i++) { + // Allocate array to trigger sampling code path for interpreter / c1 + list.add(new FindMe[0]); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestReferenceChainLimit.java 2019-02-08 18:33:35.184101040 +0300 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2015, 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.event.oldobject; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k jdk.jfr.event.oldobject.TestReferenceChainLimit + */ +public class TestReferenceChainLimit { + private final static int TEST_CHAIN_LENGTH_FACTOR = 10; // scaling factor for the how long chain to be used in the test + + final static class ChainNode { + final ChainNode next; + byte[] leak; + + public ChainNode(ChainNode node) { + next = node; + leak = new byte[10_000]; + } + } + + private static ChainNode createChain() { + ChainNode node = null; + final int testChainLength = OldObjects.MAX_CHAIN_LENGTH * TEST_CHAIN_LENGTH_FACTOR; + for (int i = 0; i < testChainLength; i++) { + node = new ChainNode(node); + } + return node; + } + + public static ChainNode leak; + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); + r.start(); + leak = createChain(); + List events = Events.fromRecording(r); + if (OldObjects.countMatchingEvents(events, byte[].class, null, null, -1, "createChain") == 0) { + throw new Exception("Could not find ChainNode"); + } + for (RecordedEvent e : events) { + OldObjects.validateReferenceChainLimit(e, OldObjects.MAX_CHAIN_LENGTH); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestSanityDefault.java 2019-02-08 18:33:35.324096166 +0300 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 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.event.oldobject; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * @summary Purpose of this test is to run leak profiler without command line tweaks or WhiteBox hacks until we succeed + * @run main/othervm jdk.jfr.event.oldobject.TestSanityDefault + */ +public class TestSanityDefault { + + static private class FindMe { + } + + public static List list = new ArrayList<>(OldObjects.MIN_SIZE); + + public static void main(String[] args) throws Exception { + // Should not use WhiteBox API, we want to execute actual code paths + + // Trigger c2 compilation, so we get sample + for (long i = 0; i < 100_000_000; i++) { + allocateFindMe(true); + } + + + // It's hard to get samples with interpreter / C1 so loop until we do + while (true) { + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); + r.start(); + allocateFindMe(false); + System.gc(); + r.stop(); + List events = Events.fromRecording(r); + if (OldObjects.countMatchingEvents(events, FindMe.class, null, null, -1, "allocateFindMe") > 0) { + return; + } + // no events found, retry + } + } + } + + public static void allocateFindMe(boolean doNothing) { + if (doNothing) { + return; + } + for (int i = 0; i < OldObjects.MIN_SIZE; i++) { + // Purposely don't allocate array, so we at least + // in one old-object test check an ordinary object. + list.add(new FindMe()); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestSerial.java 2019-02-08 18:33:35.460091431 +0300 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 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.event.oldobject; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * + * @summary Test leak profiler with Serial GC + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k -XX:+UseSerialGC jdk.jfr.event.oldobject.TestSerial + */ +public class TestSerial { + + static private class FindMe { + } + + public static List list = new ArrayList<>(OldObjects.MIN_SIZE); + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); + r.start(); + allocateFindMe(); + System.gc(); + r.stop(); + List events = Events.fromRecording(r); + System.out.println(events); + if (OldObjects.countMatchingEvents(events, FindMe[].class, null, null, -1, "allocateFindMe") == 0) { + throw new Exception("Could not find leak with " + FindMe[].class); + } + } + } + + public static void allocateFindMe() { + for (int i = 0; i < OldObjects.MIN_SIZE; i++) { + // Allocate array to trigger sampling code path for interpreter / c1 + list.add(new FindMe[0]); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/oldobject/TestThreadLocalLeak.java 2019-02-08 18:33:35.600086557 +0300 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 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.event.oldobject; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * + * @run main/othervm -XX:TLABSize=2k jdk.jfr.event.oldobject.TestThreadLocalLeak + */ +public class TestThreadLocalLeak { + + private static ThreadLocal> threadLocal = new ThreadLocal>() { + @Override + public List initialValue() { + return new ArrayList(OldObjects.MIN_SIZE); + } + }; + + static class ThreadLocalObject { + } + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + try (Recording r = new Recording()) { + r.enable(EventNames.OldObjectSample).withStackTrace().with("cutoff", "infinity"); + r.start(); + allocateThreadLocal(); + r.stop(); + List events = Events.fromRecording(r); + if (OldObjects.countMatchingEvents(events, ThreadLocalObject[].class, null, null, -1, "allocateThreadLocal") == 0) { + throw new Exception("Could not find thread local object " + ThreadLocalObject.class); + } + } + } + + private static void allocateThreadLocal() { + for (int i = 0; i < OldObjects.MIN_SIZE; i++) { + // Allocate array to trigger sampling code path for interpreter / c1 + threadLocal.get().add(new ThreadLocalObject[0]); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/os/TestCPUInformation.java 2019-02-08 18:33:35.740081683 +0300 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013, 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.event.os; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.os.TestCPUInformation + */ +public class TestCPUInformation { + private final static String EVENT_NAME = EventNames.CPUInformation; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event: " + event); + Events.assertField(event, "hwThreads").atLeast(1); + Events.assertField(event, "cores").atLeast(1); + Events.assertField(event, "sockets").atLeast(1); + Events.assertField(event, "cpu").containsAny("Intel", "AMD", "Unknown x86", "sparc", "ARM", "PPC", "PowerPC", "AArch64", "s390"); + Events.assertField(event, "description").containsAny("Intel", "AMD", "Unknown x86", "SPARC", "ARM", "PPC", "PowerPC", "AArch64", "zArch"); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/os/TestCPULoad.java 2019-02-08 18:33:35.876076948 +0300 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2013, 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.event.os; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.os.TestCPULoad + */ +public class TestCPULoad { + private final static String EVENT_NAME = EventNames.CPULoad; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + // Need to sleep so a time delta can be calculated + Thread.sleep(100); + recording.stop(); + + List events = Events.fromRecording(recording); + if (events.isEmpty()) { + // CPU Load events are unreliable on Windows because + // the way processes are identified with perf. counters. + // See BUG 8010378. + // Workaround is to detect Windows and allow + // test to pass if events are missing. + if (isWindows()) { + return; + } + throw new AssertionError("Expected at least one event"); + } + for (RecordedEvent event : events) { + System.out.println("Event: " + event); + for (String loadName : loadNames) { + Events.assertField(event, loadName).atLeast(0.0f).atMost(1.0f); + } + } + } + + private static final String[] loadNames = {"jvmUser", "jvmSystem", "machineTotal"}; + + private static boolean isWindows() { + return System.getProperty("os.name").startsWith("Windows"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/os/TestCPUTimeStampCounter.java 2019-02-08 18:33:36.012072213 +0300 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013, 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.event.os; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.os.TestCPUTimeStampCounter + */ +public class TestCPUTimeStampCounter { + private final static String EVENT_NAME = EventNames.CPUTimeStampCounter; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event: " + event); + Events.assertField(event, "fastTimeEnabled"); + Events.assertField(event, "fastTimeAutoEnabled"); + Events.assertField(event, "osFrequency").atLeast(0L); + Events.assertField(event, "fastTimeFrequency").atLeast(0L); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/os/TestInitialEnvironmentVariable.java 2019-02-08 18:33:36.148067479 +0300 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013, 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.event.os; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertTrue; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +public class TestInitialEnvironmentVariable { + private final static String EVENT_NAME = EventNames.InitialEnvironmentVariable; + + public static void main(String[] args) throws Exception { + Map env = new HashMap<>(); + env.put("keytest1", "value1"); + env.put("keytest2", "value 2"); + + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event: " + event); + String key = Events.assertField(event, "key").notNull().getValue(); + String value = Events.assertField(event, "value").notNull().getValue(); + if (env.containsKey(key)) { + assertEquals(value, env.get(key), "Wrong value for key: " + key); + env.remove(key); + } + } + assertTrue(env.isEmpty(), "Missing env keys " + env.keySet()); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/os/TestInitialEnvironmentVariable.sh 2019-02-08 18:33:36.292062466 +0300 @@ -0,0 +1,38 @@ +# +# Copyright (c) 2013, 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. +# +# 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. +# +# @test +# @key jfr +# +# @library /lib / +# @build jdk.jfr.event.os.TestInitialEnvironmentVariable +# @run shell TestInitialEnvironmentVariable.sh + +echo ------------------------------------------------------------- +echo "TESTCLASSES='$TESTCLASSES'" +echo "TESTSRC='$TESTSRC'" +echo Launching test for `basename $0 .sh` +echo ------------------------------------------------------------- +keytest1="value1";export keytest1 +keytest2="value 2";export keytest2 +${TESTJAVA}/bin/java -cp ${TESTCLASSPATH} jdk.jfr.event.os.TestInitialEnvironmentVariable +exit $? --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/os/TestOSInfo.java 2019-02-08 18:33:36.432057592 +0300 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013, 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.event.os; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.os.TestOSInfo + */ +public class TestOSInfo { + private final static String EVENT_NAME = EventNames.OSInformation; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event: " + event); + Events.assertField(event, "osVersion").notEmpty(); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/os/TestPhysicalMemoryEvent.java 2019-02-08 18:33:36.572052719 +0300 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2013, 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.event.os; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.os.TestPhysicalMemoryEvent + */ +public class TestPhysicalMemoryEvent { + private final static String EVENT_NAME = EventNames.PhysicalMemory; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event: " + event); + long totalSize = Events.assertField(event, "totalSize").atLeast(0L).getValue(); + Events.assertField(event, "usedSize").atLeast(0L).atMost(totalSize); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/os/TestSystemProcess.java 2019-02-08 18:33:36.708047984 +0300 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013, 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.event.os; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.os.TestSystemProcess + */ +public class TestSystemProcess { + private final static String EVENT_NAME = EventNames.SystemProcess; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event: " + event); + Events.assertField(event, "pid").notEmpty(); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/os/TestThreadContextSwitches.java 2019-02-08 18:33:36.844043250 +0300 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013, 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.event.os; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.os.TestThreadContextSwitches + */ +public class TestThreadContextSwitches { + private final static String EVENT_NAME = EventNames.ThreadContextSwitchRate; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event: " + event); + Events.assertField(event, "switchRate").atLeast(0.0f); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/profiling/TestFullStackTrace.java 2019-02-08 18:33:36.984038376 +0300 @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2013, 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.event.profiling; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.RecurseThread; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.profiling.TestFullStackTrace + */ +public class TestFullStackTrace { + private final static String EVENT_NAME = EventNames.ExecutionSample; + private final static int MAX_DEPTH = 64; // currently hardcoded in jvm + + public static void main(String[] args) throws Throwable { + RecurseThread[] threads = new RecurseThread[3]; + for (int i = 0; i < threads.length; ++i) { + int depth = MAX_DEPTH - 1 + i; + threads[i] = new RecurseThread(depth); + threads[i].setName("recursethread-" + depth); + threads[i].start(); + } + + for (RecurseThread thread : threads) { + while (!thread.isInRunLoop()) { + Thread.sleep(20); + } + } + + assertStackTraces(threads); + + for (RecurseThread thread : threads) { + thread.quit(); + thread.join(); + } + } + + private static void assertStackTraces( RecurseThread[] threads) throws Throwable { + Recording recording= null; + do { + recording = new Recording(); + recording.enable(EVENT_NAME).withPeriod(Duration.ofMillis(50)); + recording.start(); + Thread.sleep(500); + recording.stop(); + } while (!hasValidStackTraces(recording, threads)); + } + + private static boolean hasValidStackTraces(Recording recording, RecurseThread[] threads) throws Throwable { + boolean[] isEventFound = new boolean[threads.length]; + + for (RecordedEvent event : Events.fromRecording(recording)) { + //System.out.println("Event: " + event); + String threadName = Events.assertField(event, "sampledThread.javaName").getValue(); + long threadId = Events.assertField(event, "sampledThread.javaThreadId").getValue(); + + for (int threadIndex = 0; threadIndex < threads.length; ++threadIndex) { + RecurseThread currThread = threads[threadIndex]; + if (threadId == currThread.getId()) { + System.out.println("ThreadName=" + currThread.getName() + ", depth=" + currThread.totalDepth); + Asserts.assertEquals(threadName, currThread.getName(), "Wrong thread name"); + if ("recurseEnd".equals(getTopMethodName(event))) { + isEventFound[threadIndex] = true; + checkEvent(event, currThread.totalDepth); + break; + } + } + } + } + + for (int i = 0; i < threads.length; ++i) { + String msg = "threadIndex=%d, recurseDepth=%d, isEventFound=%b%n"; + System.out.printf(msg, i, threads[i].totalDepth, isEventFound[i]); + } + for (int i = 0; i < threads.length; ++i) { + if(!isEventFound[i]) { + // no assertion, let's retry. + // Could be race condition, i.e safe point during Thread.sleep + System.out.println("Falied to validate all threads, will retry."); + return false; + } + } + return true; + } + + public static String getTopMethodName(RecordedEvent event) { + List frames = event.getStackTrace().getFrames(); + Asserts.assertFalse(frames.isEmpty(), "JavaFrames was empty"); + return frames.get(0).getMethod().getName(); + } + + private static void checkEvent(RecordedEvent event, int expectedDepth) throws Throwable { + RecordedStackTrace stacktrace = null; + try { + stacktrace = event.getStackTrace(); + List frames = stacktrace.getFrames(); + Asserts.assertEquals(Math.min(MAX_DEPTH, expectedDepth), frames.size(), "Wrong stacktrace depth. Expected:" + expectedDepth); + List expectedMethods = getExpectedMethods(expectedDepth); + Asserts.assertEquals(expectedMethods.size(), frames.size(), "Wrong expectedMethods depth. Test error."); + + for (int i = 0; i < frames.size(); ++i) { + String name = frames.get(i).getMethod().getName(); + String expectedName = expectedMethods.get(i); + System.out.printf("method[%d]=%s, expected=%s%n", i, name, expectedName); + Asserts.assertEquals(name, expectedName, "Wrong method name"); + } + + boolean isTruncated = stacktrace.isTruncated(); + boolean isTruncateExpected = expectedDepth > MAX_DEPTH; + Asserts.assertEquals(isTruncated, isTruncateExpected, "Wrong value for isTruncated. Expected:" + isTruncateExpected); + + String firstMethod = frames.get(frames.size() - 1).getMethod().getName(); + boolean isFullTrace = "run".equals(firstMethod); + String msg = String.format("Wrong values for isTruncated=%b, isFullTrace=%b", isTruncated, isFullTrace); + Asserts.assertTrue(isTruncated != isFullTrace, msg); + } catch (Throwable t) { + System.out.println(String.format("stacktrace:%n%s", stacktrace)); + throw t; + } + } + + private static List getExpectedMethods(int depth) { + List methods = new ArrayList<>(); + methods.add("recurseEnd"); + for (int i = 0; i < depth - 2; ++i) { + methods.add((i % 2) == 0 ? "recurseA" : "recurseB"); + } + methods.add("run"); + if (depth > MAX_DEPTH) { + methods = methods.subList(0, MAX_DEPTH); + } + return methods; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestActiveRecordingEvent.java 2019-02-08 18:33:37.120033642 +0300 @@ -0,0 +1,126 @@ +/* + * 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.event.runtime; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertNull; +import static jdk.test.lib.Asserts.assertTrue; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.time.Instant; +import java.util.List; + +import jdk.jfr.EventType; +import jdk.jfr.Recording; +import jdk.jfr.Timespan; +import jdk.jfr.Timestamp; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Tests that the recording properties are properly reflected in the ActiveRecording event + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.runtime.TestActiveRecordingEvent + */ +public final class TestActiveRecordingEvent { + + private static final String ACTIVE_RECORDING_EVENT_NAME = EventNames.ActiveRecording; + + private static final Path MY_JFR_FILEPATH = Paths.get("", "my.jfr"); + + private static final long MAX_SIZE = 1000000L; + + private static final Duration MAX_AGE = Duration.ofDays(1); + + private static final Duration REC_DURATION = Duration.ofMinutes(10); + + private static final String REC_NAME = "MYNAME"; + + public static void main(String[] args) throws Throwable { + testWithPath(null); + testWithPath(MY_JFR_FILEPATH); + } + + private static void testWithPath(Path path) throws Throwable { + Recording recording = new Recording(); + recording.enable(ACTIVE_RECORDING_EVENT_NAME); + + recording.setDuration(REC_DURATION); + recording.setMaxSize(MAX_SIZE); + recording.setMaxAge(MAX_AGE); + recording.setName(REC_NAME); + if (path != null) { + recording.setToDisk(true); + recording.setDestination(path); + } + + long tsBeforeStart = Instant.now().toEpochMilli(); + recording.start(); + recording.stop(); + long tsAfterStop = Instant.now().toEpochMilli(); + + List events = Events.fromRecording(recording); + + Events.hasEvents(events); + RecordedEvent ev = events.get(0); + + // Duration must be kept in milliseconds + assertEquals(REC_DURATION.toMillis(), ev.getValue("recordingDuration")); + + assertEquals(MAX_SIZE, ev.getValue("maxSize")); + + // maxAge must be kept in milliseconds + assertEquals(MAX_AGE.toMillis(), ev.getValue("maxAge")); + + EventType evType = ev.getEventType(); + ValueDescriptor durationField = evType.getField("recordingDuration"); + assertEquals(durationField.getAnnotation(Timespan.class).value(), Timespan.MILLISECONDS); + + if (path == null) { + assertNull(ev.getValue("destination")); + } else { + assertEquals(path.toAbsolutePath().toString(), ev.getValue("destination").toString()); + } + + ValueDescriptor recordingStartField = evType.getField("recordingStart"); + assertEquals(recordingStartField.getAnnotation(Timestamp.class).value(), Timestamp.MILLISECONDS_SINCE_EPOCH); + + long tsRecordingStart = ev.getValue("recordingStart"); + assertTrue(tsBeforeStart <= tsRecordingStart); + assertTrue(tsAfterStop >= tsRecordingStart); + + assertEquals(recording.getId(), ev.getValue("id")); + + ValueDescriptor maxAgeField = evType.getField("maxAge"); + assertEquals(maxAgeField.getAnnotation(Timespan.class).value(), Timespan.MILLISECONDS); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestActiveSettingEvent.java 2019-02-08 18:33:37.260028768 +0300 @@ -0,0 +1,253 @@ +/* + * 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.event.runtime; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.Configuration; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.Registered; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Tests that active setting are available in the ActiveSettingevent + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.runtime.TestActiveSettingEvent + */ +public final class TestActiveSettingEvent { + + private static class MyEvent extends Event { + } + + @Registered(false) + private static class MyRegistrationEvent extends Event { + } + + private static final String ACTIVE_SETTING_EVENT_NAME = EventNames.ActiveSetting; + + public static void main(String[] args) throws Throwable { + testDefaultSettings();; + testProfileSettings();; + testNewSettings(); + testChangedSetting(); + testUnregistered(); + testRegistration(); + } + + private static void testProfileSettings() throws Exception { + testSettingConfiguration("profile"); + } + + private static void testDefaultSettings() throws Exception { + testSettingConfiguration("default"); + } + + private static void testRegistration() throws Exception { + // Register new + try (Recording recording = new Recording()) { + recording.enable(ACTIVE_SETTING_EVENT_NAME); + recording.start(); + FlightRecorder.register(MyRegistrationEvent.class); + recording.stop(); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + EventType type = EventType.getEventType(MyRegistrationEvent.class); + assertSetting(events, type, "threshold", "0 ns"); + assertSetting(events, type, "enabled", "true"); + assertSetting(events, type, "stackTrace", "true"); + } + // Register unregistered + FlightRecorder.unregister(MyEvent.class); + try (Recording recording = new Recording()) { + recording.enable(ACTIVE_SETTING_EVENT_NAME); + recording.start(); + FlightRecorder.register(MyRegistrationEvent.class); + recording.stop(); + EventType type = EventType.getEventType(MyRegistrationEvent.class); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + type = EventType.getEventType(MyRegistrationEvent.class); + assertSetting(events, type, "threshold", "0 ns"); + assertSetting(events, type, "enabled", "true"); + assertSetting(events, type, "stackTrace", "true"); + } + } + + private static void testUnregistered() throws Exception { + FlightRecorder.register(MyEvent.class); + EventType type = EventType.getEventType(MyEvent.class); + FlightRecorder.unregister(MyEvent.class); + try (Recording recording = new Recording()) { + recording.enable(ACTIVE_SETTING_EVENT_NAME); + recording.start(); + MyEvent m = new MyEvent(); + m.commit(); + recording.stop(); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + assertNotSetting(events, type, "threshold", "0 ns"); + assertNotSetting(events, type, "enabled", "true"); + assertNotSetting(events, type, "stackTrace", "true"); + } + } + + private static void testNewSettings() throws Exception { + try (Recording recording = new Recording()) { + recording.enable(ACTIVE_SETTING_EVENT_NAME); + recording.start(); + MyEvent m = new MyEvent(); + m.commit(); + recording.stop(); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + EventType type = EventType.getEventType(MyEvent.class); + assertSetting(events, type, "threshold", "0 ns"); + assertSetting(events, type, "enabled", "true"); + assertSetting(events, type, "stackTrace", "true"); + assertNotSetting(events, type, "period", "everyChunk"); + } + } + + private static void testChangedSetting() throws Exception { + EventType type = EventType.getEventType(MyEvent.class); + Map base = new HashMap<>(); + base.put(ACTIVE_SETTING_EVENT_NAME + "#enabled", "true"); + try (Recording recording = new Recording()) { + recording.setSettings(base); + recording.start(); + Map newS = new HashMap<>(base); + newS.put(type.getName() + "#enabled", "true"); + newS.put(type.getName() + "#threshold", "11 ns"); + recording.setSettings(newS); + recording.stop(); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + assertSetting(events, type, "threshold", "0 ns"); // initial value + assertSetting(events, type, "enabled", "true"); + assertSetting(events, type, "threshold", "11 ns"); // changed value + } + } + + private static void assertSetting(List events, EventType evenType, String settingName, String settingValue) throws Exception { + if (!hasSetting(events, evenType, settingName, settingValue)) { + throw new Exception("Could not find setting " + settingName + " with value " + settingValue + " for event type " + evenType.getName()); + } + } + + private static void assertNotSetting(List events, EventType evenType, String settingName, String settingValue) throws Exception { + if (hasSetting(events, evenType, settingName, settingValue)) { + throw new Exception("Found unexpected setting " + settingName + " with value " + settingValue + " for event type " + evenType.getName()); + } + } + + private static boolean hasSetting(List events, EventType evenType, String settingName, String settingValue) throws Exception { + for (RecordedEvent e : events) { + if (e.getEventType().getName().equals(ACTIVE_SETTING_EVENT_NAME)) { + String name = e.getValue("name"); + String value = e.getValue("value"); + Long id = e.getValue("id"); + if (evenType.getId() == id && name.equals(settingName) && settingValue.equals(value)) { + return true; + } + } + } + return false; + } + + private static void testSettingConfiguration(String configurationName) throws Exception { + System.out.println("Testing configuration " + configurationName); + Configuration c = Configuration.getConfiguration(configurationName); + Map settingValues = c.getSettings(); + // Don't want to add these settings to the jfc-files we ship since they + // are not useful to configure. They are however needed to make the test + // pass. + settingValues.put(EventNames.ActiveSetting + "#stackTrace", "false"); + settingValues.put(EventNames.ActiveSetting + "#threshold", "0 ns"); + settingValues.put(EventNames.ActiveRecording + "#stackTrace", "false"); + settingValues.put(EventNames.ActiveRecording + "#threshold", "0 ns"); + settingValues.put(EventNames.JavaExceptionThrow + "#threshold", "0 ns"); + settingValues.put(EventNames.JavaErrorThrow + "#threshold", "0 ns"); + + try (Recording recording = new Recording(c)) { + Map eventTypes = new HashMap<>(); + for (EventType et : FlightRecorder.getFlightRecorder().getEventTypes()) { + eventTypes.put(et.getId(), et); + } + recording.start(); + Map expectedSettings = new HashMap<>(); + for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) { + for (SettingDescriptor s : type.getSettingDescriptors()) { + String settingName = type.getName() + "#" + s.getName(); + String value = settingValues.get(settingName); + if (value == null) { + throw new Exception("Could not find setting with name " + settingName); + } + // Prefer to have ms unit in jfc file + if (value.equals("0 ms")) { + value = "0 ns"; + } + expectedSettings.put(settingName, value); + } + } + recording.stop(); + + for (RecordedEvent e : Events.fromRecording(recording)) { + if (e.getEventType().getName().equals(ACTIVE_SETTING_EVENT_NAME)) { + Long id = e.getValue("id"); + EventType et = eventTypes.get(id); + if (et == null) { + throw new Exception("Could not find event type with id " + id); + } + String name = e.getValue("name"); + String settingName = et.getName() + "#" + name; + String value = e.getValue("value"); + String expectedValue = expectedSettings.get(settingName); + if (expectedValue != null) { + if (value.equals("0 ms")) { + value = "0 ns"; + } + Asserts.assertEquals(expectedValue, value, "Incorrect settings value for " + settingName + " was " + value + ", expected " + expectedValue); + expectedSettings.remove(settingName); + } + } + } + if (!expectedSettings.isEmpty()) { + throw new Exception("Not all setting in event. Missing " + expectedSettings.keySet()); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestBiasedLockRevocationEvents.java 2019-02-08 18:33:37.404023756 +0300 @@ -0,0 +1,319 @@ +/* + * 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.event.runtime; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.*; +import jdk.test.lib.Asserts; +import jdk.test.lib.dcmd.PidJcmdExecutor; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.process.OutputAnalyzer; + +import java.util.*; +import java.util.concurrent.FutureTask; +import java.util.stream.Collectors; + +/** + * @test + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.event.runtime.TestBiasedLockRevocationEvents + */ +public class TestBiasedLockRevocationEvents { + + public static void main(String[] args) throws Throwable { + testSingleRevocation(); + testBulkRevocation(); + testSelfRevocation(); + testExitedThreadRevocation(); + testBulkRevocationNoRebias(); + testRevocationSafepointIdCorrelation(); + } + + // Default value of BiasedLockingBulkRebiasThreshold is 20, and BiasedLockingBulkRevokeTreshold is 40. + // Using a value that will hit the first threshold once, and the second one the next time. + private static final int BULK_REVOKE_THRESHOLD = 25; + + static void touch(Object lock) { + synchronized(lock) { + } + } + + static Thread triggerRevocation(int numRevokes, Class lockClass) throws Throwable { + Object[] locks = new Object[numRevokes]; + for (int i = 0; i < locks.length; ++i) { + locks[i] = lockClass.getDeclaredConstructor().newInstance(); + touch(locks[i]); + } + + Thread biasBreaker = new Thread("BiasBreaker") { + @Override + public void run() { + for (Object lock : locks) { + touch(lock); + } + } + }; + + biasBreaker.start(); + biasBreaker.join(); + + return biasBreaker; + } + + // Basic stack trace validation, checking the name of the leaf method + static void validateStackTrace(RecordedStackTrace stackTrace, String leafMethodName) { + List frames = stackTrace.getFrames(); + Asserts.assertFalse(frames.isEmpty()); + String name = frames.get(0).getMethod().getName(); + Asserts.assertEquals(name, leafMethodName); + } + + // Validates that the given stack trace refers to lock.touch(); in triggerRevocation + static void validateStackTrace(RecordedStackTrace stackTrace) { + validateStackTrace(stackTrace, "touch"); + } + + // Retrieve all biased lock revocation events related to the provided lock class, sorted by start time + static List getRevocationEvents(Recording recording, String fieldName, Class lockClass) throws Throwable { + return Events.fromRecording(recording).stream() + .filter(e -> ((RecordedClass)e.getValue(fieldName)).getName().equals(lockClass.getName())) + .sorted(Comparator.comparing(RecordedEvent::getStartTime)) + .collect(Collectors.toList()); + } + + static void testSingleRevocation() throws Throwable { + class MyLock {}; + + Recording recording = new Recording(); + + recording.enable(EventNames.BiasedLockRevocation); + recording.start(); + + Thread biasBreaker = triggerRevocation(1, MyLock.class); + + recording.stop(); + List events = getRevocationEvents(recording, "lockClass", MyLock.class); + Asserts.assertEQ(events.size(), 1); + + RecordedEvent event = events.get(0); + Events.assertEventThread(event, biasBreaker); + Events.assertEventThread(event, "previousOwner", Thread.currentThread()); + + RecordedClass lockClass = event.getValue("lockClass"); + Asserts.assertEquals(lockClass.getName(), MyLock.class.getName()); + + validateStackTrace(event.getStackTrace()); + } + + static void testBulkRevocation() throws Throwable { + class MyLock {}; + + Recording recording = new Recording(); + + recording.enable(EventNames.BiasedLockClassRevocation); + recording.start(); + + Thread biasBreaker = triggerRevocation(BULK_REVOKE_THRESHOLD, MyLock.class); + + recording.stop(); + List events = getRevocationEvents(recording, "revokedClass", MyLock.class); + Asserts.assertEQ(events.size(), 1); + + RecordedEvent event = events.get(0); + Events.assertEventThread(event, biasBreaker); + Events.assertField(event, "disableBiasing").equal(false); + + RecordedClass lockClass = event.getValue("revokedClass"); + Asserts.assertEquals(lockClass.getName(), MyLock.class.getName()); + + validateStackTrace(event.getStackTrace()); + } + + static void testSelfRevocation() throws Throwable { + class MyLock {}; + + Recording recording = new Recording(); + + recording.enable(EventNames.BiasedLockSelfRevocation); + recording.start(); + + MyLock l = new MyLock(); + touch(l); + Thread.holdsLock(l); + + recording.stop(); + List events = getRevocationEvents(recording, "lockClass", MyLock.class); + Asserts.assertEQ(events.size(), 1); + + RecordedEvent event = events.get(0); + Events.assertEventThread(event, Thread.currentThread()); + + validateStackTrace(event.getStackTrace(), "holdsLock"); + } + + static void testExitedThreadRevocation() throws Throwable { + class MyLock {}; + + Recording recording = new Recording(); + + recording.enable(EventNames.BiasedLockRevocation); + recording.start(); + + FutureTask lockerTask = new FutureTask<>(() -> { + MyLock l = new MyLock(); + touch(l); + return l; + }); + + Thread locker = new Thread(lockerTask, "BiasLocker"); + locker.start(); + locker.join(); + + // Even after joining, the VM has a bit more work to do before the thread is actually removed + // from the threads list. Ensure that this has happened before proceeding. + while (true) { + PidJcmdExecutor jcmd = new PidJcmdExecutor(); + OutputAnalyzer oa = jcmd.execute("Thread.print", true); + String lockerThreadFound = oa.firstMatch("BiasLocker"); + if (lockerThreadFound == null) { + break; + } + }; + + MyLock l = lockerTask.get(); + touch(l); + + recording.stop(); + List events = getRevocationEvents(recording, "lockClass", MyLock.class); + Asserts.assertEQ(events.size(), 1); + + RecordedEvent event = events.get(0); + Events.assertEventThread(event, Thread.currentThread()); + // Previous owner will usually be null, but can also be a thread that + // was created after the BiasLocker thread exited due to address reuse. + RecordedThread prevOwner = event.getValue("previousOwner"); + if (prevOwner != null) { + Asserts.assertNE(prevOwner.getJavaName(), "BiasLocker"); + } + validateStackTrace(event.getStackTrace()); + } + + static void testBulkRevocationNoRebias() throws Throwable { + class MyLock {}; + + Recording recording = new Recording(); + + recording.enable(EventNames.BiasedLockClassRevocation); + recording.start(); + + Thread biasBreaker0 = triggerRevocation(BULK_REVOKE_THRESHOLD, MyLock.class); + Thread biasBreaker1 = triggerRevocation(BULK_REVOKE_THRESHOLD, MyLock.class); + + recording.stop(); + List events = getRevocationEvents(recording, "revokedClass", MyLock.class); + Asserts.assertEQ(events.size(), 2); + + // The rebias event should occur before the noRebias one + RecordedEvent eventRebias = events.get(0); + RecordedEvent eventNoRebias = events.get(1); + + Events.assertEventThread(eventRebias, biasBreaker0); + Events.assertField(eventRebias, "disableBiasing").equal(false); + + Events.assertEventThread(eventNoRebias, biasBreaker1); + Events.assertField(eventNoRebias, "disableBiasing").equal(true); + + RecordedClass lockClassRebias = eventRebias.getValue("revokedClass"); + Asserts.assertEquals(lockClassRebias.getName(), MyLock.class.getName()); + RecordedClass lockClassNoRebias = eventNoRebias.getValue("revokedClass"); + Asserts.assertEquals(lockClassNoRebias.getName(), MyLock.class.getName()); + + validateStackTrace(eventRebias.getStackTrace()); + validateStackTrace(eventNoRebias.getStackTrace()); + } + + static void testRevocationSafepointIdCorrelation() throws Throwable { + class MyLock {}; + + Recording recording = new Recording(); + + recording.enable(EventNames.BiasedLockRevocation); + recording.enable(EventNames.BiasedLockClassRevocation); + recording.enable(EventNames.ExecuteVMOperation); + recording.start(); + + triggerRevocation(BULK_REVOKE_THRESHOLD, MyLock.class); + + recording.stop(); + List events = Events.fromRecording(recording); + + // Determine which safepoints included single and bulk revocation VM operations + Set vmOperationsSingle = new HashSet<>(); + Set vmOperationsBulk = new HashSet<>(); + + for (RecordedEvent event : events) { + if (event.getEventType().getName().equals(EventNames.ExecuteVMOperation)) { + String operation = event.getValue("operation"); + Integer safepointId = event.getValue("safepointId"); + + if (operation.equals("RevokeBias")) { + vmOperationsSingle.add(safepointId); + } else if (operation.equals("BulkRevokeBias")) { + vmOperationsBulk.add(safepointId); + } + } + } + + int revokeCount = 0; + int bulkRevokeCount = 0; + + // Match all revoke events to a corresponding VMOperation event + for (RecordedEvent event : events) { + if (event.getEventType().getName().equals(EventNames.BiasedLockRevocation)) { + Integer safepointId = event.getValue("safepointId"); + String lockClass = ((RecordedClass)event.getValue("lockClass")).getName(); + if (lockClass.equals(MyLock.class.getName())) { + Asserts.assertTrue(vmOperationsSingle.contains(safepointId)); + revokeCount++; + } + } else if (event.getEventType().getName().equals(EventNames.BiasedLockClassRevocation)) { + Integer safepointId = event.getValue("safepointId"); + String lockClass = ((RecordedClass)event.getValue("revokedClass")).getName(); + if (lockClass.toString().equals(MyLock.class.getName())) { + Asserts.assertTrue(vmOperationsBulk.contains(safepointId)); + bulkRevokeCount++; + } + } + } + + Asserts.assertGT(bulkRevokeCount, 0); + Asserts.assertGT(revokeCount, bulkRevokeCount); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestClassDefineEvent.java 2019-02-08 18:33:37.540019022 +0300 @@ -0,0 +1,79 @@ +/* + * 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.event.runtime; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedClassLoader; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.TestClassLoader; + +/** + * @test + * @key jfr + * + * @library /lib / + * @build jdk.jfr.event.runtime.TestClasses + * @run main/othervm jdk.jfr.event.runtime.TestClassDefineEvent + */ +public final class TestClassDefineEvent { + + private final static String TEST_CLASS_NAME = "jdk.jfr.event.runtime.TestClasses"; + private final static String EVENT_NAME = EventNames.ClassDefine; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + TestClassLoader cl = new TestClassLoader(); + recording.start(); + cl.loadClass(TEST_CLASS_NAME); + recording.stop(); + + List events = Events.fromRecording(recording); + boolean foundTestClasses = false; + for (RecordedEvent event : events) { + System.out.println(event); + RecordedClass definedClass = event.getValue("definedClass"); + if (TEST_CLASS_NAME.equals(definedClass.getName())) { + RecordedClassLoader definingClassLoader = definedClass.getClassLoader(); + Asserts.assertNotNull(definingClassLoader, "Defining Class Loader should not be null"); + RecordedClass definingClassLoaderType = definingClassLoader.getType(); + Asserts.assertNotNull(definingClassLoaderType, "The defining Class Loader type should not be null"); + Asserts.assertEquals(cl.getClass().getName(), definingClassLoaderType.getName(), + "Expected type " + cl.getClass().getName() + ", got type " + definingClassLoaderType.getName()); + //Asserts.assertEquals(cl.getName(), definingClassLoader.getName(), + // "Defining Class Loader should have the same name as the original class loader"); + foundTestClasses = true; + } + } + Asserts.assertTrue(foundTestClasses, "No class define event found for " + TEST_CLASS_NAME); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestClassLoadEvent.java 2019-02-08 18:33:37.680014148 +0300 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedClassLoader; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.TestClassLoader; + +/** + * @test + * @key jfr + * + * @library /lib / + * @build jdk.jfr.event.runtime.TestClasses + * @run main/othervm jdk.jfr.event.runtime.TestClassLoadEvent + */ +public final class TestClassLoadEvent { + + private final static String TEST_CLASS_NAME = "jdk.jfr.event.runtime.TestClasses"; + private final static String BOOT_CLASS_LOADER_NAME = ""; + private final static String SEARCH_CLASS_NAME = "java.lang.Object"; + private final static String SEARCH_PACKAGE_NAME = "java/lang"; + private final static String SEARCH_MODULE_NAME = "java.base"; + private final static String EVENT_NAME = EventNames.ClassLoad; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + TestClassLoader cl = new TestClassLoader(); + recording.start(); + cl.loadClass(TEST_CLASS_NAME); + recording.stop(); + + List events = Events.fromRecording(recording); + boolean isLoaded = false; + for (RecordedEvent event : events) { + RecordedClass loadedClass = event.getValue("loadedClass"); + if (SEARCH_CLASS_NAME.equals(loadedClass.getName())) { + System.out.println(event); + //neither package nor module events are available at 8 + //Events.assertClassPackage(loadedClass, SEARCH_PACKAGE_NAME); + //Events.assertClassModule(loadedClass, SEARCH_MODULE_NAME); + RecordedClassLoader initiatingClassLoader = event.getValue("initiatingClassLoader"); + Asserts.assertEquals(cl.getClass().getName(), initiatingClassLoader.getType().getName(), + "Expected type " + cl.getClass().getName() + ", got type " + initiatingClassLoader.getType().getName()); + RecordedClassLoader definingClassLoader = loadedClass.getClassLoader(); + Asserts.assertEquals(BOOT_CLASS_LOADER_NAME, definingClassLoader.getName(), + "Expected boot loader to be the defining class loader"); + Asserts.assertNull(definingClassLoader.getType(), "boot loader should not have a type"); + isLoaded = true; + } + } + Asserts.assertTrue(isLoaded, "No class load event found for class " + SEARCH_CLASS_NAME); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestClassLoaderStatsEvent.java 2019-02-08 18:33:37.824009136 +0300 @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2014, 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.event.runtime; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClassLoader; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @build jdk.jfr.event.runtime.TestClasses + * @run main/othervm jdk.jfr.event.runtime.TestClassLoaderStatsEvent + */ +public class TestClassLoaderStatsEvent { + private final static String EVENT_NAME = EventNames.ClassLoaderStatistics; + private final static String CLASS_LOADER_NAME = "jdk/jfr/event/runtime/TestClassLoaderStatsEvent$DummyClassLoader"; // XXX unsupported in JDK8 "MyDummyClassLoader"; + private final static String CLASSLOADER_TYPE_NAME = "jdk.jfr.event.runtime.TestClassLoaderStatsEvent$DummyClassLoader"; + public static DummyClassLoader dummyloader; + + public static void main(String[] args) throws Throwable { + createDummyClassLoader(CLASS_LOADER_NAME); + + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + List consumer = Events.fromRecording(recording); + Events.hasEvents(consumer); + + boolean isAnyFound = false; + for (RecordedEvent event : consumer) { + System.out.println("Event:" + event); + if (Events.assertField(event, "classLoader").getValue() == null) { + continue; + } + RecordedClassLoader recordedClassLoader = event.getValue("classLoader"); + if (CLASSLOADER_TYPE_NAME.equals(recordedClassLoader.getType().getName())) { + Asserts.assertEquals(CLASS_LOADER_NAME, recordedClassLoader.getName(), + "Expected class loader name " + CLASS_LOADER_NAME + ", got name " + recordedClassLoader.getName()); + Events.assertField(event, "classCount").equal(1L); + Events.assertField(event, "chunkSize").above(1L); + Events.assertField(event, "blockSize").above(1L); + Events.assertField(event, "anonymousClassCount").equal(1L); + Events.assertField(event, "anonymousChunkSize").above(1L); + Events.assertField(event, "anonymousBlockSize").above(1L); + isAnyFound = true; + } + } + Asserts.assertTrue(isAnyFound, "No events found"); + } + + private static void createDummyClassLoader(String name) throws Throwable { + dummyloader = new DummyClassLoader(name); + Class c = Class.forName(TestClass.class.getName(), true, dummyloader); + if (c.getClassLoader() != dummyloader) { + throw new RuntimeException("TestClass defined by wrong classloader: " + c.getClassLoader()); + } + } + + public static class DummyClassLoader extends ClassLoader { + + static ByteBuffer readClassFile(String name) { + String testClasses = System.getProperty("test.classes"); + File f = new File(testClasses, name); + try (FileInputStream fin = new FileInputStream(f)) { + FileChannel fc = fin.getChannel(); + return fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); + } catch (IOException e) { + throw new RuntimeException("Can't open file: " + f, e); + } + } + + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + Class c; + if (TestClass.class.getName().equals(name)) { + c = findClass(name); + if (resolve) { + resolveClass(c); + } + } else { + c = super.loadClass(name, resolve); + } + return c; + } + + protected Class findClass(String name) throws ClassNotFoundException { + if (!TestClass.class.getName().equals(name)) { + throw new ClassNotFoundException("Unexpected class: " + name); + } + return defineClass(name, readClassFile(TestClass.class.getName().replace(".", File.separator) + ".class"), null); + } + + public DummyClassLoader(String name) { + super(ClassLoader.getSystemClassLoader()); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestClassLoadingStatisticsEvent.java 2019-02-08 18:33:37.960004402 +0300 @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.TestClassLoader; + +/** + * @test + * @key jfr + * + * @library /lib / + * @build jdk.jfr.event.runtime.TestClasses + * @run main/othervm jdk.jfr.event.runtime.TestClassLoadingStatisticsEvent + */ +/** + * This test will load a number of classes. After each load step we verify that + * the loadedClassCount and unloadedClassCount attributes are correct. + * + * System.gc() will trigger class unloading if -XX:+ExplicitGCInvokesConcurrent + * is NOT set. If this flag is set G1 will never unload classes on System.gc() + * and CMS will not guarantee that all semantically dead classes will be + * unloaded. As far as the "jfr" key guarantees no VM flags are set from the + * outside it should be enough with System.gc(). + */ +public class TestClassLoadingStatisticsEvent { + + private final static String EVENT_PATH = EventNames.ClassLoadingStatistics; + private final static String TESTCLASS_PUBLIC_STATIC = + "jdk.jfr.event.runtime.TestClasses$TestClassPublicStatic"; + private final static String TESTCLASS_PUBLIC_STATIC_INNER = + "jdk.jfr.event.runtime.TestClasses$TestClassPublicStatic$TestClassPublicStaticInner"; + + // Declare unloadableClassLoader as "public static" + // to prevent the compiler to optimize away all unread writes + public static TestClassLoader unloadableClassLoader; + + public static void main(String[] args) throws Throwable { + // Load twice to get more stable result. + RecordedEvent event = getCurrentEvent(); + event = getCurrentEvent(); + + // Declare class ClassLoadingStatisticsHelper. + TestClasses[] helpers = new TestClasses[10]; + event = verifyCountDelta(event, 1, 0); + + // Should load classes TestClassPrivate and TestClassPrivateStatic. + for (int c = 0; c < helpers.length; c++) { + helpers[c] = new TestClasses(); + } + event = verifyCountDelta(event, 2, 0); + + // Load classes TestClassProtected and B2. + helpers[0].new TestClassProtected(); + helpers[1].new TestClassProtected(); // This class is already loaded. + new TestClasses.TestClassProtectedStatic(); + event = verifyCountDelta(event, 2, 0); + + // Load classes TestClassProtected1 and TestClassProtectedStatic1. + for (int c = 0; c < helpers.length; c++) { + helpers[c].loadClasses(); + } + event = verifyCountDelta(event, 2, 0); + + // Load the classes with separate class loader. Will be unloaded later. + unloadableClassLoader = new TestClassLoader(); + + unloadableClassLoader.loadClass(TESTCLASS_PUBLIC_STATIC_INNER); + event = verifyCountDelta(event, 1, 0); + + unloadableClassLoader.loadClass(TESTCLASS_PUBLIC_STATIC); + event = verifyCountDelta(event, 1, 0); + + // This System.gc() should not unload classes, since the + // unloadableClassLoader object is still active. + System.gc(); + event = verifyCountDelta(event, 0, 0); + + // make classes are unloaded. + unloadableClassLoader = null; + System.gc(); + event = verifyCountDelta(event, 0, 2); + } + + private static RecordedEvent getCurrentEvent() throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_PATH); + recording.start(); + recording.stop(); + List events = Events.fromRecording(recording); + Asserts.assertFalse(events.isEmpty(), "No events in recording"); + RecordedEvent event = events.get(0); + return event; + } + + private static RecordedEvent verifyCountDelta( + RecordedEvent prevEvent, int loadDelta, int unloadDelta) throws Throwable { + RecordedEvent currEvent = null; + try { + long prevLoad = Events.assertField(prevEvent, "loadedClassCount").getValue(); + long prevUnload = Events.assertField(prevEvent, "unloadedClassCount").getValue(); + + currEvent = getCurrentEvent(); + Events.assertField(currEvent, "loadedClassCount").atLeast(prevLoad + loadDelta); + Events.assertField(currEvent, "unloadedClassCount").atLeast(prevUnload + unloadDelta); + return currEvent; + } catch (Throwable t) { + System.out.println("verifyCountDelta failed. prevEvent=" + prevEvent + ", currEvent=" + currEvent); + throw t; + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestClassUnloadEvent.java 2019-02-08 18:33:38.099999529 +0300 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedClassLoader; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.TestClassLoader; + +/** + * @test + * @summary The test verifies that a class unload event is created when class is unloaded + * @key jfr + * + * @library /lib / + * @build jdk.jfr.event.runtime.TestClasses + * @run main/othervm -XX:+PrintGCDetails -XX:+PrintGC -verbose:class -Xmx16m jdk.jfr.event.runtime.TestClassUnloadEvent + */ + +/** + * System.gc() will trigger class unloading if -XX:+ExplicitGCInvokesConcurrent is NOT set. + * If this flag is set G1 will never unload classes on System.gc() and + * CMS will not guarantee that all semantically dead classes will be unloaded. + * As far as the "jfr" key guarantees no VM flags are set from the outside + * it should be enough with System.gc(). + */ +public final class TestClassUnloadEvent { + private final static String TEST_CLASS_NAME = "jdk.jfr.event.runtime.TestClasses"; + private final static String EVENT_PATH = EventNames.ClassUnload; + + // Declare unloadableClassLoader as "public static" + // to prevent the compiler to optimize away all unread writes + public static TestClassLoader unloadableClassLoader; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_PATH).withThreshold(Duration.ofMillis(0)); + unloadableClassLoader = new TestClassLoader(); + recording.start(); + unloadableClassLoader.loadClass(TEST_CLASS_NAME); + unloadableClassLoader = null; + System.gc(); + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + boolean isAnyFound = false; + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + RecordedClass unloadedClass = event.getValue("unloadedClass"); + if (TEST_CLASS_NAME.equals(unloadedClass.getName())) { + RecordedClassLoader definingClassLoader = unloadedClass.getClassLoader(); + Asserts.assertEquals(TestClassLoader.class.getName(), definingClassLoader.getType().getName(), + "Expected " + TestClassLoader.class.getName() + ", got " + definingClassLoader.getType().getName()); + //Asserts.assertEquals(TestClassLoader.CLASS_LOADER_NAME, definingClassLoader.getName(), + // "Expected " + TestClassLoader.CLASS_LOADER_NAME + ", got " + definingClassLoader.getName()); + Asserts.assertFalse(isAnyFound, "Found more than 1 event"); + isAnyFound = true; + } + } + Asserts.assertTrue(isAnyFound, "No events found"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestClasses.java 2019-02-08 18:33:38.239994656 +0300 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +public class TestClasses { + + protected TestClassPrivate testClassPrivate; + protected TestClassPrivateStatic testClassPrivateStatic; + + public TestClasses() { + testClassPrivate = new TestClassPrivate(); + testClassPrivateStatic = new TestClassPrivateStatic(); + } + + // Classes TestClassPrivate and TestClassPrivateStatic should be loaded at + // the same time + // as the base class TestClasses + private class TestClassPrivate { + } + + private static class TestClassPrivateStatic { + } + + protected class TestClassProtected { + } + + protected static class TestClassProtectedStatic { + } + + // When loadClasses() is run, 3 new classes should be loaded. + public void loadClasses() throws ClassNotFoundException { + final ClassLoader cl = getClass().getClassLoader(); + cl.loadClass("jdk.jfr.event.runtime.TestClasses$TestClassProtected1"); + cl.loadClass("jdk.jfr.event.runtime.TestClasses$TestClassProtectedStatic1"); + } + + protected class TestClassProtected1 { + } + + protected static class TestClassProtectedStatic1 { + protected TestClassProtectedStaticInner testClassProtectedStaticInner = new TestClassProtectedStaticInner(); + + protected static class TestClassProtectedStaticInner { + } + } + + public static class TestClassPublicStatic { + public static class TestClassPublicStaticInner { + } + } + +} + +class TestClass { + static { + // force creation of anonymous class (for the lambda form) + Runnable r = () -> System.out.println("Hello"); + r.run(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestExceptionEvents.java 2019-02-08 18:33:38.383989644 +0300 @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.jfr.consumer.RecordedThread; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.runtime.TestExceptionEvents + */ +public class TestExceptionEvents { + + private static final String EXCEPTION_EVENT_PATH = EventNames.JavaExceptionThrow; + private static final String ERROR_EVENT_PATH = EventNames.JavaErrorThrow; + private static final String EXCEPTION_STATISTICS_PATH = EventNames.ExceptionStatistics; + + private static final String EXCEPTION_MESSAGE = "exceptiontest"; + private static final String ERROR_MESSAGE = "errortest"; + private static final String THROWABLE_MESSAGE = "throwabletest"; + + private static final int ITERATIONS = 10; + + private static int exceptionCount = 0; + + public static void main(String[] args) throws Throwable { + Recording recording = createRecording(); + + List events = Events.fromRecording(recording); + checkStatisticsEvent(events, exceptionCount); + checkThrowableEvents(events, EXCEPTION_EVENT_PATH, ITERATIONS, MyException.class, EXCEPTION_MESSAGE); + checkThrowableEvents(events, ERROR_EVENT_PATH, ITERATIONS, MyError.class, ERROR_MESSAGE); + checkThrowableEvents(events, EXCEPTION_EVENT_PATH, ITERATIONS, MyThrowable.class, THROWABLE_MESSAGE); + checkExceptionStackTrace(); + } + + private static void checkExceptionStackTrace() throws Exception { + @SuppressWarnings("serial") + class TestError extends Error { + } + @SuppressWarnings("serial") + class TestException extends Exception { + } + + try (Recording r = new Recording()) { + r.enable(EventNames.JavaErrorThrow).withStackTrace(); + r.enable(EventNames.JavaExceptionThrow).withStackTrace(); + r.start(); + try { + throw new TestError(); + } catch (Error e) { + System.out.println(e.getClass() + " thrown!"); + } + try { + throw new TestException(); + } catch (Exception e) { + System.out.println(e.getClass() + " thrown!"); + } + try { + throw new Exception(); + } catch (Exception e) { + System.out.println(e.getClass() + " thrown!"); + } + r.stop(); + List events = Events.fromRecording(r); + Events.hasEvents(events); + for (RecordedEvent e : events) { + RecordedStackTrace rs = e.getStackTrace(); + RecordedClass rc = e.getValue("thrownClass"); + List frames = rs.getFrames(); + RecordedFrame topFrame = frames.get(0); + System.out.println(rc.getName() + " Top frame: " + topFrame.getMethod().getName()); + if (!topFrame.getMethod().getName().equals("")) { + throw new Exception("Expected name of top frame to be "); + } + } + } + } + + private static Recording createRecording() throws Exception { + Recording recording = new Recording(); + recording.enable(EXCEPTION_STATISTICS_PATH); + recording.enable(EXCEPTION_EVENT_PATH).withThreshold(Duration.ofMillis(0)); + recording.enable(ERROR_EVENT_PATH).withThreshold(Duration.ofMillis(0)); + recording.start(); + + for (int i = 0; i < ITERATIONS; i++) { + try { + throw new MyException(EXCEPTION_MESSAGE); + } catch (MyException e) { + exceptionCount++; + } + try { + throw new MyError(ERROR_MESSAGE); + } catch (MyError e) { + exceptionCount++; + } + try { + throw new MyThrowable(THROWABLE_MESSAGE); + } catch (MyThrowable t) { + exceptionCount++; + } + } + recording.stop(); + return recording; + } + + + private static void checkStatisticsEvent(List events, long minCount) throws Exception { + // Events are not guaranteed to be in chronological order, take highest value. + long count = -1; + for(RecordedEvent event : events) { + if (Events.isEventType(event, EXCEPTION_STATISTICS_PATH)) { + System.out.println("Event: " + event); + count = Math.max(count, Events.assertField(event, "throwables").getValue()); + System.out.println("count=" + count); + } + } + Asserts.assertTrue(count != -1, "No events of type " + EXCEPTION_STATISTICS_PATH); + Asserts.assertGreaterThanOrEqual(count, minCount, "Too few exception count in statistics event"); + } + + private static void checkThrowableEvents(List events, String eventName, + int excpectedEvents, Class expectedClass, String expectedMessage) throws Exception { + int count = 0; + for(RecordedEvent event : events) { + if (Events.isEventType(event, eventName)) { + String message = Events.assertField(event, "message").getValue(); + if (expectedMessage.equals(message)) { + RecordedThread t = event.getThread(); + String threadName = t.getJavaName(); + if (threadName != null && threadName.equals(Thread.currentThread().getName())) { + RecordedClass jc = event.getValue("thrownClass"); + if (jc.getName().equals(expectedClass.getName())) { + count++; + } + } + } + } + } + Asserts.assertEquals(count, excpectedEvents, "Wrong event count for type " + eventName); + } + + private static class MyException extends Exception { + private static final long serialVersionUID = -2614309279743448910L; + public MyException(String msg) { + super(msg); + } + } + + private static class MyError extends Error { + private static final long serialVersionUID = -8519872786387358196L; + public MyError(String msg) { + super(msg); + } + } + + private static class MyThrowable extends Throwable { + private static final long serialVersionUID = -7929442863511070361L; + public MyThrowable(String msg) { + super(msg); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestExceptionSubclass.java 2019-02-08 18:33:38.523984771 +0300 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import java.util.concurrent.TimeUnit; + +/** + * @test + * @key jfr + * @bug 8013122 + * + * @library /lib / + * @run main/othervm jdk.jfr.event.runtime.TestExceptionSubclass + */ +public class TestExceptionSubclass { + + public TestExceptionSubclass() { + try { + throw new PerfectlyFineException(TimeUnit.MILLISECONDS); + } catch (PerfectlyFineException e) { + //thats perfectly fine. + } + } + + public static void main(String[] args) throws Throwable { + new TestExceptionSubclass(); + } + + + class PerfectlyFineException extends Error { + private static final long serialVersionUID = 1L; + private final TimeUnit unit; + + PerfectlyFineException(TimeUnit unit) { + this.unit = unit; + } + + public String getMessage() { + return "Failed in " + unit.toNanos(1) + " ns"; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestJavaBlockedEvent.java 2019-02-08 18:33:38.663979898 +0300 @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; + +import java.time.Duration; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.management.ThreadMXBeanTool; +import jdk.test.lib.thread.TestThread; +import jdk.test.lib.thread.XRun; + +/** + * @test + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.event.runtime.TestJavaBlockedEvent + */ +public class TestJavaBlockedEvent { + private static final String EVENT_NAME = EventNames.JavaMonitorEnter; + private static final long THRESHOLD_MILLIS = 1; + + static class Lock { + } + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(THRESHOLD_MILLIS)); + recording.start(); + final Lock lock = new Lock(); + final CountDownLatch blockerInLock = new CountDownLatch(1); + final CountDownLatch blockingFinished = new CountDownLatch(1); + final CountDownLatch blockedFinished = new CountDownLatch(1); + TestThread blockerThread = new TestThread(new XRun() { + @Override + public void xrun() throws Throwable { + synchronized (lock) { + blockerInLock.countDown(); + blockingFinished.await(); + } + blockedFinished.await(); + } + }); + + TestThread blockedThread = new TestThread(new XRun() { + @Override + public void xrun() throws Throwable { + blockerInLock.await(); + synchronized (lock) { + blockedFinished.countDown(); + } + } + }); + blockerThread.start(); + blockedThread.start(); + + blockerInLock.await(); + ThreadMXBeanTool.waitUntilBlockingOnObject(blockedThread, Thread.State.BLOCKED, lock); + Thread.sleep(2 * THRESHOLD_MILLIS); + blockingFinished.countDown(); + blockedFinished.await(); + + blockedThread.join(); + blockerThread.join(); + recording.stop(); + + List events = Events.fromRecording(recording); + boolean isAnyFound = false; + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + if (event.getThread().getJavaThreadId() == blockedThread.getId()) { + if (isMyLock(Events.assertField(event, "monitorClass.name").getValue())) { + Events.assertEventThread(event, "previousOwner", blockerThread); + Events.assertField(event, "address").above(0L); + assertFalse(isAnyFound, "Found multiple events"); + isAnyFound = true; + } + } + } + assertTrue(isAnyFound, "No blocking event from " + blockedThread.getName()); + } + + private static boolean isMyLock(String className) { + return Lock.class.getName().replace('.', '/').equals(className); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestJavaMonitorInflateEvent.java 2019-02-08 18:33:38.799975164 +0300 @@ -0,0 +1,117 @@ +/* + * 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.event.runtime; + +import static jdk.test.lib.Asserts.assertTrue; + +import java.nio.file.Paths; +import java.time.Duration; +import java.util.concurrent.CountDownLatch; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.thread.TestThread; +import jdk.test.lib.thread.XRun; + +/** + * @test TestJavaMonitorInflateEvent + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.runtime.TestJavaMonitorInflateEvent + */ +public class TestJavaMonitorInflateEvent { + + private static final String FIELD_KLASS_NAME = "monitorClass.name"; + private static final String FIELD_ADDRESS = "address"; + private static final String FIELD_CAUSE = "cause"; + + private static final String EVENT_NAME = EventNames.JavaMonitorInflate; + private static final long WAIT_TIME = 123456; + + static class Lock { + } + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + final Lock lock = new Lock(); + final CountDownLatch latch = new CountDownLatch(1); + // create a thread that waits + TestThread waitThread = new TestThread(new XRun() { + @Override + public void xrun() throws Throwable { + synchronized (lock) { + latch.countDown(); + lock.wait(WAIT_TIME); + } + } + }); + try { + recording.start(); + waitThread.start(); + latch.await(); + synchronized (lock) { + lock.notifyAll(); + } + } finally { + waitThread.join(); + recording.stop(); + } + final String thisThreadName = Thread.currentThread().getName(); + final String waitThreadName = waitThread.getName(); + final String lockClassName = lock.getClass().getName().replace('.', '/'); + boolean isAnyFound = false; + try { + // Find at least one event with the correct monitor class and check the other fields + for (RecordedEvent event : Events.fromRecording(recording)) { + assertTrue(EVENT_NAME.equals(event.getEventType().getName()), "mismatched event types?"); + // Check recorded inflation event is associated with the Lock class used in the test + final String recordedMonitorClassName = Events.assertField(event, FIELD_KLASS_NAME).getValue(); + if (!lockClassName.equals(recordedMonitorClassName)) { + continue; + } + // Check recorded thread matches one of the threads in the test + final String recordedThreadName = event.getThread().getJavaName(); + if (!(recordedThreadName.equals(waitThreadName) || recordedThreadName.equals(thisThreadName))) { + continue; + } + Events.assertField(event, FIELD_ADDRESS).notEqual(0L); + Events.assertField(event, FIELD_CAUSE).notNull(); + isAnyFound = true; + break; + } + assertTrue(isAnyFound, "Expected an inflation event from test"); + } catch (Throwable e) { + recording.dump(Paths.get("failed.jfr")); + throw e; + } finally { + recording.close(); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestJavaMonitorWaitEvent.java 2019-02-08 18:33:38.935970430 +0300 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; + +import java.time.Duration; +import java.util.concurrent.CountDownLatch; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.thread.TestThread; +import jdk.test.lib.thread.XRun; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.runtime.TestJavaMonitorWaitEvent + */ +public class TestJavaMonitorWaitEvent { + + private final static String EVENT_NAME = EventNames.JavaMonitorWait; + private static final long WAIT_TIME = 123456; + + static class Lock { + } + + static boolean silenceFindBugsNakedNotify; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + + final Lock lock = new Lock(); + final CountDownLatch latch = new CountDownLatch(1); + + TestThread waitThread = new TestThread(new XRun() { + @Override + public void xrun() throws Throwable { + synchronized (lock) { + latch.countDown(); + lock.wait(WAIT_TIME); + silenceFindBugsNakedNotify = false; + } + } + }); + + try { + recording.start(); + waitThread.start(); + latch.await(); + synchronized (lock) { + silenceFindBugsNakedNotify = true; + lock.notifyAll(); + } + } finally { + waitThread.join(); + recording.stop(); + } + + boolean isAnyFound = false; + for (RecordedEvent event : Events.fromRecording(recording)) { + System.out.println("Event:" + event); + if ((long)Events.assertField(event, "timeout").getValue() != WAIT_TIME) { + continue; + } + + assertFalse(isAnyFound, "Found more than 1 event"); + isAnyFound = true; + Events.assertEventThread(event, waitThread); + final String lockClassName = lock.getClass().getName().replace('.', '/'); + Events.assertField(event, "monitorClass.name").equal(lockClassName); + Events.assertField(event, "address").notEqual(0L); + Events.assertField(event, "timedOut").equal(false); + Events.assertEventThread(event, "notifier", Thread.currentThread()); + } + assertTrue(isAnyFound, "Correct event not found"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestJavaMonitorWaitTimeOut.java 2019-02-08 18:33:39.071965696 +0300 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.runtime.TestJavaMonitorWaitTimeOut + */ +public class TestJavaMonitorWaitTimeOut { + + static final class Lock { + + } + + private static final String THREAD_NAME = "Notifier"; + + static class Notifierhread extends Thread { + private final Object lock; + + Notifierhread(Object lock) { + super(THREAD_NAME); + this.lock = lock; + } + + public void run() { + synchronized (lock) { + lock.notify(); + } + } + } + + public static void main(String[] args) throws Throwable { + try (Recording recording = new Recording()) { + recording.enable(EventNames.JavaMonitorWait).withoutThreshold().withoutStackTrace(); + recording.start(); + Lock lock = new Lock(); + + synchronized (lock) { + // event without notifier, should be null + lock.wait(10); + } + Notifierhread s = new Notifierhread(lock); + + synchronized (lock) { + s.start(); + // event with a notifier + lock.wait(1_000_000); + s.join(); + } + + synchronized (lock) { + // event without a notifier, should be null + lock.wait(11); + } + recording.stop(); + List events = Events.fromRecording(recording); + for (RecordedEvent e : events) { + if (isWaitEvent(e)) { + System.out.println(e); + } + } + assertTimeOutEvent(events, 10, null); + assertTimeOutEvent(events, 1_000_000, THREAD_NAME); + assertTimeOutEvent(events, 11, null); + } + } + + private static boolean isWaitEvent(RecordedEvent event) { + RecordedClass t = event.getValue("monitorClass"); + return t != null && t.getName().equals(Lock.class.getName()); + } + + private static void assertTimeOutEvent(List events, long timeout, String expectedThreadName) { + for (RecordedEvent e : events) { + if (isWaitEvent(e)) { + Long l = e.getValue("timeout"); + if (l == timeout) { + RecordedThread notifier = e.getValue("notifier"); + String threadName = null; + if (notifier != null) { + threadName = notifier.getJavaName(); + } + Asserts.assertEquals(threadName, expectedThreadName, "Invalid thread"); + return; + } + } + } + Asserts.fail("Could not find event with monitorClass" + Lock.class.getName()); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestJavaThreadStatisticsEvent.java 2019-02-08 18:33:39.215960685 +0300 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import static jdk.test.lib.Asserts.assertTrue; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.runtime.TestJavaThreadStatisticsEvent + */ +public class TestJavaThreadStatisticsEvent { + private static final String EVENT_NAME = EventNames.JavaThreadStatistics; + + private static final int THREAD_COUNT = 10; + private static final Random RAND = new Random(4711); + + // verify thread count: 0 <= daemon <= active <= peak <= accumulated. + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(10)); + recording.start(); + + List threads = new ArrayList<>(); + for (int i = 0; i < THREAD_COUNT; i++) { + Thread thread = new Thread(TestJavaThreadStatisticsEvent::sleepRandom); + thread.start(); + threads.add(thread); + } + for (Thread thread : threads) { + thread.join(); + } + + recording.stop(); + List events = Events.fromRecording(recording); + boolean isAnyFound = false; + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + isAnyFound = true; + // verify thread count: 0 <= daemon <= active <= peak <= accumulated. + long daemonCount = Events.assertField(event, "daemonCount").atLeast(0L).getValue(); + long activeCount = Events.assertField(event, "activeCount").atLeast(daemonCount).getValue(); + long peakCount = Events.assertField(event, "peakCount").atLeast(activeCount).atLeast(1L).getValue(); + Events.assertField(event, "accumulatedCount").atLeast(peakCount).getValue(); + } + assertTrue(isAnyFound, "Correct event not found"); + } + + static void sleepRandom() { + try { + Thread.sleep(RAND.nextInt(10)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestJavaThreadStatisticsEventBean.java 2019-02-08 18:33:39.363955534 +0300 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import static jdk.test.lib.Asserts.assertTrue; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadMXBean; +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.event.runtime.TestJavaThreadStatisticsEventBean + */ +public class TestJavaThreadStatisticsEventBean { + private final static String EVENT_NAME = EventNames.JavaThreadStatistics; + + // Compare JFR thread counts to ThreadMXBean counts + public static void main(String[] args) throws Throwable { + long mxDaemonCount = -1; + long mxActiveCount = -1; + + // Loop until we are sure no threads were started during the recording. + Recording recording = null; + ThreadMXBean mxBean = ManagementFactory.getThreadMXBean(); + long totalCountBefore = -1; + while (totalCountBefore != mxBean.getTotalStartedThreadCount()) { + recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(10)); + totalCountBefore = mxBean.getTotalStartedThreadCount(); + recording.start(); + mxDaemonCount = mxBean.getDaemonThreadCount(); + mxActiveCount = mxBean.getThreadCount(); + recording.stop(); + final String msg = "testCountByMXBean: threadsBefore=%d, threadsAfter=%d%n"; + System.out.format(msg, totalCountBefore, mxBean.getTotalStartedThreadCount()); + } + + List events= Events.fromRecording(recording); + boolean isAnyFound = false; + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + isAnyFound = true; + Events.assertField(event, "daemonCount").equal(mxDaemonCount); + Events.assertField(event, "activeCount").equal(mxActiveCount); + Events.assertField(event, "accumulatedCount").atLeast(mxActiveCount); + Events.assertField(event, "peakCount").atLeast(mxActiveCount); + } + assertTrue(isAnyFound, "Correct event not found"); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestModuleEvents.java 2019-02-08 18:33:39.503950661 +0300 @@ -0,0 +1,126 @@ +/* + * 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.event.runtime; + +import static jdk.test.lib.Asserts.assertEquals; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @summary Tests the JFR events related to modules + * @key jfr + * + * + * @library /lib / + * @run main/othervm --limit-modules java.base,jdk.jfr jdk.jfr.event.runtime.TestModuleEvents + */ +public final class TestModuleEvents { + + private static final String MODULE_EXPORT_EVENT_NAME = EventNames.ModuleExport; + private static final String MODULE_REQUIRE_EVENT_NAME = EventNames.ModuleRequire; + private static final String UNNAMED = ""; + + public static void main(String[] args) throws Throwable { + verifyRequiredModules(); + verifyExportedModules(); + } + + private static void verifyRequiredModules() throws Throwable { + Recording recording = new Recording(); + recording.enable(MODULE_REQUIRE_EVENT_NAME); + + recording.start(); + recording.stop(); + + List events = Events.fromRecording(recording); + assertDependency(events, "jdk.jfr", "java.base"); // jdk.jfr requires java.base (by edfault) + assertDependency(events, "java.base", "jdk.jfr"); // java.base require jdk.jfr for JDK events, i.e. FileRead + + recording.close(); + } + + private static void assertDependency(List events, String source, String required) throws Exception { + for (RecordedEvent e : events) { + String sourceModule = e.getValue("source.name"); + if (source.equals(sourceModule)) { + RecordedObject module = e.getValue("requiredModule"); + if (module != null) { + if (required.equals(module.getValue("name"))) { + return; + } + } + } + } + throw new Exception("Could not find module dependency between " + source + " and requires modeule "+ required); + } + + private static void verifyExportedModules() throws Throwable { + Recording recording = new Recording(); + recording.enable(MODULE_EXPORT_EVENT_NAME); + recording.start(); + recording.stop(); + + Map edges = new HashMap<>(); + + List events = Events.fromRecording(recording); + events.stream().forEach((ev) -> { + String exportedPackage = getValue(ev.getValue("exportedPackage"), "name", UNNAMED); + String toModule = getValue(ev.getValue("targetModule"), "name", UNNAMED); + + edges.put(exportedPackage, toModule); + }); + + // We expect + // 1) jdk.jfr -> (because we use the package) + // 2) java.util -> (because we use the package) + // 3) jdk.jfr.events -> java.base (from the jfr design) + // 4) jdk.internal -> jdk.jfr (from the jfr design) + // Where 'a -> b' means "package 'a' exported to module 'b'" + assertEquals(edges.get("jdk/jfr"), UNNAMED); + assertEquals(edges.get("java/util"), UNNAMED); + assertEquals(edges.get("jdk/jfr/events"), "java.base"); + assertEquals(edges.get("jdk/internal"), "jdk.jfr"); + + recording.close(); + } + + // Helper function to get field from a RecordedObject + private static String getValue(RecordedObject ro, String field, String defVal) { + if (ro != null && ro.getValue(field) != null) { + return ro.getValue(field); + } else { + return defVal; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestNativeLibrariesEvent.java 2019-02-08 18:33:39.643945788 +0300 @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import static jdk.test.lib.Asserts.assertTrue; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Platform; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.runtime.TestNativeLibrariesEvent + */ +public class TestNativeLibrariesEvent { + + private final static String EVENT_NAME = EventNames.NativeLibrary; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + + List expectedLibs = getExpectedLibs(); + for (RecordedEvent event : Events.fromRecording(recording)) { + System.out.println("Event:" + event); + long unsignedTopAddress = event.getValue("topAddress"); + long unsignedBaseAddress = event.getValue("baseAddress"); + assertValidAddresses(unsignedBaseAddress, unsignedTopAddress); + String lib = Events.assertField(event, "name").notEmpty().getValue(); + for (String expectedLib : new ArrayList<>(expectedLibs)) { + if (lib.contains(expectedLib)) { + expectedLibs.remove(expectedLib); + } + } + } + assertTrue(expectedLibs.isEmpty(), "Missing libraries:" + expectedLibs.stream().collect(Collectors.joining(", "))); + } + + private static List getExpectedLibs() throws Throwable { + String libTemplate = null; + if (Platform.isSolaris()) { + libTemplate = "lib%s.so"; + } else if (Platform.isWindows()) { + libTemplate = "%s.dll"; + } else if (Platform.isOSX()) { + libTemplate = "lib%s.dylib"; + } else if (Platform.isLinux()) { + libTemplate = "lib%s.so"; + } + if (libTemplate == null) { + throw new Exception("Unsupported OS"); + } + + List libs = new ArrayList(); + String[] names = { "jvm", "java", "zip" }; + for (String name : names) { + libs.add(String.format(libTemplate, name)); + } + return libs; + } + + private static void assertValidAddresses(long unsignedBaseAddress, long unsignedTopAddress) throws Exception { + if (unsignedTopAddress != 0) { // guard against missing value (0) + if (Long.compareUnsigned(unsignedTopAddress, unsignedBaseAddress) < 0) { + throw new Exception("Top address " + Long.toHexString(unsignedTopAddress) + " is below base addess " + Long.toHexString(unsignedBaseAddress)); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestNetworkUtilizationEvent.java 2019-02-08 18:33:39.787940777 +0300 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 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.event.runtime; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.Platform; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.time.Duration; +import java.time.Instant; +import java.util.List; +import java.util.Map; + +import static java.util.stream.Collectors.averagingLong; +import static java.util.stream.Collectors.groupingBy; + +/** + * @test + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.event.runtime.TestNetworkUtilizationEvent + */ +public class TestNetworkUtilizationEvent { + + private static final long packetSendCount = 100; + + public static void main(String[] args) throws Throwable { + testSimple(); + } + + static void testSimple() throws Throwable { + + Instant start = Instant.now(); + Recording recording = new Recording(); + recording.enable(EventNames.NetworkUtilization); + recording.start(); + + DatagramSocket socket = new DatagramSocket(); + String msg = "hello!"; + byte[] buf = msg.getBytes(); + + // Send a few packets both to the loopback address as well to an external + DatagramPacket packet = new DatagramPacket(buf, buf.length, InetAddress.getLoopbackAddress(), 12345); + for (int i = 0; i < packetSendCount; ++i) { + socket.send(packet); + } + packet = new DatagramPacket(buf, buf.length, InetAddress.getByName("10.0.0.0"), 12345); + for (int i = 0; i < packetSendCount; ++i) { + socket.send(packet); + } + + // Now there should have been traffic on at least two different interfaces + recording.stop(); + Duration runtime = Duration.between(start, Instant.now()); + List events = Events.fromRecording(recording); + + // Calculate the average write rate for each interface + Map writeRates = events.stream() + .collect(groupingBy(e -> Events.assertField(e, "networkInterface").getValue(), + averagingLong(e -> Events.assertField(e, "writeRate").getValue()))); + + // Our test packets should have generated at least this much traffic per second + long expectedTraffic = (buf.length * packetSendCount) / Math.max((long)1, runtime.getSeconds()); + + // Count the number of interfaces that have seen at least our test traffic + long interfacesWithTraffic = writeRates.values().stream() + .filter(d -> d >= expectedTraffic) + .count(); + + if (Platform.isWindows() || Platform.isSolaris()) { + // Windows and Solaris do not track statistics for the loopback interface + Asserts.assertGreaterThanOrEqual(writeRates.size(), 1); + Asserts.assertGreaterThanOrEqual(interfacesWithTraffic, Long.valueOf(1)); + } else { + Asserts.assertGreaterThanOrEqual(writeRates.size(), 2); + Asserts.assertGreaterThanOrEqual(interfacesWithTraffic, Long.valueOf(2)); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestSafepointEvents.java 2019-02-08 18:33:39.927935904 +0300 @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import static jdk.test.lib.Asserts.assertTrue; + +import java.nio.file.Paths; +import java.time.Duration; +import java.util.*; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; + +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import sun.hotspot.WhiteBox; + +/** + * @test TestSafepointEvents + * @key jfr + * + * @library /lib / + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. + * -XX:+FlightRecorder -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * jdk.jfr.event.runtime.TestSafepointEvents + */ +public class TestSafepointEvents { + + static final String[] EVENT_NAMES = new String[] { + EventNames.SafepointBegin, + EventNames.SafepointStateSyncronization, + EventNames.SafepointWaitBlocked, + EventNames.SafepointCleanup, + EventNames.SafepointCleanupTask, + EventNames.SafepointEnd + }; + + public static void main(String[] args) throws Exception { + Recording recording = new Recording(); + for (String name : EVENT_NAMES) { + recording.enable(name).withThreshold(Duration.ofMillis(0)); + } + recording.start(); + WhiteBox.getWhiteBox().forceSafepoint(); + recording.stop(); + + try { + // Verify that each event type was seen at least once + for (String name : EVENT_NAMES) { + boolean found = false; + for (RecordedEvent event : Events.fromRecording(recording)) { + found = event.getEventType().getName().equals(name); + if (found) { + break; + } + } + assertTrue(found, "Expected event from test [" + name + "]"); + } + + // Collect all events grouped by safepoint id + SortedMap> safepointIds = new TreeMap<>(); + for (RecordedEvent event : Events.fromRecording(recording)) { + Integer safepointId = event.getValue("safepointId"); + if (!safepointIds.containsKey(safepointId)) { + safepointIds.put(safepointId, new HashSet<>()); + } + safepointIds.get(safepointId).add(event.getEventType().getName()); + } + + // The last safepoint may be related to stopping the recording and can thus be + // incomplete - so if there is more than one, ignore the last one + if (safepointIds.size() > 1) { + safepointIds.remove(safepointIds.lastKey()); + } + Asserts.assertGreaterThanOrEqual(safepointIds.size(), 1, "At least 1 safepoint must have occured"); + + // Verify that each safepoint id has an occurence of every event type, + // this ensures that all events related to a given safepoint had the same id + for (Set safepointEvents : safepointIds.values()) { + for (String name : EVENT_NAMES) { + assertTrue(safepointEvents.contains(name), "Expected event '" + name + "' to be present"); + } + } + } catch (Throwable e) { + recording.dump(Paths.get("failed.jfr")); + throw e; + } finally { + recording.close(); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestSizeTFlags.java 2019-02-08 18:33:40.063931171 +0300 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2015, 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.event.runtime; + +import static jdk.test.lib.Asserts.assertTrue; + +import java.nio.file.Paths; +import java.time.Duration; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @bug 8058552 + * + * + * @key jfr + * @summary Test checks that flags of type size_t are being sent to the jfr + * @library /lib / + * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps -XX:+UseG1GC -XX:+UseTLAB -XX:MinTLABSize=3k -XX:OldSize=30m -XX:YoungPLABSize=3k -XX:MaxDirectMemorySize=5M jdk.jfr.event.runtime.TestSizeTFlags + */ +public class TestSizeTFlags { + private static final String EVENT_NAME = EventNames.UnsignedLongFlag; + private static final int NUMBER_OF_FLAGS_TO_CHECK = 4; + private static final long MIN_TLAB_SIZE_FLAG_VALUE = 3*1024L; + private static final long OLD_SIZE_FLAG_VALUE = 30*1024*1024L; + private static final long YOUNG_PLAB_SIZE_FLAG_VALUE = 3*1024L; + private static final long MAX_DIRECT_MEMORY_SIZE_FLAG_VALUE = 5*1024*1024L; + + // Test run java with some of the flags of type size_t. + // Goals are + // - to check that flags are reported to the jfr; + // - to make sure values are as expected. + public static void main(String[] args) throws Exception { + final boolean[] flagsFoundWithExpectedValue = new boolean[NUMBER_OF_FLAGS_TO_CHECK]; + Recording recording = null; + try { + recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + recording.start(); + recording.stop(); + + for (final RecordedEvent event : Events.fromRecording(recording)) { + final String recordedFlagName = Events.assertField(event, "name").getValue(); + final long value = Events.assertField(event, "value").getValue(); + switch (recordedFlagName) { + case "MinTLABSize": { + flagsFoundWithExpectedValue[0] = MIN_TLAB_SIZE_FLAG_VALUE == value; + continue; + } + case "OldSize": { + flagsFoundWithExpectedValue[1] = OLD_SIZE_FLAG_VALUE == value; + continue; + } + case "YoungPLABSize": { + flagsFoundWithExpectedValue[2] = YOUNG_PLAB_SIZE_FLAG_VALUE == value; + continue; + } + case "MaxDirectMemorySize": { + flagsFoundWithExpectedValue[3] = MAX_DIRECT_MEMORY_SIZE_FLAG_VALUE == value; + continue; + } + default: { + continue; + } + } + } + + for (int i = 0; i < flagsFoundWithExpectedValue.length; ++i) { + assertTrue(flagsFoundWithExpectedValue[i], "Flag not found or value error!"); + } + + } catch (Throwable e) { + if (recording != null) { + recording.dump(Paths.get("failed.jfr")); + } + throw e; + } finally { + if (recording != null) { + recording.close(); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestSystemPropertyEvent.java 2019-02-08 18:33:40.199926438 +0300 @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import java.util.HashMap; +import java.util.Map; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.runtime.TestSystemPropertyEvent + */ +public class TestSystemPropertyEvent { + + private final static String EVENT_NAME = EventNames.InitialSystemProperty; + + public static void main(String[] args) throws Throwable { + Map systemProps = createInitialSystemProperties(); + // Recording should only contain properties defined at JVM start. + // This property should not be included. + System.setProperty("party.pooper", "buh!"); + + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + + Map eventProps = new HashMap<>(); + for (RecordedEvent event : Events.fromRecording(recording)) { + String key = Events.assertField(event, "key").notEmpty().getValue(); + String value = Events.assertField(event, "value").notNull().getValue(); + if (!eventProps.containsKey(key)) { + // Event is received at both start and end of chunk. Only log first. + System.out.println("Event:" + event); + } + eventProps.put(key, value); + } + Asserts.assertGreaterThan(eventProps.size(), 4, "Should have at least 5 events"); + + // Value of System.properties may change. We can not expect all values in + // events and System.getProperties() to be equal. + // To do some verification of property values we require that at least one + // property with non-empty value is equal. + int countEqualAndNonEmpty = 0; + + String missingKeys = ""; + for (String key : eventProps.keySet()) { + if (!systemProps.containsKey(key)) { + missingKeys += key + " "; + continue; + } + if (isEqualAndNonEmpty(key, eventProps.get(key), systemProps.get(key))) { + countEqualAndNonEmpty++; + } + } + + if (!missingKeys.isEmpty()) { + Asserts.fail("Event properties not found in System.properties(): " + missingKeys); + } + Asserts.assertTrue(countEqualAndNonEmpty > 0, "No property had expected value"); + } + + private static boolean isEqualAndNonEmpty(String key, String eventValue, String systemValue) { + if (eventValue == null || systemValue == null || eventValue.isEmpty() || systemValue.isEmpty()) { + return false; + } + boolean isEquals = eventValue.equals(systemValue); + System.out.printf("eq=%b, key='%s', event='%s', system='%s'%n", isEquals, key, eventValue, systemValue); + return isEquals; + } + + private static Map createInitialSystemProperties() { + Map result = new HashMap<>(); + for (Object keyObject : System.getProperties().keySet()) { + String key = (String) keyObject; + result.put(key, System.getProperty(key)); + System.out.println("initialProp: " + key); + } + return result; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestThreadAllocationEvent.java 2019-02-08 18:33:40.335921704 +0300 @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import static jdk.test.lib.Asserts.assertGreaterThan; +import static jdk.test.lib.Asserts.assertTrue; + +import java.lang.management.ManagementFactory; +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CountDownLatch; + +import com.sun.management.ThreadMXBean; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * + * + * @run main/othervm -XX:-UseTLAB jdk.jfr.event.runtime.TestThreadAllocationEvent + */ + +/** + * The test will create a few threads that will allocate memory for a short time. + * During this time a number of thread_allocation events will be generated. + * The test will verify: + * 1. That number of allocated bytes is not decreasing for a thread. + * - This assumption is only true when not using TLABs. For this reason the + * test is run with -XX:-UseTLAB. When using TLABs, the code calculating the + * allocated bytes is using the current TLAB to do as good of an approximation + * as possible, but this introduces a race which might double count the current + * TLAB when it is full and in the middle of being switched out. + * 2. That sum of allocated bytes approximately matches value in ThreadMXBean. + */ +public class TestThreadAllocationEvent { + private static final String EVENT_NAME = EventNames.ThreadAllocationStatistics; + private static final String testThreadName = "testThread-"; + private static final long eventPeriodMillis = 50; + + // The value in both the JFR event and in the ThreadMXBean is documented as + // an "approximation" of number of bytes allocated. + // To not give any false errors, we allow an error margin of 5 mb. + // The test will typically allocate over 600 mb, so 5 mb is an error of less than 1%. + private static final long allowedTotalAllocatedDiff = 5000000; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withPeriod(Duration.ofMillis(eventPeriodMillis)); + recording.start(); + + AllocatorThread[] threads = new AllocatorThread[4]; + CountDownLatch allocationsDoneLatch = new CountDownLatch(threads.length); + for (int i = 0; i < threads.length; i++) { + threads[i] = new AllocatorThread(allocationsDoneLatch, 1000 * (i + 1)); + threads[i].setName(testThreadName + i); + threads[i].setDaemon(true); + threads[i].start(); + } + + // Take regular measurements while the threads are allocating memory. + // Stop measurement when all threads are ready. + try { + allocationsDoneLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + // Verify that number of allocated bytes is not decreasing. + recording.stop(); + verifyAllocationsNotDecreasing(Events.fromRecording(recording), threads); + + // Now allocations are done and threads are waiting to die. + // Make a new instant recording to get total number of allocated bytes. + // The reason for this extra recording is to make sure we get a JFR event + // after all allocations are done so we can compare the JFR value with + // the value reported by ThreadMXBean. + recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + verifyTotalAllocated(Events.fromRecording(recording), threads); + } + + /** + * Verify that the allocated value never decreases. + * We only compare our own allocator threads. The reason for that is that other threads + * may start/stop at any time, and we do not know if other thread names are unique. + */ + private static void verifyAllocationsNotDecreasing(List events, AllocatorThread[] threads) { + Collections.sort(events, (u,v) -> u.getEndTime().compareTo(v.getEndTime())); + long[] prevAllocated = new long[threads.length]; + for (RecordedEvent event : events) { + RecordedThread rt = Events.assertField(event, "thread").notNull().getValue(); // Check that we have a thread. + String name = rt.getJavaName(); + for (int i = 0; i < threads.length; i++) { + if (name.equals(threads[i].getName())) { + long curr = Events.assertField(event, "allocated").atLeast(prevAllocated[i]).getValue(); + prevAllocated[i] = curr; + } + } + } + + for (int i = 0; i < threads.length; i++) { + assertGreaterThan(prevAllocated[i], 0L, "No allocations for thread " + threads[i].getName()); + } + } + + /** + * Verify that total allocated bytes in JFR event approximately matches the value in ThreadMXBean. + */ + private static void verifyTotalAllocated(List events, AllocatorThread[] threads) { + boolean[] isEventFound = new boolean[threads.length]; + for (RecordedEvent event : events) { + RecordedThread rt = Events.assertField(event, "thread").notNull().getValue(); + String name = rt.getJavaName(); + for (int i = 0; i < threads.length; ++i) { + if (name.equals(threads[i].getName())) { + System.out.println("Event:" + event); + long maxAllowed = threads[i].totalAllocated + allowedTotalAllocatedDiff; + long minAllowed = Math.max(0, threads[i].totalAllocated - allowedTotalAllocatedDiff); + Events.assertField(event, "allocated").atLeast(minAllowed).atMost(maxAllowed); + isEventFound[i] = true; + } + } + } + for (int i = 0; i < threads.length; ++i) { + assertTrue(isEventFound[i], "No event for thread id " + i); + } + } + + /** + * Thread that does a number of allocations and records total number of + * bytes allocated as reported by ThreadMXBean. + */ + public static class AllocatorThread extends Thread { + private volatile long totalAllocated = -1; + private final int averageAllocationSize; + public byte[] buffer; + private final CountDownLatch allocationsDoneLatch; + + public AllocatorThread(CountDownLatch allocationsDoneLatch, int averageAllocationSize) { + this.allocationsDoneLatch = allocationsDoneLatch; + this.averageAllocationSize = averageAllocationSize; + } + + @Override + public void run() { + Random rand = new Random(); + int allocationSizeBase = averageAllocationSize / 2; + int allocationSizeRandom = averageAllocationSize; + for (int batches=0; batches<100; batches++) { + for (int i=0; i<1500; i++) { + buffer = new byte[rand.nextInt(allocationSizeRandom) + allocationSizeBase]; + } + try { + // No need to allocate too much data between JFR events, so do a short sleep. + Thread.sleep(eventPeriodMillis / 5); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + totalAllocated = getThreadAllocatedBytes(); + allocationsDoneLatch.countDown(); + + // Need to keep thread alive so we can get the final JFR event. + // This is a daemon thread, so it will finish when the main thread finishes. + while (true) { + Thread.yield(); + } + } + + private long getThreadAllocatedBytes() { + ThreadMXBean bean = (ThreadMXBean) ManagementFactory.getThreadMXBean(); + return bean.getThreadAllocatedBytes(Thread.currentThread().getId()); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestThreadCpuTimeEvent.java 2019-02-08 18:33:40.475916832 +0300 @@ -0,0 +1,276 @@ +/* + * 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.event.runtime; + +import com.sun.management.ThreadMXBean; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +import java.lang.management.ManagementFactory; +import java.time.Duration; +import java.time.Instant; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; +import java.util.stream.Collectors; + +/** + * @test + * @key jfr + * + * @library /lib / + * + * + * @run main/othervm jdk.jfr.event.runtime.TestThreadCpuTimeEvent + */ + +/** + */ +public class TestThreadCpuTimeEvent { + + public static void main(String[] args) throws Throwable { + testSimple(); + testCompareWithMXBean(); + testEventAtThreadExit(); + } + + private static final long eventPeriodMillis = 50; + private static final String cpuConsumerThreadName = "cpuConsumer"; + + // The cpu consumer will run for eventPeriodMillis times this factor to ensure that we see some + // events even if the scheduler isn't cooperating. + private static final long cpuConsumerRunFactor = 10; + + // The cpu consumer will run at least this number of loops, even if it takes longer than + // the requested period of time (in case the thread didn't get scheduled within the allotted time). + private static final long cpuConsumerMinCount = 1000000; + + static class CpuConsumingThread extends Thread { + + Duration runTime; + CyclicBarrier barrier; + volatile long counter; + + CpuConsumingThread(Duration runTime, CyclicBarrier barrier, String threadName) { + super(threadName); + this.runTime = runTime; + this.barrier = barrier; + } + + CpuConsumingThread(Duration runTime, CyclicBarrier barrier) { + this(runTime, barrier, cpuConsumerThreadName); + } + + @Override + public void run() { + try { + while (true) { + barrier.await(); + Instant start = Instant.now(); + counter = 0; + while ((Duration.between(start, Instant.now()).compareTo(runTime) < 0) || + (counter < cpuConsumerMinCount)) { + counter++; + } + barrier.await(); + } + } catch (BrokenBarrierException e) { + // Another thread has been interrupted - wait for us to be interrupted as well + while (!interrupted()) { + yield(); + } + } catch (InterruptedException e) { + // Normal way of stopping the thread + } + } + } + + // For a given thread, check that accumulated processTime >= cpuTime >= userTime. + // This may not hold for a single event instance due to differences in counter resolution + static void verifyPerThreadInvariant(List events, String threadName) { + List filteredEvents = events.stream() + .filter(e -> e.getThread().getJavaName().equals(threadName)) + .sorted(Comparator.comparing(RecordedEvent::getStartTime)) + .collect(Collectors.toList()); + + int numCpus = Runtime.getRuntime().availableProcessors(); + Iterator i = filteredEvents.iterator(); + while (i.hasNext()) { + RecordedEvent event = i.next(); + + Float systemLoad = (Float)event.getValue("system"); + Float userLoad = (Float)event.getValue("user"); + + Asserts.assertLessThan(systemLoad + userLoad, 1.01f / numCpus); // 100% + rounding errors + } + } + + static Duration getAccumulatedTime(List events, String threadName, String fieldName) { + List filteredEvents = events.stream() + .filter(e -> e.getThread().getJavaName().equals(threadName)) + .sorted(Comparator.comparing(RecordedEvent::getStartTime)) + .collect(Collectors.toList()); + + int numCpus = Runtime.getRuntime().availableProcessors(); + Iterator i = filteredEvents.iterator(); + RecordedEvent cur = i.next(); + Duration totalTime = Duration.ZERO; + while (i.hasNext()) { + RecordedEvent prev = cur; + cur = i.next(); + + Duration sampleTime = Duration.between(prev.getStartTime(), cur.getStartTime()); + Float load = (Float)cur.getValue(fieldName); + + // Adjust load to be thread-relative (fully loaded thread would give 100%) + Float totalLoadForThread = load * numCpus; + Duration threadTime = Duration.ofMillis((long) (sampleTime.toMillis() * totalLoadForThread)); + totalTime = totalTime.plus(threadTime); + } + + return totalTime; + } + + static List generateEvents(int minimumEventCount, CyclicBarrier barrier) throws Throwable { + int retryCount = 0; + + while (true) { + Recording recording = new Recording(); + + // Default period is once per chunk + recording.enable(EventNames.ThreadCPULoad).withPeriod(Duration.ofMillis(eventPeriodMillis)); + recording.start(); + + // Run a single pass + barrier.await(); + barrier.await(); + + recording.stop(); + List events = Events.fromRecording(recording); + + long numEvents = events.stream() + .filter(e -> e.getThread().getJavaName().equals(cpuConsumerThreadName)) + .count(); + + // If the JFR periodicals thread is really starved, we may not get enough events. + // In that case, we simply retry the operation. + if (numEvents < minimumEventCount) { + System.out.println("Not enough events recorded, trying again..."); + if (retryCount++ > 10) { + Asserts.fail("Retry count exceeded"); + throw new RuntimeException(); + } + } else { + return events; + } + } + } + + static void testSimple() throws Throwable { + Duration testRunTime = Duration.ofMillis(eventPeriodMillis * cpuConsumerRunFactor); + CyclicBarrier barrier = new CyclicBarrier(2); + CpuConsumingThread thread = new CpuConsumingThread(testRunTime, barrier); + thread.start(); + + List events = generateEvents(1, barrier); + verifyPerThreadInvariant(events, cpuConsumerThreadName); + + thread.interrupt(); + thread.join(); + } + + static void testCompareWithMXBean() throws Throwable { + Duration testRunTime = Duration.ofMillis(eventPeriodMillis * cpuConsumerRunFactor); + CyclicBarrier barrier = new CyclicBarrier(2); + CpuConsumingThread thread = new CpuConsumingThread(testRunTime, barrier); + thread.start(); + + List beforeEvents = generateEvents(2, barrier); + verifyPerThreadInvariant(beforeEvents, cpuConsumerThreadName); + + // Run a second single pass + barrier.await(); + barrier.await(); + + ThreadMXBean bean = (ThreadMXBean) ManagementFactory.getThreadMXBean(); + Duration cpuTime = Duration.ofNanos(bean.getThreadCpuTime(thread.getId())); + Duration userTime = Duration.ofNanos(bean.getThreadUserTime(thread.getId())); + + // Check something that should hold even in the presence of unfortunate scheduling + Asserts.assertGreaterThanOrEqual(cpuTime.toMillis(), eventPeriodMillis); + Asserts.assertGreaterThanOrEqual(userTime.toMillis(), eventPeriodMillis); + + Duration systemTimeBefore = getAccumulatedTime(beforeEvents, cpuConsumerThreadName, "system"); + Duration userTimeBefore = getAccumulatedTime(beforeEvents, cpuConsumerThreadName, "user"); + Duration cpuTimeBefore = userTimeBefore.plus(systemTimeBefore); + + Asserts.assertLessThan(cpuTimeBefore, cpuTime); + Asserts.assertLessThan(userTimeBefore, userTime); + Asserts.assertGreaterThan(cpuTimeBefore, Duration.ZERO); + + thread.interrupt(); + thread.join(); + } + + static void testEventAtThreadExit() throws Throwable { + Recording recording = new Recording(); + + recording.enable(EventNames.ThreadCPULoad).withPeriod(Duration.ofHours(10)); + recording.start(); + + Duration testRunTime = Duration.ofMillis(eventPeriodMillis * cpuConsumerRunFactor); + CyclicBarrier barrier = new CyclicBarrier(2); + CpuConsumingThread thread = new CpuConsumingThread(testRunTime, barrier); + + // Run a single pass + thread.start(); + barrier.await(); + barrier.await(); + + thread.interrupt(); + thread.join(); + + recording.stop(); + + List events = Events.fromRecording(recording); + verifyPerThreadInvariant(events, cpuConsumerThreadName); + + int exitingCount = 0; + for (RecordedEvent event : events) { + RecordedThread eventThread = event.getThread(); + if (eventThread.getJavaName().equals(cpuConsumerThreadName)) { + exitingCount++; + } + } + Asserts.assertEquals(exitingCount, 1); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestThreadDumpEvent.java 2019-02-08 18:33:40.619911821 +0300 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import static jdk.test.lib.Asserts.assertTrue; + +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventField; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.runtime.TestThreadDumpEvent + */ +public class TestThreadDumpEvent { + + private final static String EVENT_NAME = EventNames.ThreadDump; + private static final String RESULT_STRING_MATCH = "Full thread dump"; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + + List events = Events.fromRecording(recording); + boolean isAnyFound = false; + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + isAnyFound = true; + EventField dumpField = Events.assertField(event, "result"); + dumpField.instring(RESULT_STRING_MATCH).instring("TestThreadDumpEvent.main"); + } + assertTrue(isAnyFound, "Correct event not found"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestThreadParkEvent.java 2019-02-08 18:33:40.759906948 +0300 @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import static jdk.test.lib.Asserts.assertFalse; +import static jdk.test.lib.Asserts.assertTrue; + +import java.time.Duration; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.locks.LockSupport; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.management.ThreadMXBeanTool; +import jdk.test.lib.thread.TestThread; + + +/** + * @test + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.event.runtime.TestThreadParkEvent + */ + +public class TestThreadParkEvent { + private static final String EVENT_NAME = EventNames.ThreadPark; + private static final long THRESHOLD_MILLIS = 1; + + static class Blocker { + } + + public static void main(String[] args) throws Throwable { + final CountDownLatch stop = new CountDownLatch(1); + final Blocker blocker = new Blocker(); + TestThread parkThread = new TestThread(new Runnable() { + public void run() { + while (stop.getCount() > 0) { + LockSupport.park(blocker); + } + } + }); + + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(THRESHOLD_MILLIS)); + try { + recording.start(); + parkThread.start(); + ThreadMXBeanTool.waitUntilBlockingOnObject(parkThread, Thread.State.WAITING, blocker); + // sleep so we know the event is recorded + Thread.sleep(2 * THRESHOLD_MILLIS); + } finally { + stop.countDown(); + LockSupport.unpark(parkThread); + parkThread.join(); + recording.stop(); + } + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + boolean isAnyFound = false; + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + String klassName = Events.assertField(event, "parkedClass.name").notNull().getValue(); + if (klassName.equals(blocker.getClass().getName().replace('.', '/'))) { + assertFalse(isAnyFound, "Found more than 1 event"); + isAnyFound = true; + Events.assertField(event, "timeout").equal(0L); + Events.assertField(event, "address").notEqual(0L); + Events.assertEventThread(event, parkThread); + } + } + assertTrue(isAnyFound, "Correct event not found"); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestThreadSleepEvent.java 2019-02-08 18:33:40.911901658 +0300 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import static jdk.test.lib.Asserts.assertTrue; + +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.runtime.TestThreadSleepEvent + */ +public class TestThreadSleepEvent { + + private final static String EVENT_NAME = EventNames.ThreadSleep; + // Need to set the sleep time quite high (47 ms) since the sleep + // time on Windows has been proved unreliable. + // See bug 6313903 + private final static Long SLEEP_TIME_MS = new Long(47); + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + recording.start(); + Thread.sleep(SLEEP_TIME_MS); + recording.stop(); + + List events = Events.fromRecording(recording); + boolean isAnyFound = false; + for (RecordedEvent event : events) { + if (event.getThread().getJavaThreadId() == Thread.currentThread().getId()) { + System.out.println("Event:" + event); + isAnyFound = true; + Events.assertField(event, "time").equal(SLEEP_TIME_MS); + } + } + assertTrue(isAnyFound, "No matching events found"); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestThreadStartEndEvents.java 2019-02-08 18:33:41.047896925 +0300 @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import java.time.Duration; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.event.runtime.TestThreadStartEndEvents + */ + +/** + * Starts and stops a number of threads in order. + * Verifies that events are in the same order. + */ +public class TestThreadStartEndEvents { + private final static String EVENT_NAME_THREAD_START = EventNames.ThreadStart; + private final static String EVENT_NAME_THREAD_END = EventNames.ThreadEnd; + private static final String THREAD_NAME_PREFIX = "TestThread-"; + + public static void main(String[] args) throws Throwable { + // Test Java Thread Start event + Recording recording = new Recording(); + recording.enable(EVENT_NAME_THREAD_START).withThreshold(Duration.ofMillis(0)); + recording.enable(EVENT_NAME_THREAD_END).withThreshold(Duration.ofMillis(0)); + recording.start(); + LatchedThread[] threads = startThreads(); + stopThreads(threads); + recording.stop(); + + int currThreadIndex = 0; + long currentThreadId = Thread.currentThread().getId(); + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + if (event.getThread().getJavaThreadId() != currentThreadId) { + continue; + } + // Threads should be started and stopped in the correct order. + Events.assertEventThread(event, threads[currThreadIndex % threads.length]); + String eventName = currThreadIndex < threads.length ? EVENT_NAME_THREAD_START : EVENT_NAME_THREAD_END; + if (!eventName.equals(event.getEventType().getName())) { + throw new Exception("Expected event of tyoe " + eventName + " but got " + event.getEventType().getName()); + } + currThreadIndex++; + } + } + + private static LatchedThread[] startThreads() { + LatchedThread threads[] = new LatchedThread[10]; + ThreadGroup threadGroup = new ThreadGroup("TestThreadGroup"); + for (int i = 0; i < threads.length; i++) { + threads[i] = new LatchedThread(threadGroup, THREAD_NAME_PREFIX + i); + threads[i].startThread(); + System.out.println("Started thread id=" + threads[i].getId()); + } + return threads; + } + + private static void stopThreads(LatchedThread[] threads) { + for (LatchedThread thread : threads) { + thread.stopThread(); + while (thread.isAlive()) { + try { + Thread.sleep(5); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + + private static class LatchedThread extends Thread { + private final CountDownLatch start = new CountDownLatch(1); + private final CountDownLatch stop = new CountDownLatch(1); + + public LatchedThread(ThreadGroup threadGroup, String name) { + super(threadGroup, name); + } + + public void run() { + start.countDown(); + try { + stop.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public void startThread() { + this.start(); + try { + start.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public void stopThread() { + stop.countDown(); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestThrowableInstrumentation.java 2019-02-08 18:33:41.187892053 +0300 @@ -0,0 +1,61 @@ +/* + * 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.event.runtime; + +import sun.hotspot.WhiteBox; +import java.util.Objects; +import jdk.test.lib.Platform; + +/** + * @test + * @bug 8153324 + * @summary Verify instrumented Throwable bytecode by compiling it with C1. + * + * @library /lib / + * + * + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xbatch -XX:StartFlightRecording=dumponexit=true jdk.jfr.event.runtime.TestThrowableInstrumentation + */ +public class TestThrowableInstrumentation { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + private static int COMP_LEVEL_SIMPLE = 1; + + private static boolean isTieredCompilationEnabled() { + return Boolean.valueOf(Objects.toString(WHITE_BOX.getVMFlag("TieredCompilation"))); + } + + public static void main(String[] args) { + // Compile Throwable:: with C1 (if available) + if (!WHITE_BOX.enqueueInitializerForCompilation(java.lang.Throwable.class, COMP_LEVEL_SIMPLE)) { + if (!Platform.isServer() || isTieredCompilationEnabled() || Platform.isEmulatedClient()) { + throw new RuntimeException("Unable to compile Throwable:: with C1"); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestVMInfoEvent.flags 2019-02-08 18:33:41.323887321 +0300 @@ -0,0 +1 @@ ++FlightRecorder --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestVMInfoEvent.java 2019-02-08 18:33:41.463882449 +0300 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + + + +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.util.List; +import java.util.stream.Collectors; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * The test will verify that JVM Information event values are delivered + * and compare them with the RuntimeMXBean's values. + */ +public class TestVMInfoEvent { + private final static String EVENT_NAME = EventNames.JVMInformation; + + public static void main(String[] args) throws Exception { + RuntimeMXBean mbean = ManagementFactory.getRuntimeMXBean(); + Recording recording = new Recording(); + recording.enable(EVENT_NAME); + recording.start(); + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + Events.assertField(event, "jvmName").equal(mbean.getVmName()); + String jvmVersion = Events.assertField(event, "jvmVersion").notEmpty().getValue(); + if (!jvmVersion.contains(mbean.getVmVersion())) { + Asserts.fail(String.format("%s does not contain %s", jvmVersion, mbean.getVmVersion())); + } + + String jvmArgs = Events.assertField(event, "jvmArguments").notNull().getValue(); + String jvmFlags = Events.assertField(event, "jvmFlags").notNull().getValue(); + String eventArgs = (jvmFlags.trim() + " " + jvmArgs).trim(); + String beanArgs = mbean.getInputArguments().stream().collect(Collectors.joining(" ")); + Asserts.assertEquals(eventArgs, beanArgs, "Wrong inputArgs"); + + final String javaCommand = mbean.getSystemProperties().get("sun.java.command"); + Events.assertField(event, "javaArguments").equal(javaCommand); + Events.assertField(event, "jvmStartTime").equal(mbean.getStartTime()); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestVMInfoEvent.sh 2019-02-08 18:33:41.603877576 +0300 @@ -0,0 +1,35 @@ +# +# Copyright (c) 2013, 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. +# +# 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. +# +# @test +# @key jfr +# +# @library /lib / +# @build jdk.jfr.event.runtime.TestVMInfoEvent +# @run shell TestVMInfoEvent.sh + +echo ------------------------------------------------------------- +echo Launching test for `basename $0 .sh` +echo ------------------------------------------------------------- + +${TESTJAVA}/bin/java -cp ${TESTCLASSPATH} -XX:Flags=${TESTSRC}/TestVMInfoEvent.flags jdk.jfr.event.runtime.TestVMInfoEvent arg1 arg2 +exit $? --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestVMOperation.java 2019-02-08 18:33:41.743872705 +0300 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import java.time.Duration; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * + * + * @key jfr + * @library /lib / + * @run main/othervm -XX:+UseParallelGC jdk.jfr.event.runtime.TestVMOperation + */ +public class TestVMOperation { + + private static final String EVENT_NAME = EventNames.ExecuteVMOperation; + private static final String VM_OPERATION = "ParallelGCSystemGC"; + + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + recording.enable(EVENT_NAME).withThreshold(Duration.ofMillis(0)); + recording.start(); + System.gc(); + recording.stop(); + + List events = Events.fromRecording(recording); + Events.hasEvents(events); + for (RecordedEvent event : Events.fromRecording(recording)) { + String operation = Events.assertField(event, "operation").notEmpty().getValue(); + if (operation.equals(VM_OPERATION)) { + Events.assertField(event, "safepoint").equal(true); + Events.assertField(event, "blocking").equal(true); + RecordedThread jt = event.getValue("caller"); + if (Thread.currentThread().getName().equals(jt.getJavaName())) { + return; + } + } + } + throw new AssertionError("No matching event with VM operation name " + VM_OPERATION + " and current threasd as caller"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/TestVmFlagChangedEvent.java 2019-02-08 18:33:41.883867833 +0300 @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2013, 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.event.runtime; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertTrue; + +import java.lang.management.ManagementFactory; + +import com.sun.management.HotSpotDiagnosticMXBean; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test TestVmFlagChangedEvent + * @key jfr + * + * @library /lib / + * + * + * @run main/othervm jdk.jfr.event.runtime.TestVmFlagChangedEvent + */ +public final class TestVmFlagChangedEvent { + + public static void main(String[] args) throws Throwable { + EventFlag[] eventFlags = { + new EventFlag(EventNames.LongFlagChanged, "CMSWaitDuration", "2500"), + new EventFlag(EventNames.StringFlagChanged, "HeapDumpPath", "/a/sample/path"), + new EventFlag(EventNames.BooleanFlagChanged, "HeapDumpOnOutOfMemoryError", "true") + }; + + Recording recording = new Recording(); + for (EventFlag eventFlag : eventFlags) { + recording.enable(eventFlag.eventName); + } + + recording.start(); + HotSpotDiagnosticMXBean mbean = ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class); + for (EventFlag eventFlag : eventFlags) { + eventFlag.update(mbean); + } + recording.stop(); + + for (RecordedEvent event : Events.fromRecording(recording)) { + final String flagName = Events.assertField(event, "name").getValue(); + System.out.println("flag name=" + flagName); + for (EventFlag eventFlag : eventFlags) { + if (flagName.equals(eventFlag.eventLabel)) { + System.out.println("Event:" + event); + Object newValue = Events.assertField(event, "newValue").getValue(); + Object oldValue = Events.assertField(event, "oldValue").getValue(); + System.out.println("newValue:" + asText(newValue)); + System.out.println("oldValue:" + asText(oldValue)); + assertEquals(eventFlag.newValue, asText(newValue), "Wrong new value: expected" + eventFlag.newValue); + assertEquals(eventFlag.oldValue, asText(oldValue), "Wrong old value: expected" + eventFlag.oldValue); + Events.assertField(event, "origin").equal("Management"); + eventFlag.isFound = true; + break; + } + } + } + for (EventFlag eventFlag : eventFlags) { + assertTrue(eventFlag.isFound, "Missing flag change for: " + eventFlag.eventLabel); + } + } + + private static String asText(Object value) { + if (value == null) { + return ""; // HotSpotDiagnosticMXBean interface return "" for unset values + } + return String.valueOf(value); + } + + private static class EventFlag { + final String eventName; + final String eventLabel; + String newValue; + String oldValue; + boolean isFound = false; + + EventFlag(String eventName, String eventLabel, String newValue) { + this.eventName = eventName; + this.eventLabel = eventLabel; + this.newValue = newValue; + } + + void update(HotSpotDiagnosticMXBean mbean) { + this.oldValue = mbean.getVMOption(eventLabel).getValue(); + mbean.setVMOption(eventLabel, newValue); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/runtime/exception.security.policy 2019-02-08 18:33:42.023862961 +0300 @@ -0,0 +1,20 @@ +grant { + // must allow file reads so that jtreg itself and JFR can run + permission java.io.FilePermission "<>", "read"; + // must allow file delete so that JFR can delete repository + permission java.io.FilePermission "<>", "delete"; + // must allow file write so that the test can create the recording + permission java.io.FilePermission "<>", "write"; + + // need to be able to create temporary files + permission java.util.PropertyPermission "java.io.tmpdir", "read"; + permission java.util.PropertyPermission "user.dir", "read"; + + // need ManagementPermission to control JFR from the test + permission java.lang.management.ManagementPermission "control"; + permission java.lang.management.ManagementPermission "monitor"; + + // JDK-8019403 - access to sun.security.util, which is needed for creation of temp files, + // is not permitted automatically on solaris + permission java.lang.RuntimePermission "accessClassInPackage.sun.security.util"; +}; --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/sampling/TestNative.java 2019-02-08 18:33:42.163858089 +0300 @@ -0,0 +1,94 @@ +/* + * 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.event.sampling; + +import java.io.File; +import java.nio.file.Paths; +import java.time.Duration; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +/* + * @test + * @key jfr + * + * @library /lib / + * + * @run main/native jdk.jfr.event.sampling.TestNative + */ +public class TestNative { + + public final static String EVENT_SETTINGS_FILE = System.getProperty("test.src", ".") + File.separator + "sampling.jfc"; + public final static String JFR_DUMP = "samples.jfr"; + public final static String EXCEPTION = "No native samples found"; + public final static String NATIVE_EVENT = EventNames.NativeMethodSample; + public static Recording recording; + + public static native void longTime(); + + public static void main(String[] args) throws Exception { + String lib = System.getProperty("test.nativepath"); + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, "-Djava.library.path=" + lib, "jdk.jfr.event.sampling.TestNative$Test"); + + OutputAnalyzer output = ProcessTools.executeProcess(pb); + output.shouldHaveExitValue(0); + output.stdoutShouldNotContain("No native samples found"); + } + + static class Test { + public static void main(String[] args) throws Exception { + System.loadLibrary("TestNative"); + FlightRecorder.getFlightRecorder(); + recording = new Recording(); + recording.setToDisk(true); + recording.setDestination(Paths.get(JFR_DUMP)); + recording.enable(NATIVE_EVENT).withPeriod(Duration.ofMillis(10)); + recording.start(); + + longTime(); + + recording.stop(); + recording.close(); + + try (RecordingFile rf = new RecordingFile(Paths.get(JFR_DUMP))) { + while (rf.hasMoreEvents()) { + RecordedEvent re = rf.readEvent(); + if (re.getEventType().getName().equals(NATIVE_EVENT)) { + return; + } + } + } + + throw new Exception("No native samples found"); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/event/sampling/libTestNative.c 2019-02-08 18:33:42.303853217 +0300 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 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. + * + * 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. + * + */ + +#include + +#ifdef WINDOWS +#include +#else +#include +#endif + +JNIEXPORT void JNICALL Java_com_oracle_jfr_event_sampling_TestNative_longTime + (JNIEnv *env, jclass jc) +{ +#ifdef WINDOWS + Sleep(2*1000); +#else + usleep(2*1000*1000); +#endif +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/JcmdAsserts.java 2019-02-08 18:33:42.443848346 +0300 @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2015, 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.jcmd; + +import java.io.File; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.process.OutputAnalyzer; + + + +public class JcmdAsserts { + + private static final String NEW_LINE = System.getProperty("line.separator"); + + public static void assertJfrNotUsed(OutputAnalyzer output) { + output.shouldMatch("Flight Recorder has not been used"); + } + + public static void assertJfrUsed(OutputAnalyzer output) { + output.shouldMatch("Flight Recorder has been used"); + } + + public static void assertRecordingDumpedToFile(OutputAnalyzer output, File recording) { + output.shouldContain("Dumped recording"); + output.shouldContain(recording.getAbsolutePath()); + } + + public static void assertNotAbleToWriteToFile(OutputAnalyzer output) { + output.shouldContain("Could not start recording, not able to write to file"); + } + + public static void assertFileNotFoundException(OutputAnalyzer output, String name) { + output.shouldMatch("Could not write recording \"" + name + "\" to file.*"); + } + +// public static void assertNotAbleToSetFilename(OutputAnalyzer output) { +// output.shouldContain( +// "Filename can only be set for a recording with a duration, " + +// "or if dumponexit=true"); +// } + + public static void assertNotAbleToFindSettingsFile(OutputAnalyzer output) { + output.shouldContain("Could not parse setting"); + } + + public static void assertNoRecordingsAvailable(OutputAnalyzer output) { + output.shouldContain("No available recordings"); + } + + public static void assertRecordingNotExist(OutputAnalyzer output, String name) { + output.shouldContain("Could not find " + name); + } + + public static void assertRecordingNotRunning(OutputAnalyzer output, String name) { + output.shouldNotMatch(".*" + name + ".*running"); + } + + public static void assertRecordingIsRunning(OutputAnalyzer output, String name) { + output.shouldMatch(".*" + name + ".*running"); + } + + public static void assertRecordingHasStarted(OutputAnalyzer output) { + output.shouldContain("Started recording"); + } + + public static void assertCouldNotStartDefaultRecordingWithName(OutputAnalyzer output) { + output.shouldContain( + "It's not possible to set custom name for the defaultrecording"); + } + + public static void assertCouldNotStartDefaultRecording(OutputAnalyzer output) { + output.shouldContain( + "The only option that can be combined with defaultrecording is settings"); + } + + public static void assertRecordingIsUnstarted(OutputAnalyzer output, + String name, String duration) { + output.stdoutShouldMatch("^Recording \\d+: name=" + name + + " duration=" + duration + " .*\\W{1}unstarted\\W{1}"); + } + + public static void assertRecordingIsStopped(OutputAnalyzer output, String name) { + output.stdoutShouldMatch("^Recording \\d+: name=" + name + + " .*\\W{1}stopped\\W{1}"); + } + + public static void assertRecordingIsStopped(OutputAnalyzer output, String name, String duration) { + output.stdoutShouldMatch("^Recording \\d+: name=" + name + + " duration=" + duration + " .*\\W{1}stopped\\W{1}"); + } + + public static void assertStartTimeGreaterOrEqualThanMBeanValue(String name, + long actualStartTime) throws Exception { + Recording recording = findRecording(name); + Asserts.assertNotNull(recording.getStartTime(), "Start time is not set"); + Asserts.assertGreaterThanOrEqual(actualStartTime, recording.getStartTime().toEpochMilli()); + } + + public static void assertDelayAtLeast1s(OutputAnalyzer output) { + output.shouldContain("Could not start recording, delay must be at least 1 second."); + } + + public static void assertRecordingIsScheduled(OutputAnalyzer output, String name, String delay) { + output.stdoutShouldMatch( + "^\\s*Recording\\s+" + name + "\\s+scheduled to start in " + delay); + } + + public static void assertMaxSizeEqualsMBeanValue(String name, long maxSize) throws Exception { + Recording recording = findRecording(name); + Asserts.assertEquals(maxSize, recording.getMaxSize()); + } + + private static Recording findRecording(String name) { + for(Recording r : FlightRecorder.getFlightRecorder().getRecordings()) { + if (r.getName().equals(name)) { + return r; + } + } + throw new AssertionError("Could not find recording named " + name); + } + + public static void assertMaxAgeEqualsMBeanValue(String name, long maxAge) + throws Exception { + Recording recording = findRecording(name); + Asserts.assertNotNull(recording, "No recording found"); + Asserts.assertEquals(maxAge, recording.getMaxAge().toMillis()); + } + + public static void assertDurationEqualsMBeanValue(String name, + long duration) throws Exception { + Recording recording = findRecording(name); + Asserts.assertNotNull(recording, "No recording found"); + Asserts.assertEquals(duration, recording.getDuration().toMillis()); + } + + public static void assertDurationAtLeast1s(OutputAnalyzer output) { + output.shouldContain("Could not start recording, duration must be at least 1 second."); + } + + public static void assertStoppedRecording(OutputAnalyzer output, String name) { + output.shouldContain("Stopped recording \"" + name + "\""); + } + + public static void assertStoppedAndWrittenTo(OutputAnalyzer output, String name, File file) { + output.shouldMatch("^Stopped recording \"" + name + "\"" + ".*written to:"); + output.shouldContain(file.getAbsolutePath()); + } + + public static void assertStoppedDefaultRecording(OutputAnalyzer output) { + output.shouldContain("Stopped recording 0"); + } + + public static void assertThreadSleepThresholdIsSet(OutputAnalyzer output) throws Exception { + output.stdoutShouldMatch("\\s+\\W{1}" + EventNames.ThreadSleep + "\\W{1}" + + NEW_LINE + ".*threshold=1 ms.*"); + } + + public static void assertMonitorWaitThresholdIsSet(OutputAnalyzer output) throws Exception { + output.stdoutShouldMatch("\\s+\\W{1}" + EventNames.JavaMonitorWait + "\\W{1}" + + NEW_LINE + ".*threshold=1 ms.*"); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/JcmdHelper.java 2019-02-08 18:33:42.583843474 +0300 @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2015, 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.jcmd; + +import java.io.File; +import java.util.Arrays; +import java.util.stream.Collectors; + +import jdk.test.lib.Asserts; +import jdk.test.lib.dcmd.CommandExecutor; +import jdk.test.lib.dcmd.PidJcmdExecutor; +import jdk.test.lib.process.OutputAnalyzer; + +public class JcmdHelper { + + // Wait until recording's state became running + public static void waitUntilRunning(String name) throws Exception { + long timeoutAt = System.currentTimeMillis() + 10000; + while (true) { + OutputAnalyzer output = jcmdCheck(name, false); + try { + // The expected output can look like this: + // Recording 1: name=1 (running) + output.shouldMatch("^Recording \\d+: name=" + name + + " .*\\W{1}running\\W{1}"); + return; + } catch (RuntimeException e) { + if (System.currentTimeMillis() > timeoutAt) { + Asserts.fail("Recording not started: " + name); + } + Thread.sleep(100); + } + } + } + + public static void stopAndCheck(String name) throws Exception { + jcmd("JFR.stop", "name=\"" + name + "\""); + assertRecordingNotRunning(name); + } + + public static void stopWriteToFileAndCheck(String name, File file) throws Exception { + OutputAnalyzer output = jcmd("JFR.stop", + "name=\"" + name + "\"", + "filename=\"" + file.getAbsolutePath() + "\""); + JcmdAsserts.assertStoppedAndWrittenTo(output, name, file); + assertRecordingNotRunning(name); + } + + public static void stopCompressAndCheck(String name, File file) throws Exception { + OutputAnalyzer output = jcmd("JFR.stop", + "name=\"" + name + "\"", + "compress=true", + "filename=\"" + file.getAbsolutePath() + "\""); + JcmdAsserts.assertStoppedAndWrittenTo(output, name, file); + checkAndAssertNoRecordingsAvailable(); + } + + public static void stopDefaultRecordingAndCheck() throws Exception { + OutputAnalyzer output = jcmd("JFR.stop", "recording=0"); + JcmdAsserts.assertStoppedDefaultRecording(output); + checkAndAssertNoRecordingsAvailable(); + } + + public static void checkAndAssertNoRecordingsAvailable() throws Exception { + OutputAnalyzer output = jcmd("JFR.check"); + JcmdAsserts.assertNoRecordingsAvailable(output); + } + + public static void assertRecordingNotExist(String name) throws Exception { + OutputAnalyzer output = jcmdCheck(name, false); + JcmdAsserts.assertRecordingNotExist(output, name); + } + + public static void assertRecordingNotRunning(String name) throws Exception { + OutputAnalyzer output = jcmdCheck(name, false); + JcmdAsserts.assertRecordingNotRunning(output, name); + } + + public static void assertRecordingIsRunning(String name) throws Exception { + OutputAnalyzer output = jcmdCheck(name, false); + JcmdAsserts.assertRecordingIsRunning(output, name); + } + + public static OutputAnalyzer jcmd(int expectedExitValue, String... args) { + String argsString = Arrays.stream(args).collect(Collectors.joining(" ")); + CommandExecutor executor = new PidJcmdExecutor(); + OutputAnalyzer oa = executor.execute(argsString); + oa.shouldHaveExitValue(expectedExitValue); + return oa; + } + + public static OutputAnalyzer jcmd(String... args) { + return jcmd(0, args); + } + + + public static OutputAnalyzer jcmdCheck(String recordingName, boolean verbose) { + return jcmd("JFR.check", "name=" + recordingName, "verbose=" + verbose); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TEST.properties 2019-02-08 18:33:42.727838464 +0300 @@ -0,0 +1,2 @@ +modules = jdk.jcmd + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdChangeLogLevel.java 2019-02-08 18:33:42.871833453 +0300 @@ -0,0 +1,82 @@ +/* + * 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.jcmd; + + + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +import jdk.test.lib.dcmd.JcmdExecutor; +import jdk.test.lib.dcmd.PidJcmdExecutor; + +/** + * @test TestJcmdLogLevelChange + * @key jfr + * @summary Test changing log level + * + * + * @library /lib / + * + * @run main/othervm -Xlog:jfr=info jdk.jfr.jcmd.TestJcmdChangeLogLevel + */ +public class TestJcmdChangeLogLevel { + public static void main(String[] args) throws Exception { + final String fileName = "jfr_trace.txt"; + final String findWhat = "[info][jfr] Flight Recorder initialized"; + boolean passed = false; + + JcmdExecutor je = new PidJcmdExecutor(); + je.execute("VM.log output='file=" + fileName + "' what='jfr=info'"); + je.execute("JFR.start duration=1s"); + List lines; + + do { + try { + lines = Files.readAllLines(Paths.get(fileName)); + } catch (IOException e) { + throw new Error(e); + } + for (String l : lines) { + if (l.toString().contains(findWhat)) { + passed = true; + break; + } + } + if (lines.size() > 100) { + break; /* did not find it */ + } + } while(!passed); + + if (!passed) { + throw new Error("Not found " + findWhat + " in stream" + lines); + } + + System.out.println("PASSED"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdConfigure.java 2019-02-08 18:33:43.011828581 +0300 @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2015, 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.jcmd; + +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.internal.Options; +import jdk.test.lib.Asserts; + +/** + * @test + * @summary The test verifies JFR.configure command + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.jcmd.TestJcmdConfigure + */ +public class TestJcmdConfigure { + + private static final String DUMPPATH = "dumppath"; + private static final String STACK_DEPTH = "stackdepth"; + private static final String GLOBAL_BUFFER_COUNT = "globalbuffercount"; + private static final String GLOBAL_BUFFER_SIZE = "globalbuffersize"; + private static final String THREAD_BUFFER_SIZE = "thread_buffer_size"; + private static final String MAX_CHUNK_SIZE = "maxchunksize"; + private static final String SAMPLE_THREADS = "samplethreads"; + private static final String UNSUPPORTED_OPTION = "unsupportedoption"; + + public static void main(String[] args) throws Exception { + // + // Simple sanity tests against what is available in Java, + // before Flight Recorder is loaded. To do: + // + // - set values when JFR is running, check for errors. + // - validate against output from JFR.configure + // - where feasible, check if they are respected + // + + String dumpPath = Files.createTempDirectory("dump-path").toAbsolutePath().toString(); + + test(DUMPPATH, dumpPath); + test(STACK_DEPTH, 15); + test(GLOBAL_BUFFER_COUNT, 7); + test(GLOBAL_BUFFER_SIZE, 6); + test(THREAD_BUFFER_SIZE, 5); + test(MAX_CHUNK_SIZE, 14 * 1000 * 1000); + test(SAMPLE_THREADS, false); + test(SAMPLE_THREADS, true); + testNegative(UNSUPPORTED_OPTION, 100000); + testNegative(MAX_CHUNK_SIZE, -500); + + if (!testExceptions.isEmpty()) { + for (Exception e : testExceptions) { + System.out.println("Error: " + e.getMessage()); + } + throw testExceptions.get(0); + } + } + + private static List testExceptions = new ArrayList<>(); + + private static void test(String configName, Object value) { + JcmdHelper.jcmd("JFR.configure", configName + "=" + value); + Object actualValue = getOption(configName); + System.out.format("Test param='%s', expected='%s', actual='%s'%n", configName, value, actualValue); + try { + // Need convert to string to compare Integer and Long + Asserts.assertEquals(value.toString(), actualValue.toString(), "Wrong JFR.configure " + configName); + } catch (Exception e) { + testExceptions.add(e); + } + } + + private static void testNegative(String configName, Object value) { + try { + // Syntactically invalid arguments are catched by the JCMD framework where an error code of 1 is returned. + // Syntactically valid arguments that are semantically invalid (invalid value ranges for example) are handled by JFR code, it will always return a value of 0. + JcmdHelper.jcmd(configName.equals(UNSUPPORTED_OPTION) ? 1 : 0, "JFR.configure", configName + "=" + value); + } catch(Exception e) { + testExceptions.add(e); + } + } + + private static Object getOption(String name) { + switch (name) { + case DUMPPATH: return Options.getDumpPath().toString(); + case STACK_DEPTH: return Options.getStackDepth(); + case GLOBAL_BUFFER_COUNT: return Options.getGlobalBufferCount(); + case GLOBAL_BUFFER_SIZE: return Options.getGlobalBufferSize(); + case THREAD_BUFFER_SIZE: return Options.getThreadBufferSize(); + case MAX_CHUNK_SIZE: return Options.getMaxChunkSize(); + case SAMPLE_THREADS: return Options.getSampleThreads(); + default: throw new RuntimeException("Unknown option " + name); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdDump.java 2019-02-08 18:33:43.147823849 +0300 @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2015, 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.jcmd; + +import java.io.File; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary The test verifies JFR.dump command + * @key jfr + * + * @library /lib / + * @run main/othervm -XX:FlightRecorderOptions:maxchunksize=1M jdk.jfr.jcmd.TestJcmdDump + */ +public class TestJcmdDump { + + static class StoppedEvent extends Event { + } + static class RunningEvent extends Event { + } + + private static final String[] names = { null, "r1" }; + private static final boolean booleanValues[] = { true, false }; + + public static void main(String[] args) throws Exception { + + // Create a stopped recording in the repository to complicate things + Recording r = new Recording(); + r.start(); + StoppedEvent de = new StoppedEvent(); + de.commit(); + r.stop(); + + // The implementation of JFR.dump touch code that can't be executed using the + // Java API. It is therefore important to try all combinations. The + // implementation is non-trivial and depends on the combination + for (String name : names) { + for (boolean disk : booleanValues) { + try (Recording r1 = new Recording(); Recording r2 = new Recording()) { + System.out.println(); + System.out.println(); + System.out.println("Starting recordings with disk=" + disk); + r1.setToDisk(disk); + // To complicate things, only enable OldObjectSample for one recording + r1.enable(EventNames.OldObjectSample).withoutStackTrace(); + r1.setName("r1"); + r2.setToDisk(disk); + r2.setName("r2"); + r1.start(); + r2.start(); + + // Expect no path to GC roots + jfrDump(Boolean.FALSE, name, disk, rootCount -> rootCount == 0); + // Expect path to GC roots + jfrDump(null, name, disk, rootCount -> rootCount == 0); + // Expect at least one path to a GC root + jfrDump(Boolean.TRUE, name, disk, rootCount -> rootCount > 0); + } + } + } + r.close(); // release recording data from the stopped recording + } + + private static void jfrDump(Boolean pathToGCRoots, String name, boolean disk, Predicate successPredicate) throws Exception { + List leakList = new ArrayList<>(); + leakList.add(new Object[1000_0000]); + System.gc(); + while (true) { + RunningEvent re = new RunningEvent(); + re.commit(); + leakList.add(new Object[1000_0000]); + leakList.add(new Object[1000_0000]); + leakList.add(new Object[1000_0000]); + System.gc(); // This will shorten time for object to be emitted. + File recording = new File("TestJCMdDump.jfr"); + String[] params = buildParameters(pathToGCRoots, name, recording); + OutputAnalyzer output = JcmdHelper.jcmd(params); + JcmdAsserts.assertRecordingDumpedToFile(output, recording); + int rootCount = 0; + int oldObjectCount = 0; + int stoppedEventCount = 0; + int runningEventCount = 0; + for (RecordedEvent e : RecordingFile.readAllEvents(recording.toPath())) { + if (e.getEventType().getName().equals(EventNames.OldObjectSample)) { + if (e.getValue("root") != null) { + rootCount++; + } + oldObjectCount++; + } + if (e.getEventType().getName().equals(StoppedEvent.class.getName())) { + stoppedEventCount++; + } + if (e.getEventType().getName().equals(RunningEvent.class.getName())) { + runningEventCount++; + } + } + System.out.println("Name: " + name); + System.out.println("Disk: " + disk); + System.out.println("Path to GC roots: " + pathToGCRoots); + System.out.println("Old Objects: " + oldObjectCount); + System.out.println("Root objects: "+ rootCount); + System.out.println("Stopped events: "+ stoppedEventCount); + System.out.println("Running events: "+ runningEventCount); + + System.out.println(); + if (runningEventCount == 0) { + throw new Exception("Missing event from running recording"); + } + if (name == null && stoppedEventCount == 0) { + throw new Exception("Missing event from stopped recording"); + } + if (name != null && stoppedEventCount > 0) { + throw new Exception("Stopped event should not be part of dump"); + } + if (oldObjectCount != 0 && successPredicate.test(rootCount)) { + return; + } + System.out.println(); + System.out.println(); + System.out.println(); + System.out.println("************* Retrying! **************"); + Files.delete(recording.toPath()); + } + } + + private static String[] buildParameters(Boolean pathToGCRoots, String name, File recording) { + List params = new ArrayList<>(); + params.add("JFR.dump"); + params.add("filename=" + recording.getAbsolutePath()); + if (pathToGCRoots != null) { // if path-to-gc-roots is omitted, default is used (disabled). + params.add("path-to-gc-roots=" + pathToGCRoots); + } + if (name != null) { // if name is omitted, all recordings will be dumped + params.add("name=" + name); + } + return params.toArray(new String[0]); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdDumpGeneratedFilename.java 2019-02-08 18:33:43.283819117 +0300 @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2015, 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.jcmd; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Iterator; +import java.lang.management.ManagementFactory; + +import jdk.jfr.Configuration; +import jdk.jfr.Recording; +import jdk.test.lib.jfr.FileHelper; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary The test verifies JFR.dump command + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jcmd.TestJcmdDumpGeneratedFilename + */ +public class TestJcmdDumpGeneratedFilename { + + public static void main(String[] args) throws Exception { + // Increase the id for a recording + for (int i = 0; i < 300; i++) { + new Recording(); + } + try (Recording r = new Recording(Configuration.getConfiguration("default"))) { + r.start(); + r.stop(); + testDumpFilename(); + testDumpFilename(r); + testDumpDiectory(); + testDumpDiectory(r); + } + } + + private static void testDumpFilename() throws Exception { + OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump"); + verifyFile(readFilename(output), null); + } + + private static void testDumpFilename(Recording r) throws Exception { + OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "name=" + r.getId()); + verifyFile(readFilename(output), r.getId()); + } + + private static void testDumpDiectory() throws Exception { + Path directory = Paths.get(".").toAbsolutePath().normalize(); + OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + directory); + String filename = readFilename(output); + verifyFile(filename, null); + verifyDirectory(filename, directory); + } + + private static void testDumpDiectory(Recording r) throws Exception { + Path directory = Paths.get(".").toAbsolutePath().normalize(); + OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "name=" + r.getId(), "filename=" + directory); + String filename = readFilename(output); + verifyFile(filename, r.getId()); + verifyDirectory(filename, directory); + } + + private static void verifyDirectory(String filename, Path directory) throws Exception { + if (!filename.contains(directory.toAbsolutePath().normalize().toString())) { + throw new Exception("Expected dump to be at " + directory); + } + } + + private static long getProcessId() { + + // something like '@', at least in SUN / Oracle JVMs + final String jvmName = ManagementFactory.getRuntimeMXBean().getName(); + + final int index = jvmName.indexOf('@'); + + if (index < 1) { + // part before '@' empty (index = 0) / '@' not found (index = -1) + return 42; + } + + try { + return Long.parseLong(jvmName.substring(0, index)); + } catch (NumberFormatException e) { + // ignore + } + return 42; + } + + private static void verifyFile(String filename, Long id) throws Exception { + String idText = id == null ? "" : "-id-" + Long.toString(id); + String expectedName = "hotspot-pid-" + getProcessId() + idText; + if (!filename.contains(expectedName)) { + throw new Exception("Expected filename to contain " + expectedName); + } + FileHelper.verifyRecording(new File(filename)); + } + + private static String readFilename(OutputAnalyzer output) throws Exception { + Iterator it = output.asLines().iterator(); + while (it.hasNext()) { + String line = it.next(); + if (line.contains("written to")) { + line = it.next(); // blank line + return it.next(); + } + } + throw new Exception("Could not find filename of dumped recording."); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdDumpLimited.java 2019-02-08 18:33:43.423814245 +0300 @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2015, 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.jcmd; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import jdk.jfr.Event; +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary The test verifies JFR.dump command + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jcmd.TestJcmdDumpLimited + */ +public class TestJcmdDumpLimited { + + static class TestEvent extends Event { + int id; + int number; + } + + static class TestRecording { + Instant time; + final Recording r; + Path path; + int size; + int total; + int id; + Instant now; + + TestRecording(int id, int events) throws IOException, InterruptedException { + r = new Recording(); + r.start(); + for (int i = 0; i < events; i++) { + TestEvent event = new TestEvent(); + event.id = id; + event.number = i; + event.commit(); + if (i == events / 2) { + time = Instant.now(); + } + } + r.stop(); + Thread.sleep(1); + path = Paths.get("dump-" + id + ".jfr"); + r.dump(path); + size = (int) Files.size(path); + this.id = id; + this.now = Instant.now(); + } + + public void close() { + r.close(); + } + } + + private static long totalSize; + private static long lastFiveSize; + private static long firstFiveSize; + private static long middleSize; + private static long centerSize; + private static long lastSize; + + private static Instant middle; + private static Instant centerLeft; + private static Instant centerRight; + + public static void main(String[] args) throws Exception { + + List recs = new ArrayList<>(); + + for (int i = 0; i < 9; i++) { + recs.add(new TestRecording(i, 100)); + } + int last = 0; + List reversed = new ArrayList<>(recs); + Collections.reverse(reversed); + for (TestRecording r : reversed) { + r.total = r.size + last; + last += r.size; + } + + for (TestRecording r : recs) { + System.out.println("Recording " + r.id + ": size=" + r.size + " (total=" + r.total + ", time=" + r.now + ")"); + } + + centerLeft = recs.get(3).time; + middle = recs.get(4).time; + centerRight = recs.get(5).time; + + totalSize = size(recs, 0, 9); + lastFiveSize = size(recs, 4, 5); + firstFiveSize = size(recs, 0, 5); + middleSize = size(recs, 4, 1); + centerSize = size(recs, 3, 3); + lastSize = size(recs, 8, 1); + + testDump(); + testDumpMaxSize(); + testDumpMaxSizeSmall(); + testDumpBegin(); + testDumpEnd(); + testDumpBeginEndInstant(); + testDumpBeginEndLocalDateTime(); + testDumpBeginEndLocalTime(); + testDumpBeginEndSame(); + testDumpMaxAge(); + testDumpBeginEndRelative(); + testDumpTooEarly(); + testDumpTooLate(); + testDumpBeginMaxAge(); + TestDumpEndMaxage(); + testDumpEndBegin(); + testDumpInvalidTime(); + } + + private static int size(List recs, int skip, int limit) { + return recs.stream().skip(skip).limit(limit).mapToInt(r -> r.size).sum(); + } + + private static void testDumpEndBegin() throws Exception { + Path testEndBegin = Paths.get("testEndBegin.jfr"); + OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + testEndBegin.toFile().getAbsolutePath(), "begin=" + Instant.now(), "end=" + Instant.now().minusSeconds(200)); + output.shouldContain("Dump failed, begin must preceed end."); + assertMissingFile(testEndBegin); + } + + private static void TestDumpEndMaxage() throws Exception { + Path testEndMaxAge = Paths.get("testEndMaxAge.jfr"); + OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + testEndMaxAge.toFile().getAbsolutePath(), "end=" + Instant.now(), "maxage=2h"); + output.shouldContain("Dump failed, maxage can't be combined with begin or end."); + assertMissingFile(testEndMaxAge); + } + + private static Path testDumpBeginMaxAge() throws Exception { + Path testBeginMaxAge = Paths.get("testBeginMaxAge.jfr"); + OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + testBeginMaxAge.toFile().getAbsolutePath(), "begin=" + Instant.now().minusSeconds(100), "maxage=2h"); + output.shouldContain("Dump failed, maxage can't be combined with begin or end."); + assertMissingFile(testBeginMaxAge); + return testBeginMaxAge; + } + + private static void testDumpTooLate() throws Exception { + Path missing = Paths.get("missing2.jfr"); + OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + missing.toFile().getAbsolutePath(), "begin=" + Instant.now().plus(Duration.ofHours(1)), + "end=" + Instant.now().plus(Duration.ofHours(2))); + output.shouldContain("Dump failed. No data found in the specified interval."); + assertMissingFile(missing); + } + + private static void testDumpTooEarly() throws Exception { + Path missing = Paths.get("missing.jfr"); + OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + missing.toFile().getAbsolutePath(), "end=" + Instant.now().minus(Duration.ofHours(1))); + output.shouldContain("Dump failed. No data found in the specified interval."); + assertMissingFile(missing); + } + + private static void testDumpBeginEndRelative() throws IOException { + Path testBeginEndRelative = Paths.get("testBeginEndRelative.jfr"); + JcmdHelper.jcmd("JFR.dump", "filename=" + testBeginEndRelative.toFile().getAbsolutePath(), "begin=-3h", "end=-0s"); + Asserts.assertEquals(totalSize, Files.size(testBeginEndRelative), "Expected dump with begin=-3h end=0s to contain data from all recordings"); + Files.delete(testBeginEndRelative); + } + + private static void testDumpMaxAge() throws IOException { + Path testMaxAge = Paths.get("testMaxAge.jfr"); + JcmdHelper.jcmd("JFR.dump", "filename=" + testMaxAge.toFile().getAbsolutePath(), "maxage=2h"); + Asserts.assertEquals(totalSize, Files.size(testMaxAge), "Expected dump with maxage=2h to contain data from all recordings"); + Files.delete(testMaxAge); + } + + private static void testDumpBeginEndSame() throws IOException { + Path testBeginEnd = Paths.get("testBeginEndSame.jfr"); + JcmdHelper.jcmd("JFR.dump", "filename=" + testBeginEnd.toFile().getAbsolutePath(), "begin=" + middle, "end=" + middle); + Asserts.assertEquals(middleSize, Files.size(testBeginEnd), "Expected dump with begin=" + middle + "end=" + middle + " contain data from middle recording"); + Files.delete(testBeginEnd); + } + + private static void testDumpBeginEndInstant() throws IOException { + Path testBeginEnd = Paths.get("testBeginEndInstant.jfr"); + JcmdHelper.jcmd("JFR.dump", "filename=" + testBeginEnd.toFile().getAbsolutePath(), "begin=" + centerLeft, "end=" + centerRight); + Asserts.assertEquals(centerSize, Files.size(testBeginEnd), "Expected dump with begin=" + centerLeft + " end=" + centerRight + " contain data from the 'center'-recordings"); + Files.delete(testBeginEnd); + } + + private static void testDumpBeginEndLocalDateTime() throws IOException { + LocalDateTime centerLeftLocal = LocalDateTime.ofInstant(centerLeft, ZoneOffset.systemDefault()); + LocalDateTime centerRightLocal = LocalDateTime.ofInstant(centerRight, ZoneOffset.systemDefault()); + Path testBeginEnd = Paths.get("testBeginEndLocalDateTime.jfr"); + JcmdHelper.jcmd("JFR.dump", "filename=" + testBeginEnd.toFile().getAbsolutePath(), "begin=" + centerLeftLocal, "end=" + centerRightLocal); + Asserts.assertEquals(centerSize, Files.size(testBeginEnd), "Expected dump with begin=" + centerLeftLocal + " end=" + centerRightLocal + " contain data from the 'center'-recordings"); + Files.delete(testBeginEnd); + } + + private static void testDumpBeginEndLocalTime() throws IOException { + //LocalTime centerLeftLocal = LocalTime.ofInstant(centerLeft, ZoneOffset.systemDefault()); + LocalTime centerLeftLocal = LocalDateTime.ofInstant(centerLeft, ZoneOffset.systemDefault()).toLocalTime(); + //LocalTime centerRightLocal = LocalTime.ofInstant(centerRight, ZoneOffset.systemDefault()); + LocalTime centerRightLocal = LocalDateTime.ofInstant(centerRight, ZoneOffset.systemDefault()).toLocalTime(); + Path testBeginEnd = Paths.get("testBeginEndLocalTime.jfr"); + JcmdHelper.jcmd("JFR.dump", "filename=" + testBeginEnd.toFile().getAbsolutePath(), "begin=" + centerLeftLocal, "end=" + centerRightLocal); + Asserts.assertEquals(centerSize, Files.size(testBeginEnd), "Expected dump with begin=" + centerLeftLocal + " end=" + centerRightLocal + " contain data from the 'center'-recordings"); + Files.delete(testBeginEnd); + } + + private static void testDumpEnd() throws IOException { + Path testEnd = Paths.get("testEnd.jfr"); + JcmdHelper.jcmd("JFR.dump", "filename=" + testEnd.toFile().getAbsolutePath(), "end=" + middle); + Asserts.assertEquals(firstFiveSize, Files.size(testEnd), "Expected dump with end=" + middle + " to contain data from the five first recordings"); + Files.delete(testEnd); + } + + private static void testDumpBegin() throws IOException { + Path testBegin = Paths.get("testBegin.jfr"); + JcmdHelper.jcmd("JFR.dump", "filename=" + testBegin.toFile().getAbsolutePath(), "begin=" + middle); + Asserts.assertEquals(lastFiveSize, Files.size(testBegin), "Expected dump with begin=" + middle + " to contain data from the last five recordings"); + Files.delete(testBegin); + } + + private static void testDumpMaxSize() throws IOException { + Path testMaxSize = Paths.get("testMaxSize.jfr"); + JcmdHelper.jcmd("JFR.dump", "filename=" + testMaxSize.toFile().getAbsolutePath(), "maxsize=" + lastFiveSize); + Asserts.assertEquals(lastFiveSize, Files.size(testMaxSize), "Expected dump with maxsize=" + lastFiveSize + " to contain data from the last five recordings"); + Files.delete(testMaxSize); + } + + private static void testDumpMaxSizeSmall() throws IOException { + Path testMaxSizeSmall = Paths.get("testMaxSizeSmall.jfr"); + JcmdHelper.jcmd("JFR.dump", "filename=" + testMaxSizeSmall.toFile().getAbsolutePath(), "maxsize=1k"); + Asserts.assertEquals(lastSize, Files.size(testMaxSizeSmall), "Expected dump with maxsize=1k to contain data from the last recording"); + Files.delete(testMaxSizeSmall); + } + + private static void testDump() throws IOException { + Path all = Paths.get("all.jfr"); + OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + all.toFile().getAbsolutePath()); + JcmdAsserts.assertRecordingDumpedToFile(output, all.toFile()); + Asserts.assertEquals(totalSize, Files.size(all), "Expected dump to be sum of all recordings"); + Files.delete(all); + } + + private static void testDumpInvalidTime() throws Exception { + Path invalidTime = Paths.get("invalidTime.jfr"); + OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + invalidTime.toFile().getAbsolutePath(), "begin=4711"); + output.shouldContain("Dump failed, not a valid begin time."); + assertMissingFile(invalidTime); + } + + private static void assertMissingFile(Path missing) throws Exception { + if (Files.exists(missing)) { + throw new Exception("Unexpected dumpfile found"); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdDumpPathToGCRoots.java 2019-02-08 18:33:43.559809513 +0300 @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2015, 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.jcmd; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.Enabled; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.consumer.RecordingFile; +import jdk.jfr.internal.test.WhiteBox; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @summary Start a recording with or without path-to-gc-roots + * + * + * @library /lib / + * @key jfr + * + * @run main/othervm -XX:TLABSize=2k jdk.jfr.jcmd.TestJcmdDumpPathToGCRoots + */ +public class TestJcmdDumpPathToGCRoots { + + private static final int OBJECT_COUNT = 100_000; + public static List leak = new ArrayList<>(OBJECT_COUNT); + + public static void main(String[] args) throws Exception { + WhiteBox.setWriteAllObjectSamples(true); + + String settingName = EventNames.OldObjectSample + "#" + "cutoff"; + + // dump parameter trumps previous setting + testDump("path-to-gc-roots=true", Collections.singletonMap(settingName, "infinity"), true); + testDump("path-to-gc-roots=true", Collections.singletonMap(settingName, "0 ns"), true); + testDump("path-to-gc-roots=true", Collections.emptyMap(), true); + + testDump("path-to-gc-roots=false", Collections.singletonMap(settingName, "infinity"), false); + testDump("path-to-gc-roots=false", Collections.singletonMap(settingName, "0 ns"), false); + testDump("path-to-gc-roots=false", Collections.emptyMap(), false); + + testDump("", Collections.singletonMap(settingName, "infinity"), true); + testDump("", Collections.singletonMap(settingName, "0 ns"), false); + testDump("", Collections.emptyMap(), false); + } + + private static void testDump(String pathToGcRoots, Map settings, boolean expectedChains) throws Exception { + try (Recording r = new Recording()) { + Map p = new HashMap<>(settings); + p.put(EventNames.OldObjectSample + "#" + Enabled.NAME, "true"); + r.setName("dodo"); + r.setSettings(p); + r.setToDisk(true); + r.start(); + clearLeak(); + System.out.println("Recording id: " + r.getId()); + System.out.println("Settings: " + settings.toString()); + System.out.println("Command: JFR.dump " + pathToGcRoots); + System.out.println("Chains expected: " + expectedChains); + buildLeak(); + System.gc(); + System.gc(); + File recording = new File("TestJcmdDumpPathToGCRoots" + r.getId() + ".jfr"); + recording.delete(); + JcmdHelper.jcmd("JFR.dump", "name=dodo", pathToGcRoots, "filename=" + recording.getAbsolutePath()); + r.setSettings(Collections.emptyMap()); + List events = RecordingFile.readAllEvents(recording.toPath()); + if (events.isEmpty()) { + throw new Exception("No events found in recoding"); + } + boolean chains = hasChains(events); + if (expectedChains && !chains) { + System.out.println(events); + throw new Exception("Expected chains but found none"); + } + if (!expectedChains && chains) { + System.out.println(events); + throw new Exception("Didn't expect chains but found some"); + } + } + } + + private static void clearLeak() { + leak.clear(); + } + + private static boolean hasChains(List events) throws IOException { + for (RecordedEvent e : events) { + RecordedObject ro = e.getValue("object"); + if (ro.getValue("referrer") != null) { + return true; + } + } + return false; + } + + private static void buildLeak() { + for (int i = 0; i < OBJECT_COUNT;i ++) { + leak.add(new Object[0]); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdLegacy.java 2019-02-08 18:33:43.699804642 +0300 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 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.jcmd; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Utils; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.FileHelper; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test TestClassId + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.jcmd.TestJcmdLegacy + */ +public class TestJcmdLegacy { + + private static final String DIR = System.getProperty("test.src", "."); + private static final File SETTINGS = new File(DIR, "legacy.jfc"); + + private static final String LEGACY_EVENT = "com.oracle.jdk.JVMInformation"; + + public static void main(String... args) throws Exception { + testAPI(); + testJcmd(); + } + + private static void testJcmd() throws Exception { + String name = "testLegacy"; + Path p = Paths.get(name + ".jfr").toAbsolutePath().normalize(); + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", "name=" + name, "settings=" + SETTINGS.getCanonicalPath()); + JcmdAsserts.assertRecordingHasStarted(output); + JcmdHelper.waitUntilRunning(name); + JcmdHelper.stopWriteToFileAndCheck(name, p.toFile()); + FileHelper.verifyRecording(p.toFile()); + verify(p); + } + + private static void testAPI() throws IOException, Exception { + Path p = Utils.createTempFile("enable-legacy-event", ".jfr"); + + try (Recording r = new Recording()) { + r.enable(LEGACY_EVENT); + r.start(); + r.stop(); + r.dump(p); + verify(p); + } + } + + private static void verify(Path p) throws IOException, Exception { + for (RecordedEvent e : RecordingFile.readAllEvents(p)) { + System.out.println(e.getEventType().getName()); + if (e.getEventType().getName().equals(EventNames.JVMInformation)) { + return; + } + } + throw new Exception("Could not find legacy event"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdSaveToFile.java 2019-02-08 18:33:43.835799910 +0300 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2015, 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.jcmd; + +import java.io.File; + +import jdk.test.lib.jfr.FileHelper; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary The test verifies that recording can be written to a file both with JFR.start and JFR.stop + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jcmd.TestJcmdSaveToFile + */ +public class TestJcmdSaveToFile { + + public static void main(String[] args) throws Exception { + testStartAndSave(); + testStopAndSave(); + } + + private static void testStartAndSave() throws Exception { + String name = "testStartAndSave"; + File recording = new File(name + ".jfr"); + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", + "name=" + name, + "duration=1h", + "filename=" + recording.getAbsolutePath()); + JcmdAsserts.assertRecordingHasStarted(output); + JcmdHelper.waitUntilRunning(name); + JcmdHelper.stopAndCheck(name); + FileHelper.verifyRecording(recording); + } + + private static void testStopAndSave() throws Exception { + String name = "testStopAndSave"; + File recording = new File(name + ".jfr"); + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", "name=" + name); + JcmdAsserts.assertRecordingHasStarted(output); + JcmdHelper.waitUntilRunning(name); + JcmdHelper.stopWriteToFileAndCheck(name, recording); + FileHelper.verifyRecording(recording); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdStartDirNotExist.java 2019-02-08 18:33:43.971795178 +0300 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2015, 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.jcmd; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary Verify error when starting with a dir that does not exist. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jcmd.TestJcmdStartDirNotExist + */ +public class TestJcmdStartDirNotExist { + + public static void main(String[] args) throws Exception { + Path path = Paths.get(".", "dirDoesNotExist", "my.jfr"); + String name = "testStartWithIllegalFilename"; + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", + "name=" + name, + "duration=10s", + "filename=" + path.toString()); + JcmdAsserts.assertNotAbleToWriteToFile(output); + JcmdHelper.assertRecordingNotExist(name); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdStartInvaldFile.java 2019-02-08 18:33:44.111790307 +0300 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015, 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.jcmd; + +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary Verify error when starting with invalid file. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jcmd.TestJcmdStartInvaldFile + */ +public class TestJcmdStartInvaldFile { + + private final static String ILLEGAL_FILE_NAME = ":;/\\?"; + + public static void main(String[] args) throws Exception { + String name = "testStartWithIllegalFilename"; + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", + "name=" + name, + "duration=10s", + "filename=" + ILLEGAL_FILE_NAME); + JcmdAsserts.assertNotAbleToWriteToFile(output); + JcmdHelper.assertRecordingNotExist(name); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdStartPathToGCRoots.java 2019-02-08 18:33:44.247785575 +0300 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015, 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.jcmd; + +import java.util.List; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.test.lib.jfr.EventNames; + + +/** + * @test + * @summary Start a recording with or without path-to-gc-roots + * + * @library /lib / + * @key jfr + * + * @run main/othervm jdk.jfr.jcmd.TestJcmdStartPathToGCRoots + */ +public class TestJcmdStartPathToGCRoots { + + public static void main(String[] args) throws Exception { + + JcmdHelper.jcmd("JFR.start", "path-to-gc-roots=true"); + assertCutoff("infinity", "Expected cutoff to be '0 ns' wuth -XX:StartFlightRecording=path-to-gc-roots=true"); + closeRecording(); + + JcmdHelper.jcmd("JFR.start", "path-to-gc-roots=false"); + assertCutoff("0 ns", "Expected cutoff to be '0 ns' with -XX:StartFlightRecording=path-to-gc-roots=false"); + closeRecording(); + + JcmdHelper.jcmd("JFR.start"); + assertCutoff("0 ns", "Expected cutoff to be '0 ns' with -XX:StartFlightRecording="); + closeRecording(); + } + + private static void assertCutoff(String expected, String errorMessage) throws Exception { + List recordings = FlightRecorder.getFlightRecorder().getRecordings(); + if (recordings.isEmpty()) { + throw new Exception("Expected recording to be started"); + } + if (recordings.size() != 1) { + throw new Exception("Expected only one recording"); + } + + String settingName = EventNames.OldObjectSample + "#" + "cutoff"; + Recording r = recordings.get(0); + String cutoff = r.getSettings().get(settingName); + System.out.println(settingName + "=" + cutoff); + if (!expected.equals(cutoff)) { + throw new Exception(errorMessage); + } + r.close(); + } + + private static void closeRecording() { + for (Recording r : FlightRecorder.getFlightRecorder().getRecordings()) { + r.close(); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdStartReadOnlyFile.java 2019-02-08 18:33:44.391780566 +0300 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015, 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.jcmd; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.test.lib.jfr.FileHelper; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary Verify error when starting with read-only file. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jcmd.TestJcmdStartReadOnlyFile + */ +public class TestJcmdStartReadOnlyFile { + + public static void main(String[] args) throws Exception { + String name = "TestJcmdStartReadOnlyFile"; + Path readonlyFile = FileHelper.createReadOnlyFile(Paths.get(".", name + ".jfr")); + if (!FileHelper.isReadOnlyPath(readonlyFile)) { + System.out.println("Could not create read-only file. Ignoring test."); + return; + } + + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", + "name=" + name, + "duration=10s", + "filename=" + readonlyFile.toAbsolutePath()); + JcmdAsserts.assertNotAbleToWriteToFile(output); + JcmdHelper.assertRecordingNotExist(name); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdStartStopDefault.java 2019-02-08 18:33:44.527775833 +0300 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015, 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.jcmd; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.FileHelper; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary Start a recording without name. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jcmd.TestJcmdStartStopDefault + */ +public class TestJcmdStartStopDefault { + + public static void main(String[] args) throws Exception { + Path recording = Paths.get(".","TestJcmdStartStopDefault.jfr").toAbsolutePath().normalize(); + + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start"); + JcmdAsserts.assertRecordingHasStarted(output); + + String name = parseRecordingName(output); + JcmdHelper.waitUntilRunning(name); + + output = JcmdHelper.jcmd("JFR.dump", + "name=" + name, + "filename=" + recording); + JcmdAsserts.assertRecordingDumpedToFile(output, recording.toFile()); + JcmdHelper.stopAndCheck(name); + FileHelper.verifyRecording(recording.toFile()); + } + + private static String parseRecordingName(OutputAnalyzer output) { + // Expected output: + // Started recording recording-1. No limit (duration/maxsize/maxage) in use. + // Use JFR.dump name=recording-1 filename=FILEPATH to copy recording data to file. + + String stdout = output.getStdout(); + Pattern p = Pattern.compile(".*Use jcmd \\d+ JFR.dump name=(\\S+).*", Pattern.DOTALL); + Matcher m = p.matcher(stdout); + Asserts.assertTrue(m.matches(), "Could not parse recording name"); + String name = m.group(1); + System.out.println("Recording name=" + name); + return name; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdStartWithOptions.java 2019-02-08 18:33:44.667770962 +0300 @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2013, 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.jcmd; + +import java.io.File; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary The test verifies that recording can be started with options delay|duration|maxage|maxsize + * @key jfr + * + * @library /lib / + * @run main/othervm -XX:+FlightRecorder -XX:FlightRecorderOptions=maxchunksize=2097152 jdk.jfr.jcmd.TestJcmdStartWithOptions + */ +public class TestJcmdStartWithOptions { + + private static final String DIR = System.getProperty("test.src", "."); + private static final File SETTINGS = new File(DIR, "jcmd-testsettings3.jfc"); + + public static void main(String[] args) throws Exception { + testRecordingNotStartedTooEarly(); + testDelayLessThan1s(); + testDuration(); + testDurationLessThan1s(); + testMaxAge(); + testMaxSize(); + } + + static void testRecordingNotStartedTooEarly() throws Exception { + String name = "testRecordingNotStartedTooEarly"; + long delay = 2 * 1000; + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", + "name=" + name, + "delay=" + delay + "ms"); + JcmdAsserts.assertRecordingIsScheduled(output, "1", "2 s"); + for (Recording r : FlightRecorder.getFlightRecorder().getRecordings()) { + if (name.equals(r.getName())) { + while(r.getState() != RecordingState.RUNNING) { + Thread.sleep(10); + } + long currentTime = System.currentTimeMillis(); + long afterActualStart = currentTime + delay; + JcmdAsserts.assertStartTimeGreaterOrEqualThanMBeanValue(name, afterActualStart); + JcmdHelper.stopAndCheck(name); + return; + } + } + throw new Exception("Could not find recording with name " + name); + } + + private static void testDelayLessThan1s() throws Exception { + String name = "testDelayLessThan1s"; + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", + "name=" + name, + "delay=10ms"); + JcmdAsserts.assertDelayAtLeast1s(output); + output = JcmdHelper.jcmd("JFR.check"); + JcmdAsserts.assertNoRecordingsAvailable(output); + } + + private static void testDuration() throws Exception { + String name = "testDuration"; + long duration = 3600 * 1000; + String durationS = String.valueOf(duration / 1000) + "s" ; + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", + "name=" + name, + "duration=" + durationS); + JcmdAsserts.assertRecordingHasStarted(output); + JcmdHelper.waitUntilRunning(name); + JcmdAsserts.assertDurationEqualsMBeanValue(name, duration); + JcmdHelper.stopAndCheck(name); + } + + private static void testDurationLessThan1s() throws Exception { + String name = "testDurationLessThan1s"; + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", + "name=" + name, + "duration=10ms"); + JcmdAsserts.assertDurationAtLeast1s(output); + JcmdHelper.checkAndAssertNoRecordingsAvailable(); + } + + /** + * Check the maxage is the same as MBean value + */ + private static void testMaxAge() throws Exception { + String name = "testMaxAge"; + long maxAge = 2 * 1000; + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", + "name=" + name, + "settings=" + SETTINGS.getAbsolutePath(), + "maxage=2s"); + JcmdAsserts.assertRecordingHasStarted(output); + JcmdHelper.waitUntilRunning(name); + JcmdAsserts.assertMaxAgeEqualsMBeanValue(name, maxAge); + JcmdHelper.stopAndCheck(name); + } + + /** + * Check the maxsize is the same as MBean value + */ + private static void testMaxSize() throws Exception { + String name = "testMaxSize"; + long maxSize = 2 * 1024 * 1024; + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", + "name=" + name, + "settings=" + SETTINGS.getAbsolutePath(), + "maxsize=" + maxSize); + JcmdAsserts.assertRecordingHasStarted(output); + JcmdHelper.waitUntilRunning(name); + JcmdAsserts.assertMaxSizeEqualsMBeanValue(name, maxSize); + JcmdHelper.stopAndCheck(name); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdStartWithSettings.java 2019-02-08 18:33:44.803766231 +0300 @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2015, 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.jcmd; + +import java.io.File; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedThread; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary The test verifies that recording can be started with setting file(s) + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jcmd.TestJcmdStartWithSettings + */ +public class TestJcmdStartWithSettings { + + private static final String DIR = System.getProperty("test.src", "."); + private static final File SETTINGS = new File(DIR, "jcmd-testsettings.jfc"); + private static final File SETTINGS2 = new File(DIR, "jcmd-testsettings.2.jfc"); + + public static void main(String[] args) throws Exception { + testSingleSettingFile(); + testManySettingFiles(); + testPresetSettings(); + testNonExistingSettingFile(); + } + + private static void testSingleSettingFile() throws Exception { + String name = "testSingleSettingFile"; + File recording = new File(name + ".jfr"); + + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", + "name=" + name, + "duration=1h", + "settings=" + SETTINGS.getCanonicalPath(), + "filename=" + recording.getCanonicalPath()); + JcmdAsserts.assertRecordingHasStarted(output); + JcmdHelper.waitUntilRunning(name); + output = JcmdHelper.jcmdCheck(name, true); + JcmdAsserts.assertThreadSleepThresholdIsSet(output); + + Thread.sleep(100); + JcmdHelper.stopAndCheck(name); + assertHasEvent(recording, EventNames.ThreadSleep, Thread.currentThread().getName()); + } + + /** + * Start a recording with two setting files and + * verify Java Thread Sleep and Java Monitor Wait events have been recorded. + */ + private static void testManySettingFiles() throws Exception { + String name = "testManySettingFiles"; + File recording = new File(name + ".jfr"); + + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", + "name=" + name, + "duration=1h", + "settings=" + SETTINGS.getCanonicalPath(), + "settings=" + SETTINGS2.getCanonicalPath(), + "filename=" + recording.getCanonicalPath()); + JcmdAsserts.assertRecordingHasStarted(output); + JcmdHelper.waitUntilRunning(name); + output = JcmdHelper.jcmdCheck(name, true); + JcmdAsserts.assertThreadSleepThresholdIsSet(output); + JcmdAsserts.assertMonitorWaitThresholdIsSet(output); + + // Generate Monitor Wait event + ThreadWait threadWait = new ThreadWait(); + threadWait.start(); + Thread.sleep(300); + threadWait.join(); + + JcmdHelper.stopAndCheck(name); + assertHasEvent(recording, EventNames.ThreadSleep, Thread.currentThread().getName()); + assertHasEvent(recording, EventNames.JavaMonitorWait, threadWait.getName()); + } + + /** + * It should be possible to use "profile" as non-path preset, + * both with and without '.jfc' + */ + private static void testPresetSettings() throws Exception { + String name = "testPresetSettingsJfc"; + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", + "name=" + name, + "settings=profile.jfc"); + JcmdAsserts.assertRecordingHasStarted(output); + JcmdHelper.waitUntilRunning(name); + JcmdHelper.stopAndCheck(name); + + name = "testPresetSettingsNoJfc"; + output = JcmdHelper.jcmd("JFR.start", + "name=" + name, + "settings=profile"); + JcmdAsserts.assertRecordingHasStarted(output); + JcmdHelper.waitUntilRunning(name); + JcmdHelper.stopAndCheck(name); + } + + /** + * It should not be possible to start a recording + * with a non-existing setting file + */ + private static void testNonExistingSettingFile() throws Exception { + String name = "testNonExistingSettingFile"; + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", + "name=" + name, + "settings=nonexisting.jfc"); + JcmdAsserts.assertNotAbleToFindSettingsFile(output); + JcmdHelper.assertRecordingNotExist(name); + } + + private static void assertHasEvent(File file, String eventType, String threadName) throws Exception { + for (RecordedEvent event : RecordingFile.readAllEvents(file.toPath())) { + if (Events.isEventType(event, eventType)) { + System.out.println(event); + RecordedThread t = event.getThread(); + if (t == null) { + throw new Exception("Thread null for event " + eventType); + } + if (threadName.equals(t.getJavaName())) { + System.out.println("Found event: " + event); + return; + } + } + } + Asserts.fail("No events of type " + eventType); + } + + static class ThreadWait extends Thread { + + public ThreadWait() { + setName("ThreadWait"); + } + + @Override + public void run() { + try { + synchronized (this) { + wait(100); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdStopInvalidFile.java 2019-02-08 18:33:44.943761360 +0300 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015, 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.jcmd; + +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary Verify error when stopping with invalid file. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jcmd.TestJcmdStopInvalidFile + */ +public class TestJcmdStopInvalidFile { + + private final static String ILLEGAL_FILE_NAME = ":;/\\?"; + + public static void main(String[] args) throws Exception { + String name = "testStopWithIllegalFilename"; + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", "name=" + name); + JcmdAsserts.assertRecordingHasStarted(output); + JcmdHelper.waitUntilRunning(name); + + output = JcmdHelper.jcmd("JFR.stop", + "name=" + name, + "filename=" + ILLEGAL_FILE_NAME); + JcmdAsserts.assertFileNotFoundException(output, name); + + output = JcmdHelper.jcmd("JFR.check"); + JcmdHelper.assertRecordingIsRunning(name); + JcmdHelper.stopAndCheck(name); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/TestJcmdStopReadOnlyFile.java 2019-02-08 18:33:45.079756628 +0300 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013, 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.jcmd; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.test.lib.jfr.FileHelper; +import jdk.test.lib.process.OutputAnalyzer; + +/** + * @test + * @summary Verify error when stopping with read-only file. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jcmd.TestJcmdStopReadOnlyFile + */ +public class TestJcmdStopReadOnlyFile { + + + public static void main(String[] args) throws Exception { + String name = "TestJcmdStopReadOnlyFile"; + Path readonlyFile = FileHelper.createReadOnlyFile(Paths.get(".", name + ".jfr")); + if (!FileHelper.isReadOnlyPath(readonlyFile)) { + System.out.println("Could not create read-only file. Ignoring test."); + return; + } + + OutputAnalyzer output = JcmdHelper.jcmd("JFR.start", "name=" + name); + JcmdAsserts.assertRecordingHasStarted(output); + JcmdHelper.waitUntilRunning(name); + + output = JcmdHelper.jcmd("JFR.stop", + "name=" + name, + "filename=" + readonlyFile.toAbsolutePath()); + JcmdAsserts.assertFileNotFoundException(output, name); + JcmdHelper.assertRecordingIsRunning(name); + JcmdHelper.stopAndCheck(name); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/jcmd-testsettings.2.jfc 2019-02-08 18:33:45.215751897 +0300 @@ -0,0 +1,10 @@ + + + + + true + true + 1 ms + + + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/jcmd-testsettings.jfc 2019-02-08 18:33:45.351747166 +0300 @@ -0,0 +1,10 @@ + + + + + true + true + 1 ms + + + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/jcmd-testsettings3.jfc 2019-02-08 18:33:45.491742295 +0300 @@ -0,0 +1,8 @@ + + + + + true + + + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jcmd/legacy.jfc 2019-02-08 18:33:45.639737146 +0300 @@ -0,0 +1,7 @@ + + + + true + everyChunk + + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/JmxHelper.java 2019-02-08 18:33:45.783732137 +0300 @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.jfr.SettingDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.management.jfr.EventTypeInfo; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.management.jfr.RecordingInfo; +import jdk.management.jfr.SettingDescriptorInfo; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.Events; + +public class JmxHelper { + + public static RecordingInfo getJmxRecording(long recId) { + for (RecordingInfo r : getFlighteRecorderMXBean().getRecordings()) { + if (r.getId() == recId) { + return r; + } + } + Asserts.fail("No RecordingInfo with id " + recId); + return null; + } + + public static Recording getJavaRecording(long recId) { + for (Recording r : FlightRecorder.getFlightRecorder().getRecordings()) { + if (r.getId() == recId) { + return r; + } + } + Asserts.fail("No Recording with id " + recId); + return null; + } + + public static void verifyState(long recId, RecordingState state, List recordings) { + RecordingInfo r = verifyExists(recId, recordings); + verifyState(r, state); + } + + public static void verifyState(RecordingInfo recording, RecordingState state) { + final String actual = recording.getState().toString(); + final String expected = state.toString(); + Asserts.assertEquals(actual, expected, "Wrong state"); + } + + public static void verifyState(long recId, RecordingState state, FlightRecorderMXBean bean) throws Exception { + FlightRecorder jfr = FlightRecorder.getFlightRecorder(); + Recording recording = CommonHelper.verifyExists(recId, jfr.getRecordings()); + CommonHelper.verifyRecordingState(recording, state); + verifyState(recId, state, bean.getRecordings()); + } + + public static void verifyNotExists(long recId, List recordings) { + for (RecordingInfo r : recordings) { + if (recId == r.getId()) { + logRecordingInfos(recordings); + Asserts.fail("Recording should not exist, id=" + recId); + } + } + } + + public static RecordingInfo verifyExists(long recId, List recordings) { + for (RecordingInfo r : recordings) { + if (recId == r.getId()) { + return r; + } + } + logRecordingInfos(recordings); + Asserts.fail("Recording not found, id=" + recId); + return null; + } + + + public static void logRecordingInfos(List recordings) { + System.out.println("RecordingInfos:"); + for (RecordingInfo r : recordings) { + System.out.println(asString(r)); + } + } + + public static void logRecordings(List recordings) { + System.out.println("Recordings:"); + for (Recording r : recordings) { + System.out.println(asString(r)); + } + } + + static File dump(long streamId, FlightRecorderMXBean bean) throws IOException { + File f = File.createTempFile("stream_" + streamId + "_", ".jfr", new File(".")); + try (FileOutputStream fos = new FileOutputStream(f); BufferedOutputStream bos = new BufferedOutputStream(fos)) { + while (true) { + byte[] data = bean.readStream(streamId); + if (data == null) { + bos.flush(); + return f; + } + bos.write(data); + } + } + } + + public static List parseStream(long streamId, FlightRecorderMXBean bean) throws Exception { + File dumpFile = dump(streamId, bean); + System.out.println("data.length=" + dumpFile.length()); + List events = new ArrayList<>(); + for (RecordedEvent event : RecordingFile.readAllEvents(dumpFile.toPath())) { + System.out.println("EVENT:" + event); + events.add(event); + } + return events; + } + + public static void verifyEquals(RecordingInfo ri, Recording r) { + String destination = r.getDestination() != null ? r.getDestination().toString() : null; + long maxAge = r.getMaxAge() != null ? r.getMaxAge().getSeconds() : 0; + long duration = r.getDuration() != null ? r.getDuration().getSeconds() : 0; + + Asserts.assertEquals(destination, ri.getDestination(), "Wrong destination"); + Asserts.assertEquals(r.getDumpOnExit(), ri.getDumpOnExit(), "Wrong dumpOnExit"); + Asserts.assertEquals(duration, ri.getDuration(), "Wrong duration"); + Asserts.assertEquals(r.getId(), ri.getId(), "Wrong id"); + Asserts.assertEquals(maxAge, ri.getMaxAge(), "Wrong maxAge"); + Asserts.assertEquals(r.getMaxSize(), ri.getMaxSize(), "Wrong maxSize"); + Asserts.assertEquals(r.getName(), ri.getName(), "Wrong name"); + Asserts.assertEquals(r.getSize(), ri.getSize(), "Wrong size"); + Asserts.assertEquals(toEpochMillis(r.getStartTime()), ri.getStartTime(), "Wrong startTime"); + Asserts.assertEquals(r.getState().toString(), ri.getState(), "Wrong state"); + Asserts.assertEquals(toEpochMillis(r.getStopTime()), ri.getStopTime(), "Wrong stopTime"); + + verifyMapEquals(r.getSettings(), ri.getSettings()); + } + + public static String asString(RecordingInfo r) { + StringBuffer sb = new StringBuffer(); + sb.append(String.format("RecordingInfo:%n")); + sb.append(String.format("destination=%s%n", r.getDestination())); + sb.append(String.format("dumpOnExit=%b%n", r.getDumpOnExit())); + sb.append(String.format("duration=%d%n", r.getDuration())); + sb.append(String.format("id=%d%n", r.getId())); + sb.append(String.format("maxAge=%d%n", r.getMaxAge())); + sb.append(String.format("maxSize=%d%n", r.getMaxSize())); + sb.append(String.format("getName=%s%n", r.getName())); + sb.append(String.format("size=%d%n", r.getSize())); + sb.append(String.format("startTime=%d%n", r.getStartTime())); + sb.append(String.format("state=%s%n", r.getState())); + sb.append(String.format("stopTime=%d%n", r.getStopTime())); + return sb.toString(); + } + + public static String asString(Recording r) { + StringBuffer sb = new StringBuffer(); + sb.append(String.format("Recording:%n")); + sb.append(String.format("destination=%s%n", r.getDestination())); + sb.append(String.format("dumpOnExit=%b%n", r.getDumpOnExit())); + sb.append(String.format("duration=%d%n", r.getDuration().getSeconds())); + sb.append(String.format("id=%d%n", r.getId())); + sb.append(String.format("maxAge=%d%n", r.getMaxAge().getSeconds())); + sb.append(String.format("maxSize=%d%n", r.getMaxSize())); + sb.append(String.format("getName=%s%n", r.getName())); + sb.append(String.format("size=%d%n", r.getSize())); + sb.append(String.format("startTime=%d%n", toEpochMillis(r.getStartTime()))); + sb.append(String.format("state=%s%n", r.getState())); + sb.append(String.format("stopTime=%d%n", toEpochMillis(r.getStopTime()))); + return sb.toString(); + } + + public static void verifyMapEquals(Map a, Map b) { + try { + Asserts.assertEquals(a.size(), b.size(), "Wrong number of keys"); + for (String key : a.keySet()) { + Asserts.assertTrue(a.containsKey(key), "Missing key " + key); + Asserts.assertEquals(a.get(key), b.get(key), "Wrong values for key " + key); + //System.out.printf("equal: %s=%s%n", key, a.get(key)); + } + } catch (Exception e) { + System.out.println("Error: " + e.getMessage()); + logMap("a", a); + logMap("b", b); + throw e; + } + } + + public static void logMap(String name, Map map) { + for (String key : map.keySet()) { + System.out.printf("map %s: %s=%s%n", name, key, map.get(key)); + } + } + + private static long toEpochMillis(Instant instant) { + return instant != null ? instant.toEpochMilli() : 0; + } + + public static void verifyEventSettingsEqual(EventType javaType, EventTypeInfo jmxType) { + Map javaSettings = new HashMap<>(); + for (SettingDescriptor settingDescriptor : javaType.getSettingDescriptors()) { + javaSettings.put(settingDescriptor.getName(), settingDescriptor); + } + Asserts.assertFalse(javaSettings.isEmpty(), "No ValueDescriptor for EventType " + javaType.getName()); + + for (SettingDescriptorInfo jmxSetting : jmxType.getSettingDescriptors()) { + final String name = jmxSetting.getName(); + System.out.printf("SettingDescriptorInfo: %s#%s=%s%n", jmxType.getName(), name, jmxSetting.getDefaultValue()); + SettingDescriptor javaSetting = javaSettings.remove(name); + Asserts.assertNotNull(javaSetting, "No Setting for name " + name); + Asserts.assertEquals(jmxSetting.getDefaultValue(), Events.getSetting(javaType, name).getDefaultValue(), "Wrong default value"); + Asserts.assertEquals(jmxSetting.getDescription(), javaSetting.getDescription(), "Wrong description"); + Asserts.assertEquals(jmxSetting.getLabel(), javaSetting.getLabel(), "Wrong label"); + Asserts.assertEquals(jmxSetting.getName(), javaSetting.getName(), "Wrong name"); + Asserts.assertEquals(jmxSetting.getTypeName(), javaSetting.getTypeName(), "Wrong type name"); + Asserts.assertEquals(jmxSetting.getContentType(), javaSetting.getContentType()); + } + + // Verify that all Settings have been matched. + if (!javaSettings.isEmpty()) { + for (String name : javaSettings.keySet()) { + System.out.println("Missing setting" + name + " in EventTypeInfo for " + javaType.getName()); + } + System.out.println(); + System.out.println(javaType.getName() + " Java API"); + System.out.println("==============="); + for (SettingDescriptor v : javaType.getSettingDescriptors()) { + System.out.println(" - " + v.getName()); + } + System.out.println(); + System.out.println(jmxType.getName() + " JMX API"); + System.out.println("==============="); + for (SettingDescriptorInfo v : jmxType.getSettingDescriptors()) { + System.out.println(" - " + v.getName()); + } + + Asserts.fail("Missing setting"); + } + } + + + public static FlightRecorderMXBean getFlighteRecorderMXBean() { + return ManagementFactory.getPlatformMXBean(FlightRecorderMXBean.class); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TEST.properties 2019-02-08 18:33:45.927727127 +0300 @@ -0,0 +1,2 @@ +modules = jdk.management.jfr + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestClone.java 2019-02-08 18:33:46.059722534 +0300 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Iterator; +import java.util.List; + +import jdk.jfr.RecordingState; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.management.jfr.RecordingInfo; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventField; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestClone + */ +public class TestClone { + public static void main(String[] args) throws Exception { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + long orgId = bean.newRecording(); + bean.startRecording(orgId); + SimpleEventHelper.createEvent(1); // Should be in both org and clone + + long cloneId = bean.cloneRecording(orgId, false); + Asserts.assertNotEquals(orgId, cloneId, "clone id should not be same as org id"); + + List recordings = bean.getRecordings(); + JmxHelper.verifyState(orgId, RecordingState.RUNNING, recordings); + JmxHelper.verifyState(cloneId, RecordingState.RUNNING, recordings); + + bean.stopRecording(orgId); + recordings = bean.getRecordings(); + JmxHelper.verifyState(orgId, RecordingState.STOPPED, recordings); + JmxHelper.verifyState(cloneId, RecordingState.RUNNING, recordings); + + SimpleEventHelper.createEvent(2); // Should only be in clone + + bean.stopRecording(cloneId); + recordings = bean.getRecordings(); + JmxHelper.verifyState(orgId, RecordingState.STOPPED, recordings); + JmxHelper.verifyState(cloneId, RecordingState.STOPPED, recordings); + + Path orgPath = Paths.get(".", "org.jfr"); + Path clonePath = Paths.get(".", "clone.jfr"); + bean.copyTo(orgId, orgPath.toString()); + bean.copyTo(cloneId, clonePath.toString()); + + verifyEvents(orgPath, 1); + verifyEvents(clonePath, 1, 2); + + bean.closeRecording(orgId); + bean.closeRecording(cloneId); + } + + private static void verifyEvents(Path path, int... ids) throws Exception { + List events = RecordingFile.readAllEvents(path); + Iterator iterator = events.iterator(); + for (int i=0; i ids = new ArrayList<>(); + for (int i=0; i<5; i++) { + long cloneId = bean.cloneRecording(orgId, false); + SimpleEventHelper.createEvent(i); + bean.stopRecording(cloneId); + Path path = Paths.get(".", i + "-org.jfr"); + bean.copyTo(cloneId, path.toString()); + bean.closeRecording(cloneId); + ids.add(i); + verifyEvents(path, ids); + } + + bean.closeRecording(orgId); + } + + private static void verifyEvents(Path path, List ids) throws Exception { + List events = RecordingFile.readAllEvents(path); + Iterator iterator = events.iterator(); + for (int i=0; i cc = new HashMap<>(); + for (Configuration c : Configuration.getConfigurations()) { + cc.put(c.getName(), c); + } + Asserts.assertTrue(!cc.isEmpty(), "Mising configurations, can't verify ConfigurationInfo"); + for (ConfigurationInfo ci : JmxHelper.getFlighteRecorderMXBean().getConfigurations()) { + Configuration c = cc.remove(ci.getName()); + Asserts.assertNotNull(c, "Superfluous configuration " + ci.getName()); + Asserts.assertEquals(c.getDescription(), ci.getDescription(), "Descriptions don't match"); + Asserts.assertEquals(c.getLabel(), ci.getLabel(), "Labels don't match"); + Asserts.assertEquals(c.getProvider(), ci.getProvider(), "Providers don't match"); + Asserts.assertEquals(c.getContents(), ci.getContents(), "Contents don't match"); + Asserts.assertEquals(c.getSettings(), ci.getSettings(), "Settings don't match"); + } + Asserts.assertTrue(cc.isEmpty(), "Missing configuration in FlightRecorderMXBean, " + cc.keySet()); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestCopyTo.java 2019-02-08 18:33:46.499707228 +0300 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestCopyTo + */ +public class TestCopyTo { + public static void main(String[] args) throws Exception { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + long recId = bean.newRecording(); + bean.startRecording(recId); + SimpleEventHelper.createEvent(1); + bean.stopRecording(recId); + + Path path = Paths.get(".", "my.jfr"); + bean.copyTo(recId, path.toString()); + + List events = RecordingFile.readAllEvents(path); + Asserts.assertTrue(events.iterator().hasNext(), "No events found"); + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + } + + bean.closeRecording(recId); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestCopyToInvalidPath.java 2019-02-08 18:33:46.639702358 +0300 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.io.IOException; +import java.nio.file.Paths; + +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestCopyToInvalidPath + */ +public class TestCopyToInvalidPath { + public static void main(String[] args) throws Throwable { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + long recId = bean.newRecording(); + bean.startRecording(recId); + SimpleEventHelper.createEvent(1); + bean.stopRecording(recId); + + CommonHelper.verifyException(()->{bean.copyTo(recId, null);}, "copyTo(null)", NullPointerException.class); + CommonHelper.verifyException(()->{bean.copyTo(recId, "");}, "copyTo('')", IOException.class); + + String p = Paths.get(".", "thisdir", "doesnot", "exists").toString(); + CommonHelper.verifyException(()->{bean.copyTo(recId, p);}, "copyTo(dirNotExist)", IOException.class); + + bean.closeRecording(recId); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestCopyToReadOnlyDir.java 2019-02-08 18:33:46.783697348 +0300 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.nio.file.AccessDeniedException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.FileHelper; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestCopyToReadOnlyDir + */ +public class TestCopyToReadOnlyDir { + public static void main(String[] args) throws Throwable { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + long recId = bean.newRecording(); + bean.startRecording(recId); + SimpleEventHelper.createEvent(1); + bean.stopRecording(recId); + + Path readOnlyDir = FileHelper.createReadOnlyDir(Paths.get(".", "readOnlyDir")); + System.out.println("readOnlyDir=" + readOnlyDir.toString()); + Asserts.assertTrue(readOnlyDir.toFile().isDirectory(), "Could not create directory. Test error"); + if (!FileHelper.isReadOnlyPath(readOnlyDir)) { + System.out.println("Failed to create read-only dir. Maybe running as root? Skipping test"); + return; + } + + Path file = Paths.get(readOnlyDir.toString(), "my.jfr"); + System.out.println("file=" + file.toString()); + try { + bean.copyTo(recId, file.toString()); + Asserts.fail("Should be able to dump to read only file"); + } catch (AccessDeniedException e) { + // ok as expected + } + + bean.closeRecording(recId); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestCopyToRunning.java 2019-02-08 18:33:46.923692478 +0300 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @key jfr + * @summary Copy a recording to file while it is running. + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestCopyToRunning + */ +public class TestCopyToRunning { + public static void main(String[] args) throws Exception { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + long recId = bean.newRecording(); + bean.startRecording(recId); + SimpleEventHelper.createEvent(1); + + Path path = Paths.get(".", "my.jfr"); + bean.copyTo(recId, path.toString()); + + List events = RecordingFile.readAllEvents(path); + Asserts.assertTrue(events.iterator().hasNext(), "No events found"); + for (RecordedEvent event : events) { + System.out.println("Event:" + event); + } + + Recording recording = getRecording(recId); + Asserts.assertEquals(recording.getState(), RecordingState.RUNNING, "Recording not in state running"); + bean.stopRecording(recId); + Asserts.assertEquals(recording.getState(), RecordingState.STOPPED, "Recording not in state stopped"); + bean.closeRecording(recId); + Asserts.assertEquals(recording.getState(), RecordingState.CLOSED, "Recording not in state closed"); + } + + private static Recording getRecording(long recId) { + for (Recording r : FlightRecorder.getFlightRecorder().getRecordings()) { + if (r.getId() == recId) { + return r; + } + } + Asserts.fail("Could not find recording with id " + recId); + return null; + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestEventTypes.java 2019-02-08 18:33:47.063687608 +0300 @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.Recording; +import jdk.jfr.SettingDescriptor; +import jdk.management.jfr.EventTypeInfo; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.management.jfr.SettingDescriptorInfo; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * @summary Verifies that EventTypes from jmx and FlightRecorder are the same. + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestEventTypes + */ +public class TestEventTypes { + public static void main(String[] args) throws Exception { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + FlightRecorder jfr = FlightRecorder.getFlightRecorder(); + + Recording r = new Recording(); + r.enable(MyEvent.class); + new MyEvent(); // triggers + List infos = bean.getEventTypes(); + List types = jfr.getEventTypes(); + Asserts.assertFalse(infos.isEmpty(), "No EventTypeInfos found"); + verifyMyEventType(infos); + assertSame(infos, types); + r.close(); + } + + @Name("MyEvent.name") + @Label("MyEvent.label") + @Description("MyEvent.description") + private static class MyEvent extends Event { + @Label("MyEvent.message") + public String message; + } + + private static void verifyMyEventType(List infos) { + for (EventTypeInfo info : infos) { + if ("MyEvent.name".equals(info.getName())) { + System.out.println("EventTypeInfo for MyEvent: " + info); + Asserts.assertEquals("MyEvent.label", info.getLabel()); + Asserts.assertEquals("MyEvent.description", info.getDescription()); + for (SettingDescriptorInfo si : info.getSettingDescriptors()) { + System.out.println("si=" + si); + } + return; + } + } + Asserts.fail("Missing EventTypeInfo for MyEvent"); + } + + private static void assertSame(List infos, List types) { + List ids = new ArrayList<>(); + for (EventTypeInfo info : infos) { + long id = info.getId(); + Asserts.assertFalse(ids.contains(id), "EventTypeInfo.id not unique:" + id); + ids.add(id); + boolean isFound = false; + for (EventType type : types) { + if (type.getId() == id) { + assertSame(info, type); + isFound = true; + break; + } + } + if (!isFound) { + String msg = "No EventType for EventTypeInfo"; + System.out.println(msg + ": " + info); + Asserts.fail(msg); + } + } + Asserts.assertEquals(infos.size(), types.size(), "Number of EventTypeInfos != EventTypes"); + } + + private static void assertSame(EventTypeInfo ti, EventType t) { + try { + Asserts.assertEquals(ti.getId(), t.getId(), "Wrong id"); + Asserts.assertEquals(ti.getName(), t.getName(), "Wrong name"); + Asserts.assertEquals(ti.getLabel(), t.getLabel(), "Wrong label"); + Asserts.assertEquals(ti.getDescription(), t.getDescription(), "Wrong description"); + Asserts.assertEquals(ti.getCategoryNames(), t.getCategoryNames(), "Wrong category names"); + + for (SettingDescriptorInfo si : ti.getSettingDescriptors()) { + String settingName = si.getName(); + boolean isFound = false; + for (SettingDescriptor d : t.getSettingDescriptors()) { + if (settingName.equals(d.getName())) { + assertSame(si, d, t); + isFound = true; + break; + } + } + if (!isFound) { + Asserts.fail("No ValueDescriptor for SettingDescriptorInfo: " + si); + } + } + } catch (Exception e) { + System.out.printf("EventTypeInfo != EventType%nEventTypeInfo=%s%nEventType=%s%n", ti, t); + throw e; + } + } + + private static void assertSame(SettingDescriptorInfo si, SettingDescriptor d, EventType type) { + try { + Asserts.assertEquals(si.getName(), d.getName(), "Wrong name"); + Asserts.assertEquals(si.getLabel(), d.getLabel(), "Wrong label"); + Asserts.assertEquals(si.getTypeName(), d.getTypeName(), "Wrong typeName"); + Asserts.assertEquals(si.getDescription(), d.getDescription(), "Wrong description"); + String typeDefaultValue = Events.getSetting(type, si.getName()).getDefaultValue(); + Asserts.assertEquals(si.getDefaultValue(), typeDefaultValue, "Wrong defaultValue"); + } catch (Exception e) { + System.out.printf("SettingDescriptorInfo != SettingDescriptor=%s%nValueDescriptor=%s%n", si, d); + throw e; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestGetRecordings.java 2019-02-08 18:33:47.203682738 +0300 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.List; + +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.management.jfr.RecordingInfo; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestGetRecordings + */ +public class TestGetRecordings { + public static void main(String[] args) throws Throwable { + FlightRecorderMXBean bean =JmxHelper.getFlighteRecorderMXBean(); + + List preCreateRecordings = bean.getRecordings(); + long recId = bean.newRecording(); + JmxHelper.verifyNotExists(recId, preCreateRecordings); + bean.closeRecording(recId); + JmxHelper.verifyNotExists(recId, bean.getRecordings()); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestGetRecordingsMultiple.java 2019-02-08 18:33:47.351677590 +0300 @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.ArrayList; +import java.util.List; + +import jdk.management.jfr.RecordingInfo; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestGetRecordingsMultiple + */ +public class TestGetRecordingsMultiple { + + private static class TestRecording { + long id; + boolean isClosed; + public TestRecording(long id) { + this.id = id; + isClosed = false; + } + } + + public static void main(String[] args) throws Throwable { + List testRecordings = new ArrayList<>(); + for (int i = 0; i < 5; ++i) { + verifyExistingRecordings(testRecordings); + testRecordings.add(createRecording()); + if (i >= 1) { + startRecording(testRecordings.get(i-1)); + } + if (i >= 2) { + stopRecording(testRecordings.get(i-2)); + } + if (i >= 3) { + closeRecording(testRecordings.get(i-3)); + } + } + verifyExistingRecordings(testRecordings); + + for (TestRecording r : testRecordings) { + if (!r.isClosed) { + closeRecording(r); + } + } + verifyExistingRecordings(testRecordings); + } + + // Verify that all active recordings are found, but no closed recordings. + private static void verifyExistingRecordings(List testRecordings) { + for (TestRecording testRecording : testRecordings) { + RecordingInfo r = findRecording(testRecording); + if (r != null) { + Asserts.assertFalse(testRecording.isClosed, "Found closed recording with id " + testRecording.id); + System.out.printf("Recording %d: %s%n", r.getId(), r.getState()); + } else { + Asserts.assertTrue(testRecording.isClosed, "Missing recording with id " + testRecording.id); + System.out.printf("Recording %d: CLOSED%n", testRecording.id); + } + } + } + + private static RecordingInfo findRecording(TestRecording testRecording) { + for (RecordingInfo r : JmxHelper.getFlighteRecorderMXBean().getRecordings()) { + if (r.getId() == testRecording.id) { + return r; + } + } + return null; + } + + private static TestRecording createRecording() { + long id = JmxHelper.getFlighteRecorderMXBean().newRecording(); + System.out.println("created recording " + id); + return new TestRecording(id); + } + + private static void startRecording(TestRecording rec) { + System.out.println("starting recording " + rec.id); + JmxHelper.getFlighteRecorderMXBean().startRecording(rec.id); + } + + private static void stopRecording(TestRecording rec) { + System.out.println("stopping recording " + rec.id); + JmxHelper.getFlighteRecorderMXBean().stopRecording(rec.id); + } + + private static void closeRecording(TestRecording rec) throws Exception { + System.out.println("closing recording " + rec.id); + JmxHelper.getFlighteRecorderMXBean().closeRecording(rec.id); + rec.isClosed = true; + + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestMultipleRecordings.java 2019-02-08 18:33:47.503672303 +0300 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.List; + +import jdk.jfr.RecordingState; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.management.jfr.RecordingInfo; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestMultipleRecordings + */ +public class TestMultipleRecordings { + public static void main(String[] args) throws Throwable { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + System.out.println("bean.class=" + bean.getClass().getName()); + + // Start recA + long recIdA = createRecording(bean); + startRecording(recIdA, bean); + + // Start recB + long recIdB = createRecording(bean); + startRecording(recIdB, bean); + + // Stop and destroy recA + stopRecording(recIdA, bean); + destroyRecording(recIdA, bean); + + // Start, stop and destroy recC + long recIdC = createRecording(bean); + startRecording(recIdC, bean); + stopRecording(recIdC, bean); + destroyRecording(recIdC, bean); + + // Stop and destroy recB + stopRecording(recIdB, bean); + destroyRecording(recIdB, bean); + } + + private static long createRecording(FlightRecorderMXBean bean) throws Exception { + List preCreateRecordings = bean.getRecordings(); + long recId = bean.newRecording(); + JmxHelper.verifyNotExists(recId, preCreateRecordings); + JmxHelper.verifyState(recId, RecordingState.NEW, bean); + return recId; + } + + private static void startRecording(long recId, FlightRecorderMXBean bean) throws Exception { + JmxHelper.verifyState(recId, RecordingState.NEW, bean); + bean.startRecording(recId); + JmxHelper.verifyState(recId, RecordingState.RUNNING, bean); + } + + private static void stopRecording(long recId, FlightRecorderMXBean bean) throws Exception { + JmxHelper.verifyState(recId, RecordingState.RUNNING, bean); + bean.stopRecording(recId); + JmxHelper.verifyState(recId, RecordingState.STOPPED, bean); + } + + private static void destroyRecording(long recId, FlightRecorderMXBean bean) throws Exception { + JmxHelper.verifyState(recId, RecordingState.STOPPED, bean); + bean.closeRecording(recId); + JmxHelper.verifyNotExists(recId, bean.getRecordings()); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestNotificationListener.java 2019-02-08 18:33:47.647667294 +0300 @@ -0,0 +1,65 @@ +/* + * 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.jmx; + +import java.lang.management.ManagementFactory; +import java.util.concurrent.CountDownLatch; + +import javax.management.Notification; +import javax.management.NotificationListener; +import javax.management.ObjectName; + +import jdk.management.jfr.FlightRecorderMXBean; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestNotificationListener + */ +public class TestNotificationListener { + + private final static CountDownLatch latch = new CountDownLatch(1); + + private final static NotificationListener listener = new NotificationListener() { + public void handleNotification(Notification notification, Object handback) { + System.out.println("Got notification: " + notification); + latch.countDown(); + } + }; + + public static void main(String[] args) throws Throwable { + ObjectName objectName = new ObjectName(FlightRecorderMXBean.MXBEAN_NAME); + ManagementFactory.getPlatformMBeanServer().addNotificationListener(objectName, listener, null, null); + FlightRecorderMXBean bean = ManagementFactory.getPlatformMXBean(FlightRecorderMXBean.class); + + long recId = bean.newRecording(); + bean.startRecording(recId); + + latch.await(); + System.out.println("Completed successfully"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestPredefinedConfiguration.java 2019-02-08 18:33:47.791662285 +0300 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Configuration; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.management.jfr.RecordingInfo; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestPredefinedConfiguration + */ +public class TestPredefinedConfiguration { + public static void main(String[] args) throws Exception { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + long recId = bean.newRecording(); + List configNames = new ArrayList<>(); + for (Configuration config : Configuration.getConfigurations()) { + System.out.println("name=" + config.getName()); + configNames.add(config.getName()); + bean.setPredefinedConfiguration(recId, config.getName()); + + RecordingInfo jmxRecording = JmxHelper.getJmxRecording(recId); + JmxHelper.verifyMapEquals(jmxRecording.getSettings(), config.getSettings()); + JmxHelper.verifyMapEquals(bean.getRecordingSettings(recId), config.getSettings()); + } + Asserts.assertTrue(configNames.contains("default"), "Missing config 'default'"); + Asserts.assertTrue(configNames.contains("profile"), "Missing config 'profile'"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestPredefinedConfigurationInvalid.java 2019-02-08 18:33:47.935657276 +0300 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import jdk.jfr.Configuration; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.VoidFunction; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestPredefinedConfigurationInvalid + */ +public class TestPredefinedConfigurationInvalid { + public static void main(String[] args) throws Throwable { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + long recId = bean.newRecording(); + + // Test invalid named configs. + verifyNullPointer(()->{ bean.setPredefinedConfiguration(recId, null); }, "setNamedConfig(null)"); + setInvalidConfigName(recId, ""); + setInvalidConfigName(recId, "invalidname"); + + // Verify we can set named config after failed attempts. + Configuration config = Configuration.getConfigurations().get(0); + bean.setPredefinedConfiguration(recId, config.getName()); + JmxHelper.verifyMapEquals(bean.getRecordingSettings(recId), config.getSettings()); + } + + private static void setInvalidConfigName(long recId, String name) { + try { + JmxHelper.getFlighteRecorderMXBean().setPredefinedConfiguration(recId, name); + Asserts.fail("Missing Exception when setNamedConfig('" + name + "')"); + } catch (IllegalArgumentException e) { + // Expected exception. + String msg = e.getMessage().toLowerCase(); + System.out.println("Got expected exception: " + msg); + String expectMsg = "not find configuration"; + Asserts.assertTrue(msg.contains(expectMsg), String.format("No '%s' in '%s'", expectMsg, msg)); + } + } + + private static void verifyNullPointer(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, NullPointerException.class); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestRecordingOptions.java 2019-02-08 18:33:48.079652267 +0300 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestRecordingOptions + */ +public class TestRecordingOptions { + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static void main(String[] args) throws Exception { + Map options = new HashMap<>(); + options.put("name", "myName"); + options.put("maxAge", "2 h"); + options.put("maxSize", "1234567890"); + options.put("dumpOnExit", "false"); + options.put("disk", "false"); + options.put("duration", "1 h"); // don't want recording to stop + + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + long recId = bean.newRecording(); + Map defaults = bean.getRecordingOptions(recId); + bean.setRecordingOptions(recId, options); + + // Verify that all options have been set. We only check the option we + // have set. Unknown options are ignored. + Map outOptions = bean.getRecordingOptions(recId); + logMap("set options", options); + logMap("get options", outOptions); + for (String key : options.keySet()) { + Asserts.assertTrue(outOptions.containsKey(key), "Missing key " + key); + Asserts.assertEquals(options.get(key), outOptions.get(key), "Wrong value for key " + key); + } + + // Verify options in RecordingInfo + Asserts.assertEquals(outOptions.get("name"), "myName", "Wrong name"); + Asserts.assertEquals(outOptions.get("maxAge"), "2 h", "Wrong maxAge"); + Asserts.assertEquals(outOptions.get("maxSize"), "1234567890", "Wrong maxSize"); + Asserts.assertEquals(outOptions.get("dumpOnExit"), "false", "Wrong dumpOnExit"); + Asserts.assertEquals(outOptions.get("disk"), "false", "Wrong disk"); + Asserts.assertEquals(outOptions.get("duration"), "1 h", "Wrong duration"); + + // try empty map + bean.setRecordingOptions(recId, new HashMap<>()); + + // try map that does not have string keys + Map invalidKeys = new HashMap<>(); + invalidKeys.put(4711, "value"); + try { + bean.setRecordingOptions(recId, (Map) invalidKeys); + throw new Error("Expected IllagalStateException for non String key"); + } catch (IllegalArgumentException iae) { + // OK, as expected + } + // try map that does not have string values + Map invalidValues = new HashMap<>(); + invalidValues.put("duration", 4711); + try { + bean.setRecordingOptions(recId, (Map) invalidKeys); + throw new Error("Expected IllagalStateException for non String value"); + } catch (IllegalArgumentException iae) { + // OK, as expected + } + + // Try one incorrect value, and make sure non + // of the other values are set. + Map lastIncorrect = new LinkedHashMap<>(); + lastIncorrect.put("duration", "10 h"); + lastIncorrect.put("whatever", "4711"); + try { + bean.setRecordingOptions(recId, lastIncorrect); + throw new Error("Expected IllagalStateException for incorrect key"); + } catch (IllegalArgumentException iae) { + // ok + Asserts.assertEquals("1 h", bean.getRecordingOptions(recId).get("duration")); + } + + // verify that defaults are set back, if we use null + Map nullMap = new HashMap<>(); + nullMap.put("name", null); + nullMap.put("maxAge", null); + nullMap.put("maxSize", null); + nullMap.put("dumpOnExit", null); + nullMap.put("disk", null); + nullMap.put("duration", null); + bean.setRecordingOptions(recId, nullMap); + Asserts.assertEquals(bean.getRecordingOptions(recId), defaults); + + bean.closeRecording(recId); + } + + private static void logMap(String name, Map map) { + for (String key : map.keySet()) { + System.out.printf("%s: %s=%s%n", name, key, map.get(key)); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestRecordingSettings.java 2019-02-08 18:33:48.223647258 +0300 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.HashMap; +import java.util.Map; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestRecordingSettings + */ +public class TestRecordingSettings { + public static void main(String[] args) throws Exception { + Map settings = new HashMap<>(); + settings.put("java.exception_throw#enabled", "false"); + settings.put("java.exception_throw#threshold", "2 s"); + settings.put("java.exception_throw#thread", "true"); + settings.put("java.exception_throw#stackTrace", "false"); + settings.put("os.information#enabled", "true"); + settings.put("os.information#period", "400 ms"); + + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + long recId = bean.newRecording(); + bean.setRecordingSettings(recId, settings); + + // Verify that JMX input and output settings are equal. + JmxHelper.verifyMapEquals(settings, JmxHelper.getFlighteRecorderMXBean().getRecordingSettings(recId)); + + // Verify that settings from Java API is correct. + Recording recording = null; + for (Recording r : FlightRecorder.getFlightRecorder().getRecordings()) { + if (r.getId() == recId) { + recording = r; + break; + } + } + Asserts.assertNotNull(recording, "No Recording with id " + recId); + JmxHelper.verifyMapEquals(settings, recording.getSettings()); + + bean.startRecording(recId); + bean.stopRecording(recId); + bean.closeRecording(recId); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestRecordingSettingsInvalid.java 2019-02-08 18:33:48.367642249 +0300 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.HashMap; +import java.util.Map; + +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * @summary Verify exception when setting invalid settings. + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestRecordingSettingsInvalid + */ +public class TestRecordingSettingsInvalid { + public static void main(String[] args) throws Exception { + Map settings = new HashMap<>(); + settings.put(null, "true"); + settings.put("java.exception_throw#stackTrace", null); + settings.put("java.exception_throw#threshold", "not-a-number"); + settings.put("os.information#period", "4 x"); + + // TODO: No exception for these settings. Not sure how much validation can be done on settings. + //settings.put("java.exception_throw#enabled", "maybe"); + //settings.put("os.information#period", "-4 s"); + //settings.put("java.exception_throw#thread", ""); + //settings.put("", "true"); + //settings.put("os.information#what", "4 ms"); + //settings.put("#", "4 what"); + //settings.put("java.exception_throw#", "true"); + //settings.put("java.exception_throwenabled", "false"); + + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + for (String key : settings.keySet()) { + System.out.printf("settings: %s=%s%n", key, settings.get(key)); + Map temp = new HashMap(); + temp.put(key, settings.get(key)); + long recId = -1; + try { + recId = bean.newRecording(); + bean.setRecordingSettings(recId, temp); + bean.startRecording(recId); + bean.stopRecording(recId); + Asserts.fail("Missing exception"); + } catch (Exception e) { + System.out.println("Got expected exception: " + e.getMessage()); + } finally { + bean.closeRecording(recId); + } + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestRecordingSettingsMultiple.java 2019-02-08 18:33:48.511637241 +0300 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.HashMap; +import java.util.Map; + +import jdk.management.jfr.FlightRecorderMXBean; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestRecordingSettingsMultiple + */ +public class TestRecordingSettingsMultiple { + public static void main(String[] args) throws Exception { + Map settingsA = new HashMap<>(); + settingsA.put("java.exception_throw#enabled", "false"); + settingsA.put("java.exception_throw#threshold", "2 s"); + settingsA.put("java.exception_throw#thread", "true"); + settingsA.put("java.exception_throw#stackTrace", "false"); + settingsA.put("os.information#enabled", "true"); + settingsA.put("os.information#period", "400 ms"); + + Map settingsB = new HashMap<>(); + settingsB.put("vm/code_sweeper/config#enabled", "true"); + settingsB.put("vm/code_sweeper/config#period", "everyChunk"); + settingsA.put("java.exception_throw#enabled", "true"); + settingsA.put("java.exception_throw#threshold", "6 m"); + settingsB.put("os.information#enabled", "true"); + settingsB.put("os.information#period", "0 ms"); + + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + long recIdA = bean.newRecording(); + long recIdB = bean.newRecording(); + bean.setRecordingSettings(recIdA, settingsA); + bean.setRecordingSettings(recIdB, settingsB); + + JmxHelper.verifyMapEquals(settingsA, bean.getRecordingSettings(recIdA)); + JmxHelper.verifyMapEquals(settingsB, bean.getRecordingSettings(recIdB)); + JmxHelper.verifyMapEquals(settingsA, JmxHelper.getJavaRecording(recIdA).getSettings()); + JmxHelper.verifyMapEquals(settingsB, JmxHelper.getJavaRecording(recIdB).getSettings()); + + bean.startRecording(recIdA); + bean.startRecording(recIdB); + JmxHelper.verifyMapEquals(settingsA, bean.getRecordingSettings(recIdA)); + JmxHelper.verifyMapEquals(settingsB, bean.getRecordingSettings(recIdB)); + JmxHelper.verifyMapEquals(settingsA, JmxHelper.getJavaRecording(recIdA).getSettings()); + JmxHelper.verifyMapEquals(settingsB, JmxHelper.getJavaRecording(recIdB).getSettings()); + + bean.stopRecording(recIdA); + bean.stopRecording(recIdB); + JmxHelper.verifyMapEquals(settingsA, bean.getRecordingSettings(recIdA)); + JmxHelper.verifyMapEquals(settingsB, bean.getRecordingSettings(recIdB)); + JmxHelper.verifyMapEquals(settingsA, JmxHelper.getJavaRecording(recIdA).getSettings()); + JmxHelper.verifyMapEquals(settingsB, JmxHelper.getJavaRecording(recIdB).getSettings()); + + bean.closeRecording(recIdA); + bean.closeRecording(recIdB); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestRecordingState.java 2019-02-08 18:33:48.655632232 +0300 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.List; + +import jdk.jfr.RecordingState; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.management.jfr.RecordingInfo; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestRecordingState + */ +public class TestRecordingState { + public static void main(String[] args) throws Throwable { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + List preCreateRecordings = bean.getRecordings(); + long recId = bean.newRecording(); + JmxHelper.verifyNotExists(recId, preCreateRecordings); + JmxHelper.verifyState(recId, RecordingState.NEW, bean); + + bean.startRecording(recId); + JmxHelper.verifyState(recId, RecordingState.RUNNING, bean); + + bean.stopRecording(recId); + JmxHelper.verifyState(recId, RecordingState.STOPPED, bean); + + bean.closeRecording(recId); + JmxHelper.verifyNotExists(recId, bean.getRecordings()); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestRecordingStateInvalid.java 2019-02-08 18:33:48.799627224 +0300 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.List; + +import jdk.jfr.RecordingState; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.management.jfr.RecordingInfo; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.VoidFunction; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestRecordingStateInvalid + */ +public class TestRecordingStateInvalid { + public static void main(String[] args) throws Throwable { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + long recId = createRecording(bean); + verifyIllegalState(()->{ bean.stopRecording(recId); }, "Stop not started"); + + startRecording(recId, bean); + verifyIllegalState(()->{ bean.startRecording(recId); }, "Start already started"); + + stopRecording(recId, bean); + verifyIllegalState(()->{ bean.startRecording(recId); }, "Start already stopped"); + verifyIllegalState(()->{ bean.stopRecording(recId); }, "Stop already stopped"); + + destroyRecording(recId, bean); + verifyIllegalArg(()->{ bean.startRecording(recId); }, "Start already destroyed"); + verifyIllegalArg(()->{ bean.stopRecording(recId); }, "Stop already destroyed"); + + } + + private static long createRecording(FlightRecorderMXBean bean) throws Exception { + List preCreateRecordings = bean.getRecordings(); + long recId = bean.newRecording(); + JmxHelper.verifyNotExists(recId, preCreateRecordings); + JmxHelper.verifyState(recId, RecordingState.NEW, bean); + return recId; + } + + private static void startRecording(long recId, FlightRecorderMXBean bean) throws Exception { + JmxHelper.verifyState(recId, RecordingState.NEW, bean); + bean.startRecording(recId); + JmxHelper.verifyState(recId, RecordingState.RUNNING, bean); + } + + private static void stopRecording(long recId, FlightRecorderMXBean bean) throws Exception { + JmxHelper.verifyState(recId, RecordingState.RUNNING, bean); + bean.stopRecording(recId); + JmxHelper.verifyState(recId, RecordingState.STOPPED, bean); + } + + private static void destroyRecording(long recId, FlightRecorderMXBean bean) throws Exception { + JmxHelper.verifyState(recId, RecordingState.STOPPED, bean); + bean.closeRecording(recId); + JmxHelper.verifyNotExists(recId, bean.getRecordings()); + } + + private static void verifyIllegalState(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, IllegalStateException.class); + } + + private static void verifyIllegalArg(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, IllegalArgumentException.class); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestSetConfiguration.java 2019-02-08 18:33:48.943622215 +0300 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.HashMap; +import java.util.Map; + +import jdk.jfr.Recording; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.management.jfr.RecordingInfo; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestSetConfiguration + */ +public class TestSetConfiguration { + public static void main(String[] args) throws Exception { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + long recId = bean.newRecording(); + + final String configContents = + " \n" + + "\n" + + " \r" + + " \t false\r\n" + + " true\t\r\n" + + " 5 ms \n" + + " " + + " " + + " false\n" + + " " + + ""; + + Map expectedSetting = new HashMap<>(); + expectedSetting.put(EventNames.ClassLoad + "#enabled", "false"); + expectedSetting.put(EventNames.ClassLoad + "#stackTrace", "true"); + expectedSetting.put(EventNames.ClassLoad + "#threshold", "5 ms"); + + bean.setConfiguration(recId, configContents); + RecordingInfo jmxRecording = JmxHelper.getJmxRecording(recId); + Recording javaRecording = JmxHelper.getJavaRecording(recId); + JmxHelper.verifyEquals(jmxRecording, javaRecording); + + Map settings = jmxRecording.getSettings(); + for (String name : expectedSetting.keySet()) { + String value = settings.remove(name); + Asserts.assertNotNull(value, "No setting with name " + name); + Asserts.assertEquals(value, expectedSetting.get(name), "Wrong setting value"); + } + Asserts.assertTrue(settings.isEmpty(), "Extra settings found " + settings.keySet()); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestSetConfigurationInvalid.java 2019-02-08 18:33:49.087617206 +0300 @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.HashMap; +import java.util.Map; + +import jdk.jfr.Recording; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.management.jfr.RecordingInfo; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventNames; + +/** + * @test + * @key jfr + * @summary Verify Exception when setting invalid config. + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestSetConfigurationInvalid + */ +public class TestSetConfigurationInvalid { + public static void main(String[] args) throws Exception { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + long recId = bean.newRecording(); + + final String correctConfig = + " \n" + + "\n" + + " \r" + + " \t false\r\n" + + " true\t\r\n" + + " 5 ms \n" + + " " + + " " + + " false\n" + + " " + + ""; + + Map expectedSetting = new HashMap<>(); + expectedSetting.put(EventNames.ClassLoad + "#enabled", "false"); + expectedSetting.put(EventNames.ClassLoad + "#stackTrace", "true"); + expectedSetting.put(EventNames.ClassLoad + "#threshold", "5 ms"); + + // First set a few invalid configs. Should get Exceptions. + try { + bean.setConfiguration(recId, null); + Asserts.fail("Expected NullPointerException"); + } catch (NullPointerException e) { + // Expected exception + } + + setInvalidConfig(recId, "Dummy text"); + setInvalidConfig(recId, correctConfig.replace("/event", "event")); + setInvalidConfig(recId, correctConfig.replace("", "")); + + // Verify that we can set a correct setting after the failed attempts. + bean.setConfiguration(recId, correctConfig); + RecordingInfo jmxRecording = JmxHelper.getJmxRecording(recId); + Recording javaRecording = JmxHelper.getJavaRecording(recId); + JmxHelper.verifyEquals(jmxRecording, javaRecording); + + Map settings = jmxRecording.getSettings(); + for (String name : expectedSetting.keySet()) { + String value = settings.remove(name); + Asserts.assertNotNull(value, "No setting with name " + name); + Asserts.assertEquals(value, expectedSetting.get(name), "Wrong setting value"); + } + Asserts.assertTrue(settings.isEmpty(), "Extra settings found " + settings.keySet()); + } + + private static void setInvalidConfig(long recId, String config) { + try { + JmxHelper.getFlighteRecorderMXBean().setConfiguration(recId, config); + System.out.printf("Invalid config:%n%s", config); + Asserts.fail("No exception when setting invalid configuration"); + } catch (IllegalArgumentException e) { + // Expected exception + // Simple check if error message is about parse error. + String msg = e.getMessage().toLowerCase(); + Asserts.assertTrue(msg.contains("parse"), String.format("Missing 'parse' in msg '%s'", msg)); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestSnapshot.java 2019-02-08 18:33:49.231612198 +0300 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.io.IOException; +import java.util.List; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.management.jfr.RecordingInfo; +import jdk.test.lib.jfr.SimpleEvent; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestSnapshot + */ +public class TestSnapshot { + + public static void main(String[] args) throws Exception { + testEmpty(); + testStopped(); + } + + private static void testStopped() throws IOException { + try (Recording r = new Recording()) { + r.enable(SimpleEvent.class); + r.start(); + SimpleEvent se = new SimpleEvent(); + se.commit(); + r.stop(); + + try (Recording snapshot = FlightRecorder.getFlightRecorder().takeSnapshot()) { + r.close(); + FlightRecorderMXBean mxBean = JmxHelper.getFlighteRecorderMXBean(); + List recs = mxBean.getRecordings(); + JmxHelper.verifyEquals(recs.get(0), snapshot); + } + } + } + + private static void testEmpty() throws IOException { + try (Recording snapshot = FlightRecorder.getFlightRecorder().takeSnapshot()) { + FlightRecorderMXBean mxBean = JmxHelper.getFlighteRecorderMXBean(); + List recs = mxBean.getRecordings(); + JmxHelper.verifyEquals(recs.get(0), snapshot); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestStartRecording.java 2019-02-08 18:33:49.375607190 +0300 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.List; +import java.util.Map; + +import jdk.management.jfr.ConfigurationInfo; +import jdk.management.jfr.EventTypeInfo; +import jdk.management.jfr.FlightRecorderMXBean; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestStartRecording + */ +public class TestStartRecording { + public static void main(String[] args) throws Throwable { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + long recId = bean.newRecording(); + bean.startRecording(recId); + + // TODO: Remove debug logs + List configs = bean.getConfigurations(); + for (ConfigurationInfo config : configs) { + System.out.println("config=" + config.toString()); + } + Map settings = bean.getRecordingSettings(recId); + for (String key : settings.keySet()) { + System.out.println("setting: " + key + "=" + settings.get(key)); + } + List types = bean.getEventTypes(); + for (EventTypeInfo type : types) { + System.out.println("type=" + type.getName()); + } + ////////////////////// + + bean.stopRecording(recId); + bean.closeRecording(recId); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestStream.java 2019-02-08 18:33:49.523602042 +0300 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.io.IOException; +import java.time.Instant; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestStream + */ +public class TestStream { + public static void main(String[] args) throws Exception { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + Instant startTime = Instant.now(); + SimpleEventHelper.createEvent(0); + + long recId = bean.newRecording(); + bean.startRecording(recId); + SimpleEventHelper.createEvent(1); + + bean.stopRecording(recId); + SimpleEventHelper.createEvent(2); + + Instant endTime = Instant.now(); + // Test with ISO-8601 + Map options = new HashMap<>(); + options.put("startTime", startTime.toString()); + options.put("endTime", endTime.toString()); + options.put("blockSize", String.valueOf(50_000)); + verifyStream(bean, recId, options); + // Test with milliseconds since epoch + options.put("startTime", Long.toString(startTime.toEpochMilli())); + options.put("endTime", Long.toString(endTime.toEpochMilli())); + options.put("blockSize", String.valueOf(150_000)); + verifyStream(bean, recId, options); + + bean.closeRecording(recId); + } + + private static void verifyStream(FlightRecorderMXBean bean, long recId, Map options) throws IOException, Exception { + long streamId = bean.openStream(recId, options); + + List events = JmxHelper.parseStream(streamId, bean); + SimpleEventHelper.verifyContains(events, 1); + SimpleEventHelper.verifyNotContains(events, 0, 2); + bean.closeStream(streamId); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestStreamClosed.java 2019-02-08 18:33:49.667597034 +0300 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.io.IOException; + +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @key jfr + * @summary Call readStream() after closeStream() + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestStreamClosed + */ +public class TestStreamClosed { + public static void main(String[] args) throws Exception { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + long recId = bean.newRecording(); + bean.startRecording(recId); + SimpleEventHelper.createEvent(1); + bean.stopRecording(recId); + + long streamId = bean.openStream(recId, null); + bean.closeStream(streamId); + try { + bean.readStream(streamId); + Asserts.fail("No exception whean reading closed stream"); + } catch (IOException e) { + // Expected exception. + String msg = e.getMessage().toLowerCase(); + Asserts.assertTrue(msg.contains("stream") && msg.contains("closed"), "No 'stream closed' in " + msg); + } + bean.closeRecording(recId); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestStreamMultiple.java 2019-02-08 18:33:49.811592026 +0300 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.List; + +import jdk.jfr.consumer.RecordedEvent; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.SimpleEventHelper; + +/** + * @test + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestStreamMultiple + */ +public class TestStreamMultiple { + + public static void main(String[] args) throws Exception { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + SimpleEventHelper.createEvent(0); // No recordings + + long recIdA = bean.newRecording(); + + bean.startRecording(recIdA); + SimpleEventHelper.createEvent(1); // recA + + long recIdB = bean.newRecording(); + Asserts.assertNotEquals(recIdA, recIdB, "Recording Ids should be unique"); + bean.startRecording(recIdB); + SimpleEventHelper.createEvent(2); // recA and recB + + bean.stopRecording(recIdA); + SimpleEventHelper.createEvent(3); // recB + + bean.stopRecording(recIdB); + SimpleEventHelper.createEvent(4); // No recordings + + // Check recA + long streamIdA = bean.openStream(recIdA, null); + List events = JmxHelper.parseStream(streamIdA, bean); + SimpleEventHelper.verifyContains(events, 1, 2); + SimpleEventHelper.verifyNotContains(events, 0, 3, 4); + bean.closeStream(streamIdA); + // check recB + long streamIdB = bean.openStream(recIdB, null); + events = JmxHelper.parseStream(streamIdB, bean); + SimpleEventHelper.verifyContains(events, 2, 3); + SimpleEventHelper.verifyNotContains(events, 0, 1, 4); + bean.closeStream(streamIdB); + + bean.closeRecording(recIdA); + bean.closeRecording(recIdB); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/TestWrongId.java 2019-02-08 18:33:49.955587018 +0300 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013, 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.jmx; + +import java.util.HashMap; + +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.VoidFunction; + + +/** + * @test + * @key jfr + * @summary Call functions with invalid argument id. Verify Exception. + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.TestWrongId + */ +public class TestWrongId { + public static void main(String[] args) throws Throwable { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + final int id = 123456; + verifyIllegalArg(() -> bean.startRecording(id), "startRecording(invalidId)"); + verifyIllegalArg(() -> bean.stopRecording(id), "stopRecording(invalidId)"); + verifyIllegalArg(() -> bean.closeRecording(id), "destroyRecording(invalidId)"); + verifyIllegalArg(() -> bean.openStream(id, null), "openStream(invalidId)"); + verifyIllegalArg(() -> bean.closeStream(id), "closeStream(invalidId)"); + verifyIllegalArg(() -> bean.readStream(id), "readStream(invalidId)"); + verifyIllegalArg(() -> bean.getRecordingSettings(id), "getRecordingSettings(invalidId)"); + verifyIllegalArg(() -> bean.setConfiguration(id, "dummy"), "setConfiguration(invalidId)"); + verifyIllegalArg(() -> bean.setPredefinedConfiguration(id, "dummy"), "setNamedConfiguration(invalidId)"); + verifyIllegalArg(() -> bean.setRecordingSettings(id, new HashMap()), "setRecordingSettings(invalidId)"); + verifyIllegalArg(() -> bean.copyTo(id, "./dummy.jfr"), "dumpRecording(invalidId)"); + } + + private static void verifyIllegalArg(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, IllegalArgumentException.class); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/info/TestConfigurationInfo.java 2019-02-08 18:33:50.103581870 +0300 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2013, 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.jmx.info; + + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.jmx.JmxHelper; + +import jdk.jfr.Configuration; +import jdk.management.jfr.ConfigurationInfo; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * @summary Test for ConfigurationInfo. Compare infos from java API and jmx API. + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.info.TestConfigurationInfo + */ +public class TestConfigurationInfo { + public static void main(String[] args) throws Throwable { + List configInfos = JmxHelper.getFlighteRecorderMXBean().getConfigurations(); + Asserts.assertFalse(configInfos.isEmpty(), "No ConfigurationInfos found"); + + Map configs = new HashMap<>(); + for (Configuration config : Configuration.getConfigurations()) { + configs.put(config.getName(), config); + } + Asserts.assertFalse(configs.isEmpty(), "No Configurations found"); + + for (ConfigurationInfo configInfo : configInfos) { + final String key = configInfo.getName(); + Configuration config = configs.remove(key); + Asserts.assertNotNull(config, "No Configuration for name " + key); + + System.out.println("getDescription:" + configInfo.getDescription()); + System.out.println("getLabel:" + configInfo.getLabel()); + System.out.println("getName:" + configInfo.getName()); + System.out.println("getProvider:" + configInfo.getProvider()); + + Asserts.assertEquals(configInfo.getContents(), config.getContents(), "Wrong contents"); + Asserts.assertEquals(configInfo.getDescription(), config.getDescription(), "Wrong description"); + Asserts.assertEquals(configInfo.getLabel(), config.getLabel(), "Wrong label"); + Asserts.assertEquals(configInfo.getName(), config.getName(), "Wrong name"); + Asserts.assertEquals(configInfo.getProvider(), config.getProvider(), "Wrong provider"); + + verifySettingsEqual(config, configInfo); + } + + // Verify that all EventTypes have been matched. + if (!configs.isEmpty()) { + for (String name : configs.keySet()) { + System.out.println("Found extra Configuration with name " + name); + } + Asserts.fail("Found extra Configuration"); + } + } + + private static void verifySettingsEqual(Configuration config, ConfigurationInfo configInfo) { + Map javaSettings = config.getSettings(); + Map jmxSettings = configInfo.getSettings(); + + Asserts.assertFalse(javaSettings.isEmpty(), "No Settings found in java apa"); + Asserts.assertFalse(jmxSettings.isEmpty(), "No Settings found in jmx api"); + + for (String name : jmxSettings.keySet().toArray(new String[0])) { + System.out.printf("%s: jmx=%s, java=%s%n", name, jmxSettings.get(name), javaSettings.get(name)); + Asserts.assertNotNull(javaSettings.get(name), "No java setting for " + name); + Asserts.assertEquals(jmxSettings.get(name), javaSettings.get(name), "Wrong value for setting"); + javaSettings.remove(name); + } + + // Verify that all Settings have been matched. + if (!javaSettings.isEmpty()) { + for (String name : javaSettings.keySet()) { + System.out.println("Found extra Settings name " + name); + } + Asserts.fail("Found extra Setting in java api"); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/info/TestEventTypeInfo.java 2019-02-08 18:33:50.247576863 +0300 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2013, 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.jmx.info; + + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.jmx.JmxHelper; + +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.management.jfr.EventTypeInfo; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * @summary Test for EventTypeInfo + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.info.TestEventTypeInfo + */ +public class TestEventTypeInfo { + public static void main(String[] args) throws Throwable { + FlightRecorder jfr = FlightRecorder.getFlightRecorder(); + + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + List typeInfos = bean.getEventTypes(); + + Map types = new HashMap<>(); + for (EventType type : jfr.getEventTypes()) { + types.put(type.getName(), type); + } + + Asserts.assertFalse(typeInfos.isEmpty(), "No EventTypeInfos found"); + Asserts.assertFalse(types.isEmpty(), "No EventTypes found"); + + for (EventTypeInfo typeInfo : typeInfos) { + final String key = typeInfo.getName(); + System.out.println("EventType name = " + key); + EventType type = types.get(key); + Asserts.assertNotNull(type, "No EventType for name " + key); + types.remove(key); + + Asserts.assertEquals(typeInfo.getCategoryNames(), type.getCategoryNames(), "Wrong category"); + Asserts.assertEquals(typeInfo.getDescription(), type.getDescription(), "Wrong description"); + Asserts.assertEquals(typeInfo.getId(), type.getId(), "Wrong id"); + Asserts.assertEquals(typeInfo.getLabel(), type.getLabel(), "Wrong label"); + Asserts.assertEquals(typeInfo.getName(), type.getName(), "Wrong name"); + + JmxHelper.verifyEventSettingsEqual(type, typeInfo); + } + + // Verify that all EventTypes have been matched. + if (!types.isEmpty()) { + for (String name : types.keySet()) { + System.out.println("Found extra EventType with name " + name); + } + Asserts.fail("Found extra EventTypes"); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/info/TestRecordingInfo.java 2019-02-08 18:33:50.387571994 +0300 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013, 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.jmx.info; + + +import java.nio.file.Paths; +import java.time.Duration; + +import jdk.jfr.jmx.JmxHelper; + +import jdk.jfr.Configuration; +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.management.jfr.RecordingInfo; +import jdk.test.lib.jfr.CommonHelper; + +/** + * @test + * @key jfr + * @summary Test for RecordingInfo + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.info.TestRecordingInfo + */ +public class TestRecordingInfo { + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(Configuration.getConfiguration("profile")); + recording.setDestination(Paths.get(".", "my.jfr")); + recording.setDumpOnExit(true); + recording.setDuration(Duration.ofSeconds(60)); + recording.setMaxAge(Duration.ofHours(1)); + recording.setMaxSize(123456789); + recording.setName("myName"); + recording.enable("java.exception_throw").with("threashold", "2 s"); + recording.setToDisk(true); + + recording.start(); + CommonHelper.verifyRecordingState(recording, RecordingState.RUNNING); // Wait until running + + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + RecordingInfo info = JmxHelper.verifyExists(recording.getId(), bean.getRecordings()); + + System.out.println(JmxHelper.asString(recording)); + System.out.println(JmxHelper.asString(info)); + JmxHelper.verifyEquals(info, recording); + + recording.stop(); + recording.close(); + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/info/TestSettingDescriptorInfo.java 2019-02-08 18:33:50.535566846 +0300 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013, 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.jmx.info; + + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.jmx.JmxHelper; + +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.management.jfr.EventTypeInfo; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * @summary Test for SettingDescriptorInfo. Compare infos from java API and jmx API. + * + * @library /lib / + * @run main/othervm jdk.jfr.jmx.info.TestSettingDescriptorInfo + */ +public class TestSettingDescriptorInfo { + public static void main(String[] args) throws Throwable { + + Map javaTypes = new HashMap(); + for (EventType t : FlightRecorder.getFlightRecorder().getEventTypes()) { + javaTypes.put(t.getName(), t); + } + + List jmxTypes =JmxHelper.getFlighteRecorderMXBean().getEventTypes(); + Asserts.assertFalse(jmxTypes.isEmpty(), "No EventTypes found in jmx api"); + + for (EventTypeInfo jmxType : jmxTypes) { + final String name = jmxType.getName(); + EventType javaType = javaTypes.remove(name); + Asserts.assertNotNull(javaType, "No EventType for name " + name); + JmxHelper.verifyEventSettingsEqual(javaType, jmxType); + } + + // Verify that all EventTypes have been matched. + if (!javaTypes.isEmpty()) { + for (String name : javaTypes.keySet()) { + System.out.println("Found extra EventType that is not available using JMX " + name); + } + Asserts.fail("Found extra EventType"); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/security/TestEnoughPermission.java 2019-02-08 18:33:50.675561978 +0300 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014, 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.jmx.security; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import jdk.jfr.jmx.JmxHelper; + +import jdk.management.jfr.ConfigurationInfo; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.Asserts; + +/** + * @test + * @key jfr + * @summary Test with minimal needed permissions. All functions should work. + * + * @library /lib / + * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=enough.policy jdk.jfr.jmx.security.TestEnoughPermission + */ +public class TestEnoughPermission { + + public static void main(String[] args) throws Throwable { + try { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + System.out.println("AAAAAAAAAAAAAAAAAA"); + Asserts.assertFalse(bean.getEventTypes().isEmpty(), "No EventTypes"); + System.out.println("BBBBBBBBBBBBBBB"); + List configs = bean.getConfigurations(); + System.out.println("CCCCCCCCCCCCCCCCC"); + for (ConfigurationInfo config : configs) { + System.out.println("config.name=" + config.getName() + ": " + config.getContents()); + } + + long recId = testRecording(bean); + testStream(bean, recId); + bean.closeRecording(recId); + + //*************** verifySecurityException(() -> bean.getRecordingOptions(dummyId), "getRecordingOptions()"); + //*************** verifySecurityException(() -> bean.getRecordingSettings(dummyId), "getRecordingSettings()"); + //*********** verifySecurityException(() -> bean.setConfiguration(dummyId, "<>"), "setConfiguration()"); + //************* verifySecurityException(() -> bean.setRecordingSettings(dummyId, dummyMap), "setRecordingSettings()"); + //************* verifySecurityException(() -> bean.setRecordingOptions(dummyId, dummyMap), "setRecordingOptions()"); + } catch (Throwable t) { + t.printStackTrace(); + throw t; + } + } + + private static long testRecording(FlightRecorderMXBean bean) throws Exception { + System.out.println("A"); + long recId = bean.newRecording(); + System.out.println("B"); + bean.setPredefinedConfiguration(recId, "profile"); + System.out.println("C"); + bean.startRecording(recId); + System.out.println("D"); + Asserts.assertTrue(bean.getRecordings().stream().anyMatch(r -> { return r.getId() == recId; }), "recId not found"); + System.out.println("E"); + bean.stopRecording(recId); + + final Path path = Paths.get(".", String.format("rec%d.jfr", recId)); + bean.copyTo(recId, path.toString()); + //EventSet events = EventSet.fromFile(path); + return recId; + } + + private static void testStream(FlightRecorderMXBean bean, long recId) throws Exception { + long streamId = bean.openStream(recId, null); + byte[] buff = bean.readStream(streamId); + Asserts.assertNotNull(buff, "Stream data was empty"); + while (buff != null) { + // TODO: write to file and parse. + System.out.println("buff.length=" + buff.length); + buff = bean.readStream(streamId); + } + bean.closeStream(streamId); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/security/TestNoControlPermission.java 2019-02-08 18:33:50.819556969 +0300 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014, 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.jmx.security; + +import jdk.jfr.jmx.JmxHelper; + +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.VoidFunction; + +/** + * @test + * @key jfr + * @summary Verify we get SecurityExceptions when missing management permission "control". + * + * @library /lib / + * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=nocontrol.policy jdk.jfr.jmx.security.TestNoControlPermission + */ +public class TestNoControlPermission { + + public static void main(String[] args) throws Throwable { + try { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + int dummyId = 1; + java.util.Map dummyMap = new java.util.HashMap<>(); + + verifySecurityException(() -> bean.takeSnapshot(), "takeSnapshot()"); + verifySecurityException(() -> bean.newRecording(), "newRecording()"); + verifySecurityException(() -> bean.startRecording(dummyId), "startRecording()"); + verifySecurityException(() -> bean.stopRecording(dummyId), "stopRecording()"); + verifySecurityException(() -> bean.closeRecording(dummyId), "closeRecording()"); + verifySecurityException(() -> bean.openStream(dummyId, null), "openStream()"); + verifySecurityException(() -> bean.closeStream(dummyId), "closeStream()"); + verifySecurityException(() -> bean.setConfiguration(dummyId, "dummy"), "setConfiguration()"); + verifySecurityException(() -> bean.setPredefinedConfiguration(dummyId, "dummy"), "setPredefinedConfiguration()"); + verifySecurityException(() -> bean.setRecordingSettings(dummyId, dummyMap), "setRecordingSettings()"); + verifySecurityException(() -> bean.setRecordingOptions(dummyId, dummyMap), "setRecordingOptions()"); + verifySecurityException(() -> bean.copyTo(dummyId, "."), "dumpRecording()"); + } catch (Throwable t) { + t.printStackTrace(); + throw t; + } + } + + + private static void verifySecurityException(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, java.lang.SecurityException.class); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/security/TestNoMonitorPermission.java 2019-02-08 18:33:50.967551823 +0300 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2014, 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.jmx.security; + +import jdk.jfr.jmx.JmxHelper; + +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.jfr.CommonHelper; +import jdk.test.lib.jfr.VoidFunction; + +/** + * @test + * @key jfr + * @summary Verify we get SecurityExceptions when missing management permission "monitor". + * + * @library /lib / + * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=nomonitor.policy jdk.jfr.jmx.security.TestNoMonitorPermission + */ +public class TestNoMonitorPermission { + + public static void main(String[] args) throws Throwable { + try { + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + + int dummyId = 1; + verifySecurityException(() -> bean.getRecordings(), "getRecordings()"); + verifySecurityException(() -> bean.getConfigurations(), "getConfigurations()"); + verifySecurityException(() -> bean.getEventTypes(), "getEventTypes()"); + verifySecurityException(() -> bean.getRecordingOptions(dummyId), "getRecordingOptions()"); + verifySecurityException(() -> bean.getRecordingSettings(dummyId), "getRecordingSettings()"); + verifySecurityException(() -> bean.readStream(dummyId), "readStream()"); + } catch (Throwable t) { + t.printStackTrace(); + throw t; + } + } + + + private static void verifySecurityException(VoidFunction f, String msg) throws Throwable { + CommonHelper.verifyException(f, msg, java.lang.SecurityException.class); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/security/TestNotificationListenerPermission.java 2019-02-08 18:33:51.107546954 +0300 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2014, 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.jmx.security; + +import java.lang.management.ManagementFactory; +import java.util.concurrent.CountDownLatch; + +import javax.management.Notification; +import javax.management.NotificationListener; +import javax.management.ObjectName; + +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.test.lib.Asserts; + +import jdk.jfr.jmx.JmxHelper; + +/** + * @test + * @key jfr + * @summary Test with minimal needed permissions. All functions should work. + * + * @library /lib / + * @run main/othervm/secure=java.lang.SecurityManager/java.security.policy=listener.policy jdk.jfr.jmx.security.TestNotificationListenerPermission + */ +public class TestNotificationListenerPermission { + private static boolean gotSecurityException; + + static class TestListener implements NotificationListener { + private final CountDownLatch latch = new CountDownLatch(1); + + @Override + public void handleNotification(Notification arg0, Object arg1) { + try { + System.getProperty("user.name"); + } catch (SecurityException se) { + se.printStackTrace(); + gotSecurityException = true; + } + latch.countDown(); + } + + public void awaitNotication() throws InterruptedException { + latch.await(); + } + } + + public static void main(String[] args) throws Throwable { + try { + System.getProperty("user.name"); + Asserts.fail("Didn't get security exception. Test not configured propertly?"); + } catch (SecurityException se) { + // as expected + } + FlightRecorderMXBean bean = JmxHelper.getFlighteRecorderMXBean(); + TestListener testListener = new TestListener(); + ManagementFactory.getPlatformMBeanServer().addNotificationListener(new ObjectName(FlightRecorderMXBean.MXBEAN_NAME), testListener, null, null); + long id = bean.newRecording(); + bean.startRecording(id); + testListener.awaitNotication(); + Asserts.assertTrue(gotSecurityException, "Should not get elevated privileges in notification handler!"); + bean.stopRecording(id); + bean.closeRecording(id); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/security/enough.policy 2019-02-08 18:33:51.251541946 +0300 @@ -0,0 +1,19 @@ +// Minimum policy for JMX to activate JFR and create a recording. + +grant { + +permission java.lang.management.ManagementPermission "control"; +permission java.lang.management.ManagementPermission "monitor"; + +// in order for the test to accomplish dump/copyto on a user defined recording +permission "java.io.FilePermission" "<>", "read,write,delete"; +permission "java.util.PropertyPermission" "user.dir", "read"; +permission "javax.management.MBeanPermission" "jdk.management.jfr.FlightRecorderMXBeanImpl#-[jdk.jfr.management:type=FlightRecorder]", "addNotificationListener"; + +permission "javax.management.MBeanServerPermission" "createMBeanServer"; +permission "javax.management.MBeanPermission" "jdk.management.jfr.FlightRecorderMXBeanImpl#Recordings[jdk.jfr:type=FlightRecorder]", "getAttribute"; +permission "javax.management.MBeanPermission" "jdk.management.jfr.FlightRecorderMXBeanImpl#EventTypes[jdk.jfr:type=FlightRecorder]", "getAttribute"; +permission "javax.management.MBeanPermission" "jdk.management.jfr.FlightRecorderMXBeanImpl#Configurations[jdk.jfr:type=FlightRecorder]", "getAttribute"; +permission "javax.management.MBeanPermission" "jdk.management.jfr.FlightRecorderMXBeanImpl#newRecording[jdk.jfr:type=FlightRecorder]", "invoke"; + +}; --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/security/listener.policy 2019-02-08 18:33:51.399536800 +0300 @@ -0,0 +1,11 @@ +// Minimum policy for JMX to activate JFR and create a recording. + +grant { + +permission java.lang.management.ManagementPermission "control"; +permission java.lang.management.ManagementPermission "monitor"; +permission "javax.management.MBeanPermission" "*", "addNotificationListener"; +permission "javax.management.MBeanPermission" "*", "invoke"; +permission "javax.management.MBeanServerPermission" "createMBeanServer"; + +}; --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/security/nocontrol.policy 2019-02-08 18:33:51.543531792 +0300 @@ -0,0 +1,11 @@ +// Removed security "ManagementPermission control". Should cause SecurityExceptions. + +grant { + +// Removed permission: permission java.lang.management.ManagementPermission "control"; +permission java.lang.management.ManagementPermission "monitor"; + +permission javax.management.MBeanServerPermission "createMBeanServer"; +permission "javax.management.MBeanPermission" "jdk.management.jfr.FlightRecorderMXBeanImpl#Recordings[jdk.jfr:type=FlightRecorder]", "getAttribute"; + +}; --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jmx/security/nomonitor.policy 2019-02-08 18:33:51.687526785 +0300 @@ -0,0 +1,11 @@ +// Removed security "ManagementPermission monitor". Should cause SecurityExceptions. + +grant { + +permission java.lang.management.ManagementPermission "control"; +// Removed permission: permission java.lang.management.ManagementPermission "monitor"; + +permission javax.management.MBeanServerPermission "createMBeanServer"; +permission "javax.management.MBeanPermission" "jdk.management.jfr.FlightRecorderMXBeanImpl#Recordings[jdk.jfr:type=FlightRecorder]", "getAttribute"; + +}; --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/HelloWorldEvent1.java 2019-02-08 18:33:51.831521777 +0300 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 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.jvm; + +import jdk.jfr.Description; +import jdk.jfr.Enabled; +import jdk.jfr.Event; +import jdk.jfr.Label; + +@Label("Hello World") +@Description("My first event") +@Enabled +public class HelloWorldEvent1 extends Event { + + @Label("Message") + public String message; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/HelloWorldEvent2.java 2019-02-08 18:33:51.975516769 +0300 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 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.jvm; + +import jdk.jfr.Description; +import jdk.jfr.Enabled; +import jdk.jfr.Event; +import jdk.jfr.Label; + +@Label("Hello World") +@Description("My second event") +@Enabled +public class HelloWorldEvent2 extends Event { + + @Label("Message") + public String message; +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestBeginAndEnd.java 2019-02-08 18:33:52.123511622 +0300 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 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.jvm; + +import jdk.jfr.internal.JVM; + +/** + * @test TestBeginAndEnd + * @key jfr + * + * + * @run main/othervm jdk.jfr.jvm.TestBeginAndEnd + */ +public class TestBeginAndEnd { + + private final static long MAX_CHUNK_SIZE = 12 * 1024 * 1024; + + public static void main(String... args) { + JVM jvm = JVM.getJVM(); + jvm.createNativeJFR(); + jvm.setFileNotification(MAX_CHUNK_SIZE); + jvm.beginRecording(); + jvm.endRecording(); + jvm.destroyNativeJFR(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestClassId.java 2019-02-08 18:33:52.271506477 +0300 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 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.jvm; + +import static jdk.test.lib.Asserts.assertGreaterThan; +import static jdk.test.lib.Asserts.assertNE; + +import jdk.jfr.internal.JVM; +import jdk.jfr.internal.Type; + +/** + * @test TestClassId + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.jvm.TestClassId + */ +public class TestClassId { + + public static void main(String... args) { + assertClassIds(); + JVM jvm = JVM.getJVM(); + jvm.createNativeJFR(); + assertClassIds(); + jvm.destroyNativeJFR(); + assertClassIds(); + } + + private static void assertClassIds() { + long doubleClassId = Type.getTypeId(Double.class); + assertGreaterThan(doubleClassId, 0L, "Class id must be greater than 0"); + + long floatClassId = Type.getTypeId(Float.class); + assertNE(doubleClassId, floatClassId, "Different classes must have different class ids"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestCounterTime.java 2019-02-08 18:33:52.411501608 +0300 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 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.jvm; + +import static jdk.test.lib.Asserts.assertGreaterThan; + +import jdk.jfr.internal.JVM; + +/** + * @test TestCounterTime + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.jvm.TestCounterTime + */ +public class TestCounterTime { + + public static void main(String... args) throws InterruptedException { + // Not enabled + assertCounterTime(); + + JVM jvm = JVM.getJVM(); + jvm.createNativeJFR(); + assertCounterTime(); + // Enabled + jvm.destroyNativeJFR(); + } + + private static void assertCounterTime() throws InterruptedException { + long time1 = JVM.counterTime(); + assertGreaterThan(time1, 0L, "Counter time can't be negative."); + + Thread.sleep(1); + + long time2 = JVM.counterTime(); + assertGreaterThan(time2, time1, "Counter time must be increasing."); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestCreateNative.java 2019-02-08 18:33:52.559496462 +0300 @@ -0,0 +1,76 @@ +/* + * 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.jvm; + +import java.nio.file.Paths; + +import jdk.jfr.Configuration; +import jdk.jfr.Recording; +import jdk.jfr.internal.JVM; + +/** + * @test + * @summary Checks that the JVM can rollback on native initialization failures. + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.jvm.TestCreateNative + */ +public class TestCreateNative { + + // This is a white-box test where we fabricate a native initialization + // error by calling JMV#createNative(false), which will tear down + // all native structures after they are setup, as if something went wrong + // at the last step. + public static void main(String... args) throws Exception { + JVM jvm = JVM.getJVM(); + // Ensure that repeated failures can be handled + for (int i = 1; i < 4; i++) { + System.out.println("About to try failed initialization, attempt " + i + " out of 3"); + assertFailedInitialization(jvm); + System.out.println("As expected, initialization failed."); + } + // Ensure that Flight Recorder can be initialized properly after failures + Configuration defConfig = Configuration.getConfiguration("default"); + Recording r = new Recording(defConfig); + r.start(); + r.stop(); + r.dump(Paths.get("recording.jfr")); + r.close(); + } + + private static void assertFailedInitialization(JVM jvm) throws Exception { + try { + jvm.createFailedNativeJFR(); + throw new Exception("Expected failure when creating native JFR"); + } catch (IllegalStateException ise) { + String message = ise.getMessage(); + if (!message.equals("Unable to start Jfr")) { + throw new Exception("Expected failure on initialization of native JFR"); + } + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestDumpOnCrash.java 2019-02-08 18:33:52.707491315 +0300 @@ -0,0 +1,110 @@ +/* + * 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.jvm; + +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import sun.misc.Unsafe; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +/** + * @test + * @key jfr + * @summary Verifies that data associated with a running recording can be evacuated to an hs_err_pidXXX.jfr when the VM crashes + * + * + * @library /lib / + * + + * + * @run main/othervm jdk.jfr.jvm.TestDumpOnCrash + */ +public class TestDumpOnCrash { + + private static final CharSequence LOG_FILE_EXTENSION = ".log"; + private static final CharSequence JFR_FILE_EXTENSION = ".jfr"; + + static class Crasher { + public static void main(String[] args) { + try { + Field theUnsafeRefLocation = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafeRefLocation.setAccessible(true); + ((Unsafe)theUnsafeRefLocation.get(null)).putInt(0L, 0); + } + catch(Exception ex) { + Asserts.fail("cannot execute"); + } + } + } + + public static void main(String[] args) throws Exception { + processOutput(runProcess()); + } + + private static OutputAnalyzer runProcess() throws Exception { + return new OutputAnalyzer( + ProcessTools.createJavaProcessBuilder(true, + "-Xmx64m", + "-Xint", + "-XX:-TransmitErrorReport", + "-XX:-CreateMinidumpOnCrash", + /*"--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",*/ + "-XX:StartFlightRecording=dumponexit=true", + Crasher.class.getName()).start()); + } + + private static void processOutput(OutputAnalyzer output) throws Exception { + //output.shouldContain("CreateCoredumpOnCrash turned off, no core file dumped"); + + final Path jfrEmergencyFilePath = getHsErrJfrPath(output); + Asserts.assertTrue(Files.exists(jfrEmergencyFilePath), "No emergency jfr recording file " + jfrEmergencyFilePath + " exists"); + Asserts.assertNotEquals(Files.size(jfrEmergencyFilePath), 0L, "File length 0. Should at least be some bytes"); + System.out.printf("File size=%d%n", Files.size(jfrEmergencyFilePath)); + + List events = RecordingFile.readAllEvents(jfrEmergencyFilePath); + Asserts.assertFalse(events.isEmpty(), "No event found"); + System.out.printf("Found event %s%n", events.get(0).getEventType().getName()); + } + + private static Path getHsErrJfrPath(OutputAnalyzer output) throws Exception { + // extract to find hs-err_pid log file location + final String hs_err_pid_log_file = output.firstMatch("# *(\\S*hs_err_pid\\d+\\.log)", 1); + if (hs_err_pid_log_file == null) { + throw new RuntimeException("Did not find hs_err_pid.log file in output.\n"); + } + // the dumped out jfr file should have the same name and location but with a .jfr extension + final String hs_err_pid_jfr_file = hs_err_pid_log_file.replace(LOG_FILE_EXTENSION, JFR_FILE_EXTENSION); + return Paths.get(hs_err_pid_jfr_file); + } +} + --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestGetAllEventClasses.java 2019-02-08 18:33:52.851486308 +0300 @@ -0,0 +1,125 @@ +/* + * Copyright (c) 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.jvm; + +import jdk.jfr.Event; +import jdk.jfr.internal.JVM; + +import java.util.List; + +/** + * @test TestGetAllEventClasses + * @key jfr + * + * @library /lib / + * + * + * @build jdk.jfr.jvm.HelloWorldEvent1 + * @build jdk.jfr.jvm.HelloWorldEvent2 + * @run main/othervm jdk.jfr.jvm.TestGetAllEventClasses + */ +public class TestGetAllEventClasses { + + public static void main(String... args) throws ClassNotFoundException { + JVM jvm = JVM.getJVM(); + // before creating native + assertEmptyEventList(jvm); + jvm.createNativeJFR(); + // after creating native + assertEmptyEventList(jvm); + + // Test event class load is triggered and only once + Class clazz = initialize("jdk.jfr.jvm.HelloWorldEvent1"); + // check that the event class is registered + assertEventsIncluded(jvm, clazz); + // second active use of the same event class should not add another class + // to the list - it would already be loaded + clazz = initialize(clazz); + assertEventsIncluded(jvm, clazz); + + // second event class + Class clazz2 = initialize("jdk.jfr.jvm.HelloWorldEvent2"); + // the list of event classes should now have two classes registered + assertEventsIncluded(jvm, clazz, clazz2); + + // verify that an abstract event class is not included + Class abstractClass = initialize(MyAbstractEvent.class); // to run + assertEventsExcluded(jvm, abstractClass); + + // verify that a class that is yet to run its is not included in the list of event classes + assertEventsExcluded(jvm, MyUnInitializedEvent.class); + + // ensure old classes are not lost + assertEventsIncluded(jvm, clazz, clazz2); + + jvm.destroyNativeJFR(); + } + + private static Class initialize(String name) throws ClassNotFoundException { + // Class.forName() will force the class to run its method + return Class.forName(name).asSubclass(Event.class); + } + + private static Class initialize(Class event) throws ClassNotFoundException { + return initialize(event.getName()); + } + + private static void assertEmptyEventList(JVM jvm) { + if (!jvm.getAllEventClasses().isEmpty()) { + throw new AssertionError("should not have any event classes registered!"); + } + } + + @SafeVarargs + private static void assertEventsExcluded(JVM jvm, Class... targetEvents) { + assertEvents(jvm, false, targetEvents); + } + + @SafeVarargs + private static void assertEventsIncluded(JVM jvm, Class... targetEvents) { + assertEvents(jvm, true, targetEvents); + } + + @SafeVarargs + private static void assertEvents(JVM jvm, boolean inclusion, Class... targetEvents) { + final List> list = jvm.getAllEventClasses(); + for (Class ev : targetEvents) { + if (list.contains(ev)) { + if (inclusion) { + continue; + } + throw new AssertionError(ev.getName() + " in list but should not be!"); + } + if (!inclusion) { + continue; + } + throw new AssertionError(ev.getName() + " in not in list but should be!"); + } + } + + private static abstract class MyAbstractEvent extends Event {} + private static class MyUnInitializedEvent extends Event {} +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestGetEventWriter.java 2019-02-08 18:33:52.991481440 +0300 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 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.jvm; + +import static jdk.test.lib.Asserts.assertNotNull; + +import jdk.jfr.internal.EventWriter; +import jdk.jfr.internal.JVM; + +/** + * @test TestGetEventWriter + * @key jfr + * + * @library /lib / + * + * + * @run main/othervm jdk.jfr.jvm.TestGetEventWriter + */ +public class TestGetEventWriter { + + public static void main(String... args) { + JVM jvm = JVM.getJVM(); + jvm.createNativeJFR(); + EventWriter writer = EventWriter.getEventWriter(); + assertNotNull(writer, "EventWriter should not be null"); + jvm.destroyNativeJFR(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestGetStackTraceId.java 2019-02-08 18:33:53.135476433 +0300 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 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.jvm; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.internal.JVM; +import jdk.test.lib.Asserts; + +/** + * @test TestGetStackTraceId + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.jvm.TestGetStackTraceId + */ +public class TestGetStackTraceId { + + public static void main(String... args) { + FlightRecorder.getFlightRecorder(); + JVM jvm = JVM.getJVM(); + + long id10 = getStackIdOfDepth(10); + assertValid(id10); + + long id5 = getStackIdOfDepth(5); + assertValid(id5); + + Asserts.assertNotEquals(id5, id10, "Different stack depth must return different stack trace ids"); + + assertMaxSkip(jvm); + } + + private static void assertMaxSkip(JVM jvm) { + assertValid(jvm.getStackTraceId(Integer.MAX_VALUE)); + } + + private static void assertValid(long value) { + Asserts.assertGreaterThan(value, 0L, "Stack trace id must be greater than 0, was " + value); + } + + public static long getStackIdOfDepth(int depth) { + if (depth > 0) { + return getStackIdOfDepth(depth - 1); + } + return JVM.getJVM().getStackTraceId(0); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestJFRIntrinsic.java 2019-02-08 18:33:53.283471287 +0300 @@ -0,0 +1,154 @@ +/* + * 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. + */ + +/** + * @test + * @summary Intrinsic for JFR + * @key jfr + * + * @library /lib / + * + * + * + * @build sun.hotspot.WhiteBox + * @build ClassFileInstaller + * + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * + * @run main/othervm -Xbootclasspath/a:. -ea -Xmixed -Xbatch -XX:TieredStopAtLevel=4 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * jdk.jfr.jvm.TestJFRIntrinsic + * + * @run main/othervm -Xbootclasspath/a:. -ea -Xmixed -Xbatch -XX:TieredStopAtLevel=1 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * jdk.jfr.jvm.TestJFRIntrinsic + */ + +package jdk.jfr.jvm; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.stream.IntStream; +import jdk.jfr.internal.JVM; +import jdk.test.lib.Platform; +import sun.hotspot.WhiteBox; +import sun.hotspot.code.NMethod; + +public class TestJFRIntrinsic { + + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + public Object eventWriter; + + public static void main(String... args) throws Exception { + /* + Temporarily excluded until getClassId is reworked to accommodate epoch shift tagging + Method classid = TestJFRIntrinsic.class.getDeclaredMethod("getClassIdIntrinsic", Class.class); + ti.runIntrinsicTest(classid); + */ + TestJFRIntrinsic ti = new TestJFRIntrinsic(); + Method eventWriterMethod = TestJFRIntrinsic.class.getDeclaredMethod("getEventWriterIntrinsic", Class.class); + ti.runIntrinsicTest(eventWriterMethod); + } + + /* + public void getClassIdIntrinsic(Class cls) { + long exp = JVM.getClassId(cls); + if (exp == 0) { + throw new RuntimeException("Class id is zero"); + } + } + */ + + public void getEventWriterIntrinsic(Class cls) { + Object o = JVM.getEventWriter(); + if (o != null) { + eventWriter = o; + } + } + + void runIntrinsicTest(Method method) throws Exception { + if (getMaxCompilationLevel() < 1) { + /* no compiler */ + return; + } + /* load it */ + try { + method.invoke(this, TestJFRIntrinsic.class); + } catch(Exception e) { + throw new RuntimeException(e); + } + + int[] lvls = getAvailableCompilationLevels(); + for (int i : lvls) { + //if (!WHITE_BOX.enqueueMethodForCompilation(method, i)) { + // throw new RuntimeException("Failed to enqueue method on level: " + i); + //} + + //if (WHITE_BOX.isMethodCompiled(method)) { + // NMethod nm = NMethod.get(method, false); + // if (nm.comp_level != i) { + // throw new RuntimeException("Failed to compile on correct level: " + i); + // } + // System.out.println("Compiled " + method + " on level " + i); + //} + } + } + + /* below is copied from CompilerUtil in hotspot test lib, removed this when it's moved */ + + /** + * Returns available compilation levels + * + * @return int array with compilation levels + */ + public static int[] getAvailableCompilationLevels() { + if (!WhiteBox.getWhiteBox().getBooleanVMFlag("UseCompiler")) { + return new int[0]; + } + if (WhiteBox.getWhiteBox().getBooleanVMFlag("TieredCompilation")) { + Long flagValue = WhiteBox.getWhiteBox() + .getIntxVMFlag("TieredStopAtLevel"); + int maxLevel = flagValue.intValue(); + return IntStream.rangeClosed(1, maxLevel).toArray(); + } else { + if (Platform.isServer() && !Platform.isEmulatedClient()) { + return new int[]{4}; + } + if (Platform.isClient() || Platform.isMinimal() || Platform.isEmulatedClient()) { + return new int[]{1}; + } + } + return new int[0]; + } + + /** + * Returns maximum compilation level available + * @return an int value representing maximum compilation level available + */ + public static int getMaxCompilationLevel() { + return Arrays.stream(getAvailableCompilationLevels()) + .max() + .getAsInt(); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestJavaEvent.java 2019-02-08 18:33:53.431466141 +0300 @@ -0,0 +1,213 @@ +/* + * Copyright (c) 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.jvm; + +import java.io.File; +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Event; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordingFile; + +/** + * @test TestGetThreadId + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.jvm.TestJavaEvent + */ +public class TestJavaEvent { + + private static final int EVENTS_PER_THREAD = 50; + private static final int THREAD_COUNT = 100; + + public static class MyEvent extends Event { + float floatValue; + double doubleValue; + int intValue; + long longValue; + char charValue; + byte byteValue; + String stringValue; + Thread threadValue; + Class classValue; + + public void setFloatValue(float value) { + floatValue = value; + } + + public void setDoubleValue(double value) { + doubleValue = value; + } + + public void setIntValue(int value) { + intValue = value; + } + + public void setLongValue(long value) { + longValue = value; + } + + public void setCharValue(char value) { + charValue = value; + } + + public void setByteValue(byte value) { + byteValue = value; + } + + public void setStringValue(String value) { + stringValue = value; + } + + public void setThreadValue(Thread value) { + threadValue = value; + } + + public void setClassValue(Class value) { + classValue = value; + } + } + + public static void main(String... args) throws IOException, InterruptedException { + Recording r = new Recording(); + r.enable(MyEvent.class).withThreshold(Duration.ofNanos(0)).withoutStackTrace(); + r.start(); + List threads = new ArrayList<>(); + for (int n = 0; n < THREAD_COUNT; n++) { + Thread t = new Thread(TestJavaEvent::emitEvents); + threads.add(t); + t.start(); + } + for (Thread t : threads) { + t.join(); + } + + r.stop(); + // prettyPrint(); + File file = File.createTempFile("test", ".jfr"); + r.dump(file.toPath()); + int eventCount = 0; + for (RecordedEvent e : RecordingFile.readAllEvents(file.toPath())) { + if (e.getEventType().getName().equals(MyEvent.class.getName())) { + eventCount++; + } + System.out.println(e); + } + System.out.println("Event count was " + eventCount + ", expected " + THREAD_COUNT * EVENTS_PER_THREAD); + r.close(); + } + + private static void emitEvents() { + for (int n = 0; n < EVENTS_PER_THREAD; n++) { + MyEvent event = new MyEvent(); + event.begin(); + event.end(); + event.setFloatValue(1.12345f); + event.setDoubleValue(1.234567890); + event.setIntValue(123456); + event.setLongValue(1234567890); + event.setCharValue('c'); + event.setByteValue((byte) 12); + event.setStringValue("1234567890"); + event.setThreadValue(Thread.currentThread()); + event.setClassValue(Class.class); + event.commit(); + try { + Thread.sleep(1); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + static void prettyPrint() { + for (EventType type : FlightRecorder.getFlightRecorder().getEventTypes()) { + for (AnnotationElement a : type.getAnnotationElements()) { + printAnnotation("", a); + } + System.out.print("class " + removePackage(type.getName())); + System.out.print(" extends Event"); + + System.out.println(" {"); + List values = type.getFields(); + for (int i = 0; i < values.size(); i++) { + ValueDescriptor v = values.get(i); + for (AnnotationElement a : v.getAnnotationElements()) { + printAnnotation(" ", a); + } + System.out.println(" " + removePackage(v.getTypeName() + brackets(v.isArray())) + " " + v.getName()); + if (i != values.size() - 1) { + System.out.println(); + } + } + System.out.println("}"); + System.out.println(); + } + } + + private static String brackets(boolean isArray) { + return isArray ? "[]" : ""; + } + + private static String removePackage(String s) { + + int index = s.lastIndexOf("."); + return s.substring(index + 1); + } + + private static void printAnnotation(String indent, AnnotationElement a) { + String name = removePackage(a.getTypeName()); + if (a.getValues().isEmpty()) { + System.out.println(indent + "@" + name); + return; + } + System.out.print(indent + "@" + name + "("); + for (Object o : a.getValues()) { + printAnnotationValue(o); + } + System.out.println(")"); + } + + private static void printAnnotationValue(Object o) { + if (o instanceof String) { + System.out.print("\"" + o + "\""); + } else { + System.out.print(String.valueOf(o)); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestJfrJavaBase.java 2019-02-08 18:33:53.575461135 +0300 @@ -0,0 +1,71 @@ +/* + * 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. + */ + +/** + * @test + * @bug 8157032 + * @key jfr + * @summary verify that jfr can not be used when JVM is executed only with java.base + * + * @library /lib / + * + * @run driver jdk.jfr.jvm.TestJfrJavaBase + */ + +package jdk.jfr.jvm; + + +import jdk.test.lib.dcmd.PidJcmdExecutor; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class TestJfrJavaBase { + + private static void checkOutput(OutputAnalyzer output) { + output.shouldContain("Module jdk.jfr not found."); + output.shouldContain("Flight Recorder can not be enabled."); + } + + public static void main(String[] args) throws Exception { + OutputAnalyzer output; + if (args.length == 0) { + output = ProcessTools.executeProcess(ProcessTools.createJavaProcessBuilder(false, + "-Dtest.jdk=" + System.getProperty("test.jdk"), + "--limit-modules", "java.base", "-cp", System.getProperty("java.class.path"), + TestJfrJavaBase.class.getName(), "runtest")); + output.shouldHaveExitValue(0); + } else { + output = ProcessTools.executeTestJava("-XX:StartFlightRecording=dumponexit=true", + "--limit-modules", "java.base", "-version"); + checkOutput(output); + output.shouldHaveExitValue(1); + + // Verify that JFR.start jcmd command reports an error when jdk.jfr module is not available + output = new PidJcmdExecutor().execute("JFR.start"); + checkOutput(output); + output.shouldHaveExitValue(0); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestLargeJavaEvent512k.java 2019-02-08 18:33:53.719456127 +0300 @@ -0,0 +1,148 @@ +/* + * Copyright (c) 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.jvm; + +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.Event; +import jdk.jfr.EventFactory; +import jdk.jfr.Recording; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventTypePrototype; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.Stressor; + +/** + * @test TestLargeJavaEvent512k + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.jvm.TestLargeJavaEvent512k + */ +public class TestLargeJavaEvent512k { + static boolean error; + static void setError() { + error = true; + } + static boolean hasError() { + return error; + } + + public static void main(String... args) throws Exception { + final String name = "MyLargeJavaEvent512k"; // name of synthetically generated event + final String fieldNamePrefix = "myfield"; + final int numberOfFields = 64; // 64*8k = 512k event size + final Map eventMap = new HashMap<>(); + final int numberOfThreads = 10; // 10 threads will run the test + final int numberOfEventsPerThread = 50; // each thread will generate 50 events + + List fields = new ArrayList<>(); + for (int i = 0; i < numberOfFields; ++i) { + String fieldName = fieldNamePrefix + i; + eventMap.put(fieldName, largeString()); + fields.add(new ValueDescriptor(String.class, fieldName)); + } + + EventTypePrototype prototype = new EventTypePrototype(name,Collections.emptyList(), fields); + + EventFactory ef = EventFactory.create(prototype.getAnnotations(), prototype.getFields()); + + Recording r = new Recording(); + r.enable(prototype.getName()).withThreshold(Duration.ofNanos(0)).withoutStackTrace(); + r.start(); + + Thread.UncaughtExceptionHandler eh = (t, e) -> TestLargeJavaEvent512k.setError(); + + Stressor.execute(numberOfThreads, eh, () -> { + for (int n = 0; n < numberOfEventsPerThread; ++n) { + try { + Event event = ef.newEvent(); + setEventValues(event, ef, prototype, eventMap); + event.commit(); + Thread.sleep(1); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + }); + r.stop(); + try { + if (hasError()) { + throw new RuntimeException("One (or several) of the threads had an exception/error, test failed"); + } + verifyEvents(r, numberOfThreads, numberOfEventsPerThread, eventMap); + } finally { + r.close(); + } + } + + private static void verifyEvents(Recording r, int numberOfThreads, int iterations, Map fields) throws IOException { + List recordedEvents = Events.fromRecording(r); + Events.hasEvents(recordedEvents); + int eventCount = 0; + for (RecordedEvent re : recordedEvents) { + verify(re, fields); + eventCount++; + } + System.out.println("Number of expected events: " + numberOfThreads * iterations); + System.out.println("Number of events found: " + eventCount); + Asserts.assertEquals(numberOfThreads * iterations, eventCount, "Unexpected number of events"); + } + + // each row is 64 chars for 128 rows == 8192 chars + private static String largeString() { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < 128; ++i) { + builder.append("cygwinapacygwinapacygwinapacygwinapacygwinapacygwinapacygwinapa "); + } + return builder.toString(); + } + + private static void setEventValues(Event event, EventFactory f, EventTypePrototype prototype, Map fields) { + for (Map.Entry entry : fields.entrySet()) { + int index = prototype.getFieldIndex(entry.getKey()); + event.set(index, entry.getValue()); + } + } + + private static void verify(RecordedEvent event, Map fields) { + for (Map.Entry entry : fields.entrySet()) { + String fieldName = entry.getKey(); + Object value = event.getValue(fieldName); + Object expected = fields.get(fieldName); + Asserts.assertEQ(value, expected); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestLargeJavaEvent64k.java 2019-02-08 18:33:53.863451121 +0300 @@ -0,0 +1,148 @@ +/* + * Copyright (c) 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.jvm; + +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.jfr.Event; +import jdk.jfr.EventFactory; +import jdk.jfr.Recording; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; +import jdk.test.lib.jfr.EventTypePrototype; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.Stressor; + +/** + * @test TestLargeJavaEvent64k + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.jvm.TestLargeJavaEvent64k + */ +public class TestLargeJavaEvent64k { + static boolean error; + static void setError() { + error = true; + } + static boolean hasError() { + return error; + } + + public static void main(String... args) throws Exception { + final String name = "MyLargeJavaEvent64k"; // name of synthetically generated event + final String fieldNamePrefix = "myfield"; + final int numberOfFields = 8; // 8*8k = ~64k event size + final Map eventMap = new HashMap<>(); + final int numberOfThreads = 100; // 50 threads will run the test + final int numberOfEventsPerThread = 50; // each thread will generate 50 events + + List fields = new ArrayList<>(); + for (int i = 0; i < numberOfFields; ++i) { + String fieldName = fieldNamePrefix + i; + eventMap.put(fieldName, largeString()); + fields.add(new ValueDescriptor(String.class, fieldName)); + } + + EventTypePrototype prototype = new EventTypePrototype(name,Collections.emptyList(), fields); + + EventFactory ef = EventFactory.create(prototype.getAnnotations(), prototype.getFields()); + + Recording r = new Recording(); + r.enable(prototype.getName()).withThreshold(Duration.ofNanos(0)).withoutStackTrace(); + r.start(); + + Thread.UncaughtExceptionHandler eh = (t, e) -> TestLargeJavaEvent64k.setError(); + + Stressor.execute(numberOfThreads, eh, () -> { + for (int n = 0; n < numberOfEventsPerThread; ++n) { + try { + Event event = ef.newEvent(); + setEventValues(event, ef, prototype, eventMap); + event.commit(); + Thread.sleep(1); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + }); + r.stop(); + try { + if (hasError()) { + throw new RuntimeException("One (or several) of the threads had an exception/error, test failed"); + } + verifyEvents(r, numberOfThreads, numberOfEventsPerThread, eventMap); + } finally { + r.close(); + } + } + + private static void verifyEvents(Recording r, int numberOfThreads, int iterations, Map fields) throws IOException { + List recordedEvents = Events.fromRecording(r); + Events.hasEvents(recordedEvents); + int eventCount = 0; + for (RecordedEvent re : recordedEvents) { + verify(re, fields); + eventCount++; + } + System.out.println("Number of expected events: " + numberOfThreads * iterations); + System.out.println("Number of events found: " + eventCount); + Asserts.assertEquals(numberOfThreads * iterations, eventCount, "Unexpected number of events"); + } + + // each row is 64 chars for 128 rows == 8192 chars + private static String largeString() { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < 128; ++i) { + builder.append("cygwinapacygwinapacygwinapacygwinapacygwinapacygwinapacygwinapa "); + } + return builder.toString(); + } + + private static void setEventValues(Event event, EventFactory f, EventTypePrototype prototype, Map fields) { + for (Map.Entry entry : fields.entrySet()) { + int index = prototype.getFieldIndex(entry.getKey()); + event.set(index, entry.getValue()); + } + } + + private static void verify(RecordedEvent event, Map fields) { + for (Map.Entry entry : fields.entrySet()) { + String fieldName = entry.getKey(); + Object value = event.getValue(fieldName); + Object expected = fields.get(fieldName); + Asserts.assertEQ(value, expected); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestLogImplementation.java 2019-02-08 18:33:54.015445836 +0300 @@ -0,0 +1,144 @@ +/* + * Copyright (c) 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.jvm; + +import jdk.jfr.internal.JVM; +import jdk.jfr.internal.Logger; +import jdk.jfr.internal.LogTag; +import jdk.jfr.internal.LogLevel; + +/** + * @test TestLogImplementation + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.jvm.TestLogImplementation + */ +public class TestLogImplementation { + private static LogLevel DEFAULT_TEST_LOG_LEVEL = LogLevel.ERROR; + private static LogTag JFR_LOG_TAG = LogTag.JFR; + + private static void assertAllLevelsForTag(LogTag tag, String message) { + for (LogLevel level : LogLevel.values()) { + Logger.log(tag, level, message); + } + } + + private static void assertAllTagsForLevel(LogLevel level, String message) { + for (LogTag tag : LogTag.values()) { + Logger.log(tag, level, message); + } + } + + public static void main(String... args) { + assertEmptyMessageOK(); + assertNullMessageOK(); + assertAllCharsOK(); + assertLargeMessageOK(); + assertFormatSpecifierOK(); + assertAllLevelOK(); + assertLogLevelUnderflow(); + assertLogLevelOverflow(); + assertLogTagUnderflow(); + assertLogTagOverflow(); + } + + private static void assertAllLevelOK() { + assertAllLevelsForTag(JFR_LOG_TAG, ""); + assertAllTagsForLevel(DEFAULT_TEST_LOG_LEVEL, ""); + } + + private static void assertFormatSpecifierOK() { + assertAllTagsForLevel(DEFAULT_TEST_LOG_LEVEL, "%s"); + assertAllTagsForLevel(DEFAULT_TEST_LOG_LEVEL, "%n"); + assertAllTagsForLevel(DEFAULT_TEST_LOG_LEVEL, "%h"); + } + + private static void assertLargeMessageOK() { + StringBuilder large = new StringBuilder(); + for (int i = 0; i < 1000 * 1000; i++) { + large.append('o'); + } + Logger.log(JFR_LOG_TAG, DEFAULT_TEST_LOG_LEVEL, large.toString()); + } + + private static void assertAllCharsOK() { + char c = Character.MIN_VALUE; + StringBuilder large = new StringBuilder(); + int logSize = 0; + for(int i = Character.MIN_VALUE; i< (int)Character.MAX_VALUE; i++, c++) { + large.append(c); + logSize++; + if (logSize == 80) { + Logger.log(JFR_LOG_TAG, DEFAULT_TEST_LOG_LEVEL, large.toString()); + large = new StringBuilder(); + } + } + } + + private static void assertEmptyMessageOK() { + assertAllLevelsForTag(JFR_LOG_TAG, ""); + } + + private static void assertNullMessageOK() { + assertAllLevelsForTag(JFR_LOG_TAG, (String)null); + } + + private static void assertLogLevelUnderflow() { + try { + JVM.log(JFR_LOG_TAG.ordinal(), 0, (String)null); + } catch (IllegalArgumentException ex) { + // Expected + } + } + + private static void assertLogLevelOverflow() { + try { + JVM.log(JFR_LOG_TAG.ordinal(), LogLevel.ERROR.ordinal() + 1, (String)null); + } catch (IllegalArgumentException ex) { + // Expected + } + } + + private static void assertLogTagUnderflow() { + try { + JVM.log(JFR_LOG_TAG.ordinal() - 1, DEFAULT_TEST_LOG_LEVEL.ordinal(), (String)null); + } catch (IllegalArgumentException ex) { + // Expected + } + } + + // since the enum will grow with new entries, provoking an overflow is problematic + // is there a built-in "current max" in a Java enum + private static void assertLogTagOverflow() { + try { + JVM.log(10000, DEFAULT_TEST_LOG_LEVEL.ordinal(), (String)null); + } catch (IllegalArgumentException ex) { + // Expected + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestLogOutput.java 2019-02-08 18:33:54.159440829 +0300 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 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.jvm; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +/** + * @test TestLogOutput + * @key jfr + * @summary Sanity test jfr logging output + * + * @library /lib / + * @run main/othervm -Xlog:disable -Xlog:jfr*=trace:file=jfr_trace.txt -XX:StartFlightRecording=duration=1s,filename=recording.jfr jdk.jfr.jvm.TestLogOutput + */ +public class TestLogOutput { + public static void main(String[] args) throws Exception { + final String fileName = "jfr_trace.txt"; + final ListfindWhat = new ArrayList<>(); + findWhat.add("Starting a recording"); + findWhat.add("Flight Recorder initialized"); + boolean passed = false; + List matches = new ArrayList(findWhat); + + do { + List lines; + try { + lines = Files.readAllLines(Paths.get(fileName)); + } catch (IOException e) { + throw new Error(e); + } + for (String l : lines) { + for (String m : matches) { + if (l.toString().contains(m)) { + matches.remove(m); + break; + } + } + } + if (matches.size() < 1) { + passed = true; + break; + } + if (lines.size() > 100) { + break; /* did not find it */ + } + } while(!passed); + + if (!passed) { + throw new Error("Not found " + findWhat + " in stream"); + } + + System.out.println("PASSED"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestPid.java 2019-02-08 18:33:54.303435822 +0300 @@ -0,0 +1,76 @@ +/* + * Copyright (c) 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.jvm; + +import static jdk.test.lib.Asserts.assertEquals; +import java.lang.management.ManagementFactory; + +import jdk.jfr.internal.JVM; + +/** + * @test TestPid + * @key jfr + * + * @library /lib / + * + * @run main/othervm jdk.jfr.jvm.TestPid + */ +public class TestPid { + + private static long getProcessId() { + + // something like '@', at least in SUN / Oracle JVMs + final String jvmName = ManagementFactory.getRuntimeMXBean().getName(); + + final int index = jvmName.indexOf('@'); + + if (index < 1) { + // part before '@' empty (index = 0) / '@' not found (index = -1) + return 42; + } + + try { + return Long.parseLong(jvmName.substring(0, index)); + } catch (NumberFormatException e) { + // ignore + } + return 42; + } + + public static void main(String... args) throws InterruptedException { + + JVM jvm = JVM.getJVM(); + String pid = jvm.getPid(); + + try { + String managementPid = String.valueOf(getProcessId()); + assertEquals(pid, managementPid, "Pid doesn't match value returned by RuntimeMXBean"); + } catch (NumberFormatException nfe) { + throw new AssertionError("Pid must be numeric, but was '" + pid + "'"); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestUnloadEventClassCount.java 2019-02-08 18:33:54.447430815 +0300 @@ -0,0 +1,111 @@ +/* + * Copyright (c) 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.jvm; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; + +import jdk.jfr.Event; +import jdk.jfr.FlightRecorder; +import jdk.jfr.internal.JVM; + +/** + * @test + * @key jfr + * @summary Unit test for JVM#getUnloadedEventClassCount + * + * + * @library /lib / + * + * + * @run main/othervm -XX:+PrintGCDetails -XX:+PrintGC -verbose:class -Xmx16m jdk.jfr.jvm.TestUnloadEventClassCount + */ +public class TestUnloadEventClassCount { + + private static final String EVENT_NAME = "jdk.jfr.jvm.TestUnloadEventClassCount$ToBeUnloaded"; + + public static class ToBeUnloaded extends Event { + } + + static public class MyClassLoader extends ClassLoader { + public MyClassLoader() { + super(null); + } + + public final Class defineClass(String name, byte[] b) { + return super.defineClass(name, b, 0, b.length); + } + } + + public static MyClassLoader myClassLoader; + + public static void main(String[] args) throws Throwable { + FlightRecorder.getFlightRecorder(); + myClassLoader = createClassLoaderWithEventClass(); + System.out.println("MyClassLoader instance created"); + long initialCount = JVM.getJVM().getUnloadedEventClassCount(); + System.out.println("Initiali unloaded count is " + initialCount); + myClassLoader = null; + System.out.println("Reference to class loader cleared"); + long count = 0; + do { + System.gc(); + System.out.println("GC triggered"); + count = JVM.getJVM().getUnloadedEventClassCount(); + System.out.println("Unloaded count was " + count); + Thread.sleep(1000); // sleep to reduce log + } while (count != initialCount + 1); + } + + private static MyClassLoader createClassLoaderWithEventClass() throws Exception { + String resourceName = EVENT_NAME.replace('.', '/') + ".class"; + try (InputStream is = TestUnloadEventClassCount.class.getClassLoader().getResourceAsStream(resourceName)) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[4096]; + int byteValue = 0; + while ((byteValue = is.read(buffer, 0, buffer.length)) != -1) { + baos.write(buffer, 0, byteValue); + } + baos.flush(); + MyClassLoader myClassLoader = new MyClassLoader(); + Class eventClass = myClassLoader.defineClass(EVENT_NAME, baos.toByteArray()); + if (eventClass == null) { + throw new Exception("Could not define test class"); + } + if (eventClass.getSuperclass() != Event.class) { + throw new Exception("Superclass should be jdk.jfr.Event"); + } + if (eventClass.getSuperclass().getClassLoader() != null) { + throw new Exception("Class loader of jdk.jfr.Event should be null"); + } + if (eventClass.getClassLoader() != myClassLoader) { + throw new Exception("Incorrect class loader for event class"); + } + eventClass.newInstance(); // force + return myClassLoader; + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/jvm/TestUnsupportedVM.java 2019-02-08 18:33:54.595425670 +0300 @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2014, 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.jvm; + + +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.concurrent.Callable; + +import jdk.jfr.AnnotationElement; +import jdk.jfr.Configuration; +import jdk.jfr.Description; +import jdk.jfr.Event; +import jdk.jfr.EventFactory; +import jdk.jfr.EventSettings; +import jdk.jfr.EventType; +import jdk.jfr.FlightRecorder; +import jdk.jfr.FlightRecorderListener; +import jdk.jfr.FlightRecorderPermission; +import jdk.jfr.Label; +import jdk.jfr.Recording; +import jdk.jfr.RecordingState; +import jdk.jfr.SettingControl; +import jdk.jfr.ValueDescriptor; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedMethod; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.jfr.consumer.RecordedThread; +import jdk.jfr.consumer.RecordedThreadGroup; +import jdk.jfr.consumer.RecordingFile; +import jdk.management.jfr.ConfigurationInfo; +import jdk.management.jfr.EventTypeInfo; +import jdk.management.jfr.FlightRecorderMXBean; +import jdk.management.jfr.RecordingInfo; +import jdk.management.jfr.SettingDescriptorInfo; +import jdk.test.lib.Utils; + +/** + * @test TestUnsupportedVM + * @key jfr + * + * + * + * + * @library /lib / + * @run main/othervm -Dprepare-recording=true jdk.jfr.jvm.TestUnsupportedVM + * @run main/othervm -Djfr.unsupported.vm=true jdk.jfr.jvm.TestUnsupportedVM + */ +public class TestUnsupportedVM { + + private static Path RECORDING_FILE = Paths.get("recording.jfr"); + private static Class [] APIClasses = { + AnnotationElement.class, + Configuration.class, + ConfigurationInfo.class, + Event.class, + EventFactory.class, + EventSettings.class, + EventType.class, + EventTypeInfo.class, + FlightRecorder.class, + FlightRecorderPermission.class, + FlightRecorderListener.class, + FlightRecorderMXBean.class, + RecordedClass.class, + RecordedEvent.class, + RecordedFrame.class, + RecordedMethod.class, + RecordedObject.class, + RecordedStackTrace.class, + RecordedThread.class, + RecordedThreadGroup.class, + Recording.class, + RecordingFile.class, + RecordingInfo.class, + RecordingState.class, + SettingControl.class, + SettingDescriptorInfo.class, + ValueDescriptor.class + }; + // * @run main/othervm -Dprepare-recording=true jdk.jfr.jvm.TestUnsupportedVM + @Label("My Event") + @Description("My fine event") + static class MyEvent extends Event { + int myValue; + } + + public static void main(String... args) throws Exception { + if (Boolean.getBoolean("prepare-recording")) { + Recording r = new Recording(Configuration.getConfiguration("default")); + r.start(); + r.stop(); + r.dump(RECORDING_FILE); + r.close(); + return; + } + + System.out.println("jdk.jfr.unsupportedvm=" + System.getProperty("jdk.jfr.unsupportedvm")); + // Class FlightRecorder + if (FlightRecorder.isAvailable()) { + throw new AssertionError("JFR should not be available on an unsupported VM"); + } + + if (FlightRecorder.isInitialized()) { + throw new AssertionError("JFR should not be initialized on an unsupported VM"); + } + + assertIllegalStateException(() -> FlightRecorder.getFlightRecorder()); + assertSwallow(() -> FlightRecorder.addListener(new FlightRecorderListener() {})); + assertSwallow(() -> FlightRecorder.removeListener(new FlightRecorderListener() {})); + assertSwallow(() -> FlightRecorder.register(MyEvent.class)); + assertSwallow(() -> FlightRecorder.unregister(MyEvent.class)); + assertSwallow(() -> FlightRecorder.addPeriodicEvent(MyEvent.class, new Runnable() { public void run() {} })); + assertSwallow(() -> FlightRecorder.removePeriodicEvent(new Runnable() { public void run() {} })); + + // Class Configuration + if (!Configuration.getConfigurations().isEmpty()) { + throw new AssertionError("Configuration files should not exist on an unsupported VM"); + } + Path jfcFile = Utils.createTempFile("empty", ".jfr"); + assertIOException(() -> Configuration.getConfiguration("default")); + assertIOException(() -> Configuration.create(jfcFile)); + assertIOException(() -> Configuration.create(new FileReader(jfcFile.toFile()))); + + // Class EventType + assertInternalError(() -> EventType.getEventType(MyEvent.class)); + + // Class EventFactory + assertInternalError(() -> EventFactory.create(new ArrayList<>(), new ArrayList<>())); + + // Create a static event + MyEvent myEvent = new MyEvent(); + myEvent.begin(); + myEvent.end(); + myEvent.shouldCommit(); + myEvent.commit(); + + // Trigger class initialization failure + for (Class c : APIClasses) { + assertNoClassInitFailure(c); + } + + // jdk.jfr.consumer.* + // Only run this part of tests if we are on VM + // that can produce a recording file + if (Files.exists(RECORDING_FILE)) { + for(RecordedEvent re : RecordingFile.readAllEvents(RECORDING_FILE)) { + System.out.println(re); + } + } + } + + private static void assertNoClassInitFailure(Class clazz) { + try { + Class.forName(clazz.getName(), true, clazz.getClassLoader()); + } catch (ClassNotFoundException e) { + throw new AssertionError("Could not find public API class on unsupported VM"); + } + } + + private static void assertInternalError(Runnable r) { + try { + r.run(); + } catch (InternalError e) { + // OK, as expected + return; + } + throw new AssertionError("Expected InternalError on an unsupported JVM"); + } + + private static void assertIOException(Callable c) { + try { + c.call(); + } catch (Exception e) { + if (e.getClass() == IOException.class) { + return; + } + } + throw new AssertionError("Expected IOException on an unsupported JVM"); + } + + private static void assertIllegalStateException(Runnable r) throws Exception { + try { + r.run(); + } catch (IllegalStateException ise) { + if (!ise.getMessage().equals("Flight Recorder is not supported on this VM")) { + throw new AssertionError("Expected 'Flight Recorder is not supported on this VM'"); + } + } + } + + private static void assertSwallow(Runnable r) throws Exception { + try { + r.run(); + } catch (Exception e) { + throw new AssertionError("Unexpected exception '" + e.getMessage() + " on an unspported VM"); + } + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/startupargs/StartupHelper.java 2019-02-08 18:33:54.739420663 +0300 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2013, 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.startupargs; + +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import jdk.jfr.FlightRecorder; +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; +import jdk.test.lib.dcmd.CommandExecutor; +import jdk.test.lib.dcmd.PidJcmdExecutor; +import jdk.test.lib.process.OutputAnalyzer; + + + +public class StartupHelper { + + public static Recording getRecording(String name) { + for (Recording r : FlightRecorder.getFlightRecorder().getRecordings()) { + System.out.println("Recording=" + r.getName()); + if (name.equals(r.getName())) { + return r; + } + } + Asserts.fail("No recording with name " + name); + return null; + } + + public static List listFilesInDir(Path root) throws IOException { + List paths = new ArrayList<>(); + List searchPaths = new ArrayList<>(); + searchPaths.add(root); + + while (!searchPaths.isEmpty()) { + Path currRoot = searchPaths.remove(searchPaths.size() - 1); + DirectoryStream contents = Files.newDirectoryStream(currRoot); + for (Path path : contents) { + paths.add(path); + if (Files.isDirectory(path)) { + searchPaths.add(path); + } + } + } + + System.out.println("Files in '" + root + "':"); + for (Path path : paths) { + System.out.println(" path: " + path); + } + return paths; + } + + public static Path findRecordingFileInRepo(Path baseLocation) throws IOException { + // Check that repo contains a file ending in "jfr" or "part" + for (Path path : listFilesInDir(baseLocation)) { + if (Files.isRegularFile(path)) { + String filename = path.toString(); + if (filename.endsWith("jfr") || filename.endsWith("part")) { + return path; + } + } + } + return null; + } + + public static OutputAnalyzer jcmd(String... args) { + String argsString = Arrays.stream(args).collect(Collectors.joining(" ")); + CommandExecutor executor = new PidJcmdExecutor(); + OutputAnalyzer oa = executor.execute(argsString); + oa.shouldHaveExitValue(0); + return oa; + } + + public static OutputAnalyzer jcmdCheck(String recordingName) { + return jcmd("JFR.check", "name=" + recordingName, "verbose=true"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/startupargs/TestBadOptionValues.java 2019-02-08 18:33:54.883415657 +0300 @@ -0,0 +1,151 @@ +/* + * 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.startupargs; + +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +/** + * @test + * @key jfr + * + * + * @library /lib / + * + + * + * @run main jdk.jfr.startupargs.TestBadOptionValues + */ +public class TestBadOptionValues { + + private static final String START_FLIGHT_RECORDING = "-XX:StartFlightRecording="; + private static final String FLIGHT_RECORDER_OPTIONS = "-XX:FlightRecorderOptions="; + + private static void test(String prepend, String expectedOutput, String... options) throws Exception { + ProcessBuilder pb; + OutputAnalyzer output; + + Asserts.assertGreaterThan(options.length, 0); + + for (String option : options) { + pb = ProcessTools.createJavaProcessBuilder(prepend + option, "-version"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain(expectedOutput); + } + } + + private static void testBoolean(String prepend, String... options) throws Exception { + String[] splitOption; + + Asserts.assertGreaterThan(options.length, 0); + + for (String option : options) { + splitOption = option.split("=", 2); + test(prepend, String.format("Boolean parsing error in command argument '%s'. Could not parse: %s.", splitOption[0], splitOption[1]), option); + } + } + + private static void testJlong(String prepend, String... options) throws Exception { + String[] splitOption; + + Asserts.assertGreaterThan(options.length, 0); + + for (String option : options) { + splitOption = option.split("=", 2); + test(prepend, String.format("Integer parsing error in command argument '%s'. Could not parse: %s.", + splitOption[0], splitOption.length > 1 ? splitOption[1]: ""), option); + } + } + + public static void main(String[] args) throws Exception { + // Nanotime options + test(START_FLIGHT_RECORDING, "Integer parsing error nanotime value: syntax error", + "delay=ms", + "duration=", + "maxage=q"); + test(START_FLIGHT_RECORDING, "Integer parsing error nanotime value: syntax error, value is null", + "duration"); + test(START_FLIGHT_RECORDING, "Integer parsing error nanotime value: illegal unit", + "delay=1000mq", + "duration=2000mss", + "maxage=-1000"); + test(START_FLIGHT_RECORDING, "Integer parsing error nanotime value: unit required", + "delay=3037", + "maxage=1"); + + // Memory size options + test(START_FLIGHT_RECORDING, "Parsing error memory size value: negative values not allowed", + "maxsize=-1", + "maxsize=-10k"); + + test(FLIGHT_RECORDER_OPTIONS, "Parsing error memory size value: negative values not allowed", + "threadbuffersize=-1M", + "memorysize=-1g", + "globalbuffersize=-0", + "maxchunksize=-"); + + test(START_FLIGHT_RECORDING, "Parsing error memory size value: syntax error, value is null", + "maxsize"); + + test(FLIGHT_RECORDER_OPTIONS, "Parsing error memory size value: syntax error, value is null", + "threadbuffersize", + "memorysize", + "globalbuffersize", + "maxchunksize"); + + test(START_FLIGHT_RECORDING, "Parsing error memory size value: invalid value", + "maxsize="); + + test(FLIGHT_RECORDER_OPTIONS, "Parsing error memory size value: invalid value", + "threadbuffersize=a", + "globalbuffersize=G", + "maxchunksize=M10"); + + // Jlong options + testJlong(FLIGHT_RECORDER_OPTIONS, + "stackdepth=q", + "stackdepth=", + "numglobalbuffers=10m", + "numglobalbuffers", + "numglobalbuffers=0x15"); + + // Boolean options + testBoolean(START_FLIGHT_RECORDING, + "disk=on", + "dumponexit=truee"); + + testBoolean(FLIGHT_RECORDER_OPTIONS, + "samplethreads=falseq", + "retransform=0"); + + // Not existing options + test(START_FLIGHT_RECORDING, "Unknown argument 'dumponexitt' in diagnostic command.", + "dumponexitt=true"); + test(FLIGHT_RECORDER_OPTIONS, "Unknown argument 'notexistoption' in diagnostic command.", + "notexistoption"); + } +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/startupargs/TestDumpOnExit.java 2019-02-08 18:33:55.031410511 +0300 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2013, 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.startupargs; + +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Iterator; +import java.util.function.Supplier; + +import jdk.jfr.consumer.RecordingFile; +import jdk.test.lib.Asserts; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +/** + * @test + * @summary Start a FlightRecording with dumponexit. Verify dump exists. + * @key jfr + * + * @library /lib / + * @run main/othervm jdk.jfr.startupargs.TestDumpOnExit + */ +public class TestDumpOnExit { + + public static void main(String[] args) throws Exception { + Path dumpPath = Paths.get(".", "dumped.jfr"); + + // Test without security manager and a file name relative to current directory + testDumponExit(() -> dumpPath, + "-XX:+LogJFR", + "-XX:StartFlightRecording=filename=./dumped.jfr,dumponexit=true,settings=profile", + "jdk.jfr.startupargs.TestDumpOnExit$TestMain" + ); + // Test a memory recording without a security manager + testDumponExit(() -> findJFRFileInCurrentDirectory(), + "-XX:+LogJFR", + "-XX:StartFlightRecording=dumponexit=true,disk=false", + "jdk.jfr.startupargs.TestDumpOnExit$TestMain" + ); + + // Test with security manager and a file name relative to current directory + testDumponExit(() -> dumpPath, + "-XX:+LogJFR", + "-XX:StartFlightRecording=filename=./dumped.jfr,dumponexit=true,settings=profile", + "-Djava.security.manager", + "jdk.jfr.startupargs.TestDumpOnExit$TestMain" + ); + // Test with security manager but without a name + testDumponExit(() -> findJFRFileInCurrentDirectory(), + "-XX:+LogJFR", + "-XX:StartFlightRecording=dumponexit=true,settings=profile", + "-Djava.security.manager", + "jdk.jfr.startupargs.TestDumpOnExit$TestMain" + ); + } + + private static Path findJFRFileInCurrentDirectory() { + try { + DirectoryStream ds = Files.newDirectoryStream(Paths.get("."), "*pid-*.jfr"); + Iterator pathIterator = ds.iterator(); + if (!pathIterator.hasNext()) { + throw new RuntimeException("Could not find jfr file in current directory"); + } + return pathIterator.next(); + } catch (IOException e) { + throw new RuntimeException("Could not list jfr file in current directory"); + } + } + + private static void testDumponExit(Supplier p,String... args) throws Exception, IOException { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, args); + OutputAnalyzer output = ProcessTools.executeProcess(pb); + System.out.println(output.getOutput()); + output.shouldHaveExitValue(0); + Path dump = p.get(); + Asserts.assertTrue(Files.isRegularFile(dump), "No recording dumped " + dump); + System.out.println("Dumped recording size=" + Files.size(dump)); + Asserts.assertFalse(RecordingFile.readAllEvents(dump).isEmpty(), "No events in dump"); + } + + @SuppressWarnings("unused") + private static class TestMain { + public static void main(String[] args) throws Exception { + System.out.println("Hello from test main"); + } + } + +} --- /dev/null 2019-02-07 20:54:51.336000000 +0300 +++ new/test/jdk/jfr/startupargs/TestMemoryOptions.java 2019-02-08 18:33:55.175405505 +0300 @@ -0,0 +1,656 @@ +/* + * 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.startupargs; + +import java.util.List; +import java.util.ArrayList; +import java.lang.reflect.Field; + +import jdk.jfr.internal.Options; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import sun.misc.Unsafe; + +/** + * @test + * @key jfr + * + * @library /lib / + * + * @run main/timeout=900 jdk.jfr.startupargs.TestMemoryOptions + */ +public class TestMemoryOptions { + + public enum Unit { + b('b'), k('k'), m('m'), g('g'); + + private char unit; + + Unit(char unit) { + this.unit = unit; + } + + char getUnit() { + return unit; + } + }; + + static class Option implements Comparable