--- /dev/null 2017-11-09 09:38:01.297999907 +0100 +++ new/test/jdk/jdk/jfr/api/event/dynamic/TestEventFactory.java 2018-04-09 16:42:25.434776703 +0200 @@ -0,0 +1,288 @@ +/* + * 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 /test/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; + } +}