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 }