1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.jfr.api.event.dynamic;
  27 
  28 import java.io.IOException;
  29 import java.lang.annotation.ElementType;
  30 import java.lang.annotation.Retention;
  31 import java.lang.annotation.RetentionPolicy;
  32 import java.lang.annotation.Target;
  33 import java.util.ArrayList;
  34 import java.util.Collections;
  35 import java.util.Comparator;
  36 import java.util.HashMap;
  37 import java.util.List;
  38 import java.util.Map;
  39 
  40 import jdk.jfr.AnnotationElement;
  41 import jdk.jfr.Event;
  42 import jdk.jfr.EventFactory;
  43 import jdk.jfr.EventType;
  44 import jdk.jfr.MetadataDefinition;
  45 import jdk.jfr.Recording;
  46 import jdk.jfr.ValueDescriptor;
  47 import jdk.jfr.consumer.RecordedClass;
  48 import jdk.jfr.consumer.RecordedEvent;
  49 import jdk.jfr.consumer.RecordedThread;
  50 import jdk.jfr.consumer.RecordingFile;
  51 import jdk.test.lib.Asserts;
  52 import jdk.test.lib.jfr.EventTypePrototype;
  53 import jdk.test.lib.jfr.Events;
  54 
  55 
  56 /**
  57  * @test
  58  * @key jfr
  59  * 
  60  * @library /lib /
  61  * @run main/othervm jdk.jfr.api.event.dynamic.TestEventFactory
  62  */
  63 public class TestEventFactory {
  64 
  65     @MetadataDefinition
  66     @Retention(RetentionPolicy.RUNTIME)
  67     @Target({ ElementType.FIELD, ElementType.TYPE })
  68     public @interface TestAnnotation {
  69         String value();
  70     }
  71 
  72     public final static Map<String, Object> EVENT_VALUES = new HashMap<>();
  73     public final static EventTypePrototype EVENT_TYPE_SHOULD_NOT_COMMIT;
  74     public final static EventTypePrototype EVENT_TYPE_SHOULD_COMMIT;
  75 
  76     // keep alive to prevent event metadata getting GC.
  77     public static EventFactory ef1;
  78     public static EventFactory ef2;
  79 
  80     static {
  81         EVENT_VALUES.put("intField", Integer.MAX_VALUE);
  82         EVENT_VALUES.put("longField", Long.MAX_VALUE);
  83         EVENT_VALUES.put("byteField", (byte) 5);
  84         EVENT_VALUES.put("charField", (char) 'H');
  85         EVENT_VALUES.put("shortField", (short) 56);
  86         EVENT_VALUES.put("booleanField", true);
  87         EVENT_VALUES.put("floatField", 4711.0f);
  88         EVENT_VALUES.put("doubleField", 3.141);
  89         EVENT_VALUES.put("classField", String.class);
  90         EVENT_VALUES.put("stringField", "Yeah!");
  91         EVENT_VALUES.put("threadField", Thread.currentThread());
  92 
  93         EVENT_TYPE_SHOULD_NOT_COMMIT = makeEventType("com.test.ShouldNotCommit");
  94         EVENT_TYPE_SHOULD_COMMIT = makeEventType("com.test.ShouldCommit");
  95     }
  96 
  97     public static void main(String[] args) throws Throwable {
  98         Recording r = new Recording();
  99         r.enable(EVENT_TYPE_SHOULD_COMMIT.getName()).withoutStackTrace();
 100         r.enable(EVENT_TYPE_SHOULD_NOT_COMMIT.getName()).withoutStackTrace();
 101 
 102         // Commit before start, should not be included
 103         ef1 = EventFactory.create(EVENT_TYPE_SHOULD_NOT_COMMIT.getAnnotations(), EVENT_TYPE_SHOULD_NOT_COMMIT.getFields());
 104 
 105         Event event1 = ef1.newEvent();
 106 
 107         setEventValues(event1, ef1, EVENT_TYPE_SHOULD_NOT_COMMIT);
 108         event1.commit();
 109 
 110         r.start();
 111         // Commit after start, should be included
 112         ef2 = EventFactory.create(EVENT_TYPE_SHOULD_COMMIT.getAnnotations(),  EVENT_TYPE_SHOULD_COMMIT.getFields());
 113 
 114         Event event2 = ef2.newEvent();
 115         setEventValues(event2, ef2, EVENT_TYPE_SHOULD_COMMIT);
 116         event2.commit();
 117 
 118         r.stop();
 119 
 120         RecordingFile es = Events.copyTo(r);
 121         EventType e1 = findEventType(es.readEventTypes(), EVENT_TYPE_SHOULD_NOT_COMMIT.getName());
 122         assertEquals(e1, ef1.getEventType());
 123 
 124         EventType e2 = findEventType(es.readEventTypes(), EVENT_TYPE_SHOULD_COMMIT.getName());
 125         assertEquals(e2, ef2.getEventType());
 126 
 127         verifyEvent(es);
 128     }
 129 
 130     private static EventType findEventType(List<EventType> es, String name) {
 131         for (EventType t : es) {
 132             if (t.getName().equals(name)) {
 133                 return t;
 134             }
 135         }
 136         throw new AssertionError("Could not find expected event type " + name);
 137     }
 138 
 139     private static void assertEquals(EventType e1, EventType expected) {
 140         Asserts.assertEquals(e1.getName(), expected.getName());
 141         Asserts.assertEquals(e1.getDescription(), expected.getDescription());
 142         Asserts.assertEquals(e1.getLabel(), expected.getLabel());
 143         assertValueDescriptorEquals(e1.getFields(), expected.getFields());
 144         assertAnnotationEquals(e1.getAnnotationElements(), expected.getAnnotationElements());
 145     }
 146 
 147     private static void assertValueDescriptorEquals(List<ValueDescriptor> values, List<ValueDescriptor> expected) {
 148         if (values.isEmpty() && expected.isEmpty()) {
 149             return;
 150         }
 151 
 152         Map<String, ValueDescriptor> valueMap = new HashMap<>();
 153         for (ValueDescriptor v : values) {
 154             valueMap.put(v.getName(), v);
 155         }
 156         for (ValueDescriptor f : expected) {
 157             ValueDescriptor v = valueMap.remove(f.getName());
 158             if (v == null) {
 159                 throw new AssertionError("Expected value descriptor " + f.getName() + " not found");
 160             }
 161             assertEquals(v, f);
 162         }
 163         if (!valueMap.isEmpty()) {
 164             throw new AssertionError("More fields than expected");
 165         }
 166     }
 167 
 168     private static void assertEquals(ValueDescriptor v1, ValueDescriptor expected) {
 169         Asserts.assertEquals(v1.getName(), expected.getName());
 170         Asserts.assertEquals(v1.getTypeName(), expected.getTypeName());
 171         assertAnnotationEquals(v1.getAnnotationElements(), expected.getAnnotationElements());
 172     }
 173 
 174     private static void assertAnnotationEquals(List<AnnotationElement> annotations, List<AnnotationElement> expected) {
 175         annotations = new ArrayList<>(annotations); // make mutable
 176         expected = new ArrayList<>(expected); // make mutable
 177         class AnnotationTypeComparator implements Comparator<AnnotationElement> {
 178             @Override
 179             public int compare(AnnotationElement a, AnnotationElement b) {
 180                 return a.getTypeName().compareTo(b.getTypeName());
 181             }
 182         }
 183 
 184         if (annotations.isEmpty() && expected.isEmpty()) {
 185             return;
 186         }
 187 
 188         if (annotations.size() != expected.size()) {
 189             System.out.println("Was:");
 190             for(AnnotationElement ae: annotations) {
 191                 System.out.println(ae.getTypeName());
 192             }
 193             System.out.println("Expected:");
 194             for(AnnotationElement ae: expected) {
 195                 System.out.println(ae.getTypeName());
 196             }
 197             throw new AssertionError("Wrong number of annotations");
 198         }
 199         Collections.sort(expected, new AnnotationTypeComparator());
 200         Collections.sort(annotations, new AnnotationTypeComparator());
 201         for (int i = 0; i < expected.size(); i++) {
 202             assertEquals(annotations.get(i), expected.get(i));
 203         }
 204     }
 205 
 206     private static void assertEquals(AnnotationElement a1, AnnotationElement expected) {
 207         Asserts.assertEquals(a1.getTypeName(), expected.getTypeName());
 208         // Don't recurse into annotation
 209         assertValueDescriptorEquals(a1.getValueDescriptors(), expected.getValueDescriptors());
 210     }
 211 
 212     private static void verifyEvent(RecordingFile rf) throws IOException {
 213         if (!rf.hasMoreEvents()) {
 214             throw new AssertionError("Expected one dynamic event");
 215         }
 216         verifyValues(rf.readEvent());
 217         if (rf.hasMoreEvents()) {
 218             throw new AssertionError("Expected one dynamic event");
 219         }
 220     }
 221 
 222     private static void setEventValues(Event event, EventFactory f, EventTypePrototype eventTypeProto) {
 223         for (Map.Entry<String, Object> entry : EVENT_VALUES.entrySet()) {
 224             int index = eventTypeProto.getFieldIndex(entry.getKey());
 225             event.set(index, entry.getValue());
 226         }
 227     }
 228 
 229     private static void verifyValues(RecordedEvent event) {
 230         for (Map.Entry<String, Object> entry : EVENT_VALUES.entrySet()) {
 231             String fieldName = entry.getKey();
 232             Object value = event.getValue(fieldName);
 233             Object expected = EVENT_VALUES.get(fieldName);
 234             if (expected instanceof Class) {
 235                 value = ((RecordedClass) value).getName();
 236                 expected = ((Class<?>) expected).getName();
 237             }
 238             if (expected instanceof Thread) {
 239                 value = ((RecordedThread) value).getJavaName();
 240                 expected = ((Thread) expected).getName();
 241             }
 242             Asserts.assertEQ(value, expected);
 243         }
 244     }
 245 
 246     private static EventTypePrototype makeEventType(String eventName) {
 247         EventTypePrototype prototype = new EventTypePrototype(eventName);
 248         prototype.addAnnotation(new AnnotationElement(TestAnnotation.class, "type"));
 249         for (Map.Entry<String, Object> entry : EVENT_VALUES.entrySet()) {
 250             Class<?> type = makePrimitive(entry.getValue().getClass());
 251             String fieldName = entry.getKey();
 252             prototype.addField(new ValueDescriptor(type, fieldName));
 253         }
 254         // add an annotated field
 255         List<AnnotationElement> annos = new ArrayList<>();
 256         annos.add(new AnnotationElement(TestAnnotation.class, "field"));
 257         prototype.addField( new ValueDescriptor(int.class, "annotatedField", annos));
 258 
 259         return prototype;
 260     }
 261 
 262     private static Class<?> makePrimitive(Class<? extends Object> clazz) {
 263         if (clazz == Integer.class) {
 264             return int.class;
 265         }
 266         if (clazz == Long.class) {
 267             return long.class;
 268         }
 269         if (clazz == Double.class) {
 270             return double.class;
 271         }
 272         if (clazz == Float.class) {
 273             return float.class;
 274         }
 275         if (clazz == Short.class) {
 276             return short.class;
 277         }
 278         if (clazz == Character.class) {
 279             return char.class;
 280         }
 281         if (clazz == Byte.class) {
 282             return byte.class;
 283         }
 284         if (clazz == Boolean.class) {
 285             return boolean.class;
 286         }
 287         return clazz;
 288     }
 289 }