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 * @library /test/lib 60 * @run main/othervm jdk.jfr.api.event.dynamic.TestEventFactory 61 */ 62 public class TestEventFactory { 63 64 @MetadataDefinition 65 @Retention(RetentionPolicy.RUNTIME) 66 @Target({ ElementType.FIELD, ElementType.TYPE }) 67 public @interface TestAnnotation { 68 String value(); 69 } 70 71 public final static Map<String, Object> EVENT_VALUES = new HashMap<>(); 72 public final static EventTypePrototype EVENT_TYPE_SHOULD_NOT_COMMIT; 73 public final static EventTypePrototype EVENT_TYPE_SHOULD_COMMIT; 74 75 // keep alive to prevent event metadata getting GC. 76 public static EventFactory ef1; 77 public static EventFactory ef2; 78 79 static { 80 EVENT_VALUES.put("intField", Integer.MAX_VALUE); 81 EVENT_VALUES.put("longField", Long.MAX_VALUE); 82 EVENT_VALUES.put("byteField", (byte) 5); 83 EVENT_VALUES.put("charField", (char) 'H'); 84 EVENT_VALUES.put("shortField", (short) 56); 85 EVENT_VALUES.put("booleanField", true); 86 EVENT_VALUES.put("floatField", 4711.0f); 87 EVENT_VALUES.put("doubleField", 3.141); 88 EVENT_VALUES.put("classField", String.class); 89 EVENT_VALUES.put("stringField", "Yeah!"); 90 EVENT_VALUES.put("threadField", Thread.currentThread()); 91 92 EVENT_TYPE_SHOULD_NOT_COMMIT = makeEventType("com.test.ShouldNotCommit"); 93 EVENT_TYPE_SHOULD_COMMIT = makeEventType("com.test.ShouldCommit"); 94 } 95 96 public static void main(String[] args) throws Throwable { 97 Recording r = new Recording(); 98 r.enable(EVENT_TYPE_SHOULD_COMMIT.getName()).withoutStackTrace(); 99 r.enable(EVENT_TYPE_SHOULD_NOT_COMMIT.getName()).withoutStackTrace(); 100 101 // Commit before start, should not be included 102 ef1 = EventFactory.create(EVENT_TYPE_SHOULD_NOT_COMMIT.getAnnotations(), EVENT_TYPE_SHOULD_NOT_COMMIT.getFields()); 103 104 Event event1 = ef1.newEvent(); 105 106 setEventValues(event1, ef1, EVENT_TYPE_SHOULD_NOT_COMMIT); 107 event1.commit(); 108 109 r.start(); 110 // Commit after start, should be included 111 ef2 = EventFactory.create(EVENT_TYPE_SHOULD_COMMIT.getAnnotations(), EVENT_TYPE_SHOULD_COMMIT.getFields()); 112 113 Event event2 = ef2.newEvent(); 114 setEventValues(event2, ef2, EVENT_TYPE_SHOULD_COMMIT); 115 event2.commit(); 116 117 r.stop(); 118 119 RecordingFile es = Events.copyTo(r); 120 EventType e1 = findEventType(es.readEventTypes(), EVENT_TYPE_SHOULD_NOT_COMMIT.getName()); 121 assertEquals(e1, ef1.getEventType()); 122 123 EventType e2 = findEventType(es.readEventTypes(), EVENT_TYPE_SHOULD_COMMIT.getName()); 124 assertEquals(e2, ef2.getEventType()); 125 126 verifyEvent(es); 127 } 128 129 private static EventType findEventType(List<EventType> es, String name) { 130 for (EventType t : es) { 131 if (t.getName().equals(name)) { 132 return t; 133 } 134 } 135 throw new AssertionError("Could not find expected event type " + name); 136 } 137 138 private static void assertEquals(EventType e1, EventType expected) { 139 Asserts.assertEquals(e1.getName(), expected.getName()); 140 Asserts.assertEquals(e1.getDescription(), expected.getDescription()); 141 Asserts.assertEquals(e1.getLabel(), expected.getLabel()); 142 assertValueDescriptorEquals(e1.getFields(), expected.getFields()); 143 assertAnnotationEquals(e1.getAnnotationElements(), expected.getAnnotationElements()); 144 } 145 146 private static void assertValueDescriptorEquals(List<ValueDescriptor> values, List<ValueDescriptor> expected) { 147 if (values.isEmpty() && expected.isEmpty()) { 148 return; 149 } 150 151 Map<String, ValueDescriptor> valueMap = new HashMap<>(); 152 for (ValueDescriptor v : values) { 153 valueMap.put(v.getName(), v); 154 } 155 for (ValueDescriptor f : expected) { 156 ValueDescriptor v = valueMap.remove(f.getName()); 157 if (v == null) { 158 throw new AssertionError("Expected value descriptor " + f.getName() + " not found"); 159 } 160 assertEquals(v, f); 161 } 162 if (!valueMap.isEmpty()) { 163 throw new AssertionError("More fields than expected"); 164 } 165 } 166 167 private static void assertEquals(ValueDescriptor v1, ValueDescriptor expected) { 168 Asserts.assertEquals(v1.getName(), expected.getName()); 169 Asserts.assertEquals(v1.getTypeName(), expected.getTypeName()); 170 assertAnnotationEquals(v1.getAnnotationElements(), expected.getAnnotationElements()); 171 } 172 173 private static void assertAnnotationEquals(List<AnnotationElement> annotations, List<AnnotationElement> expected) { 174 annotations = new ArrayList<>(annotations); // make mutable 175 expected = new ArrayList<>(expected); // make mutable 176 class AnnotationTypeComparator implements Comparator<AnnotationElement> { 177 @Override 178 public int compare(AnnotationElement a, AnnotationElement b) { 179 return a.getTypeName().compareTo(b.getTypeName()); 180 } 181 } 182 183 if (annotations.isEmpty() && expected.isEmpty()) { 184 return; 185 } 186 187 if (annotations.size() != expected.size()) { 188 System.out.println("Was:"); 189 for(AnnotationElement ae: annotations) { 190 System.out.println(ae.getTypeName()); 191 } 192 System.out.println("Expected:"); 193 for(AnnotationElement ae: expected) { 194 System.out.println(ae.getTypeName()); 195 } 196 throw new AssertionError("Wrong number of annotations"); 197 } 198 Collections.sort(expected, new AnnotationTypeComparator()); 199 Collections.sort(annotations, new AnnotationTypeComparator()); 200 for (int i = 0; i < expected.size(); i++) { 201 assertEquals(annotations.get(i), expected.get(i)); 202 } 203 } 204 205 private static void assertEquals(AnnotationElement a1, AnnotationElement expected) { 206 Asserts.assertEquals(a1.getTypeName(), expected.getTypeName()); 207 // Don't recurse into annotation 208 assertValueDescriptorEquals(a1.getValueDescriptors(), expected.getValueDescriptors()); 209 } 210 211 private static void verifyEvent(RecordingFile rf) throws IOException { 212 if (!rf.hasMoreEvents()) { 213 throw new AssertionError("Expected one dynamic event"); 214 } 215 verifyValues(rf.readEvent()); 216 if (rf.hasMoreEvents()) { 217 throw new AssertionError("Expected one dynamic event"); 218 } 219 } 220 221 private static void setEventValues(Event event, EventFactory f, EventTypePrototype eventTypeProto) { 222 for (Map.Entry<String, Object> entry : EVENT_VALUES.entrySet()) { 223 int index = eventTypeProto.getFieldIndex(entry.getKey()); 224 event.set(index, entry.getValue()); 225 } 226 } 227 228 private static void verifyValues(RecordedEvent event) { 229 for (Map.Entry<String, Object> entry : EVENT_VALUES.entrySet()) { 230 String fieldName = entry.getKey(); 231 Object value = event.getValue(fieldName); 232 Object expected = EVENT_VALUES.get(fieldName); 233 if (expected instanceof Class) { 234 value = ((RecordedClass) value).getName(); 235 expected = ((Class<?>) expected).getName(); 236 } 237 if (expected instanceof Thread) { 238 value = ((RecordedThread) value).getJavaName(); 239 expected = ((Thread) expected).getName(); 240 } 241 Asserts.assertEQ(value, expected); 242 } 243 } 244 245 private static EventTypePrototype makeEventType(String eventName) { 246 EventTypePrototype prototype = new EventTypePrototype(eventName); 247 prototype.addAnnotation(new AnnotationElement(TestAnnotation.class, "type")); 248 for (Map.Entry<String, Object> entry : EVENT_VALUES.entrySet()) { 249 Class<?> type = makePrimitive(entry.getValue().getClass()); 250 String fieldName = entry.getKey(); 251 prototype.addField(new ValueDescriptor(type, fieldName)); 252 } 253 // add an annotated field 254 List<AnnotationElement> annos = new ArrayList<>(); 255 annos.add(new AnnotationElement(TestAnnotation.class, "field")); 256 prototype.addField( new ValueDescriptor(int.class, "annotatedField", annos)); 257 258 return prototype; 259 } 260 261 private static Class<?> makePrimitive(Class<? extends Object> clazz) { 262 if (clazz == Integer.class) { 263 return int.class; 264 } 265 if (clazz == Long.class) { 266 return long.class; 267 } 268 if (clazz == Double.class) { 269 return double.class; 270 } 271 if (clazz == Float.class) { 272 return float.class; 273 } 274 if (clazz == Short.class) { 275 return short.class; 276 } 277 if (clazz == Character.class) { 278 return char.class; 279 } 280 if (clazz == Byte.class) { 281 return byte.class; 282 } 283 if (clazz == Boolean.class) { 284 return boolean.class; 285 } 286 return clazz; 287 } 288 }