1 /* 2 * Copyright (c) 2016, 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.internal; 27 28 import java.io.IOException; 29 import java.lang.annotation.Annotation; 30 import java.lang.annotation.Repeatable; 31 import java.lang.reflect.Field; 32 import java.lang.reflect.InvocationTargetException; 33 import java.lang.reflect.Method; 34 import java.lang.reflect.Modifier; 35 import java.util.ArrayDeque; 36 import java.util.ArrayList; 37 import java.util.Collection; 38 import java.util.Collections; 39 import java.util.HashMap; 40 import java.util.HashSet; 41 import java.util.LinkedHashMap; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Queue; 45 import java.util.Set; 46 import java.util.function.Consumer; 47 import java.util.function.Predicate; 48 import java.util.stream.Stream; 49 50 import jdk.jfr.AnnotationElement; 51 import jdk.jfr.Description; 52 import jdk.jfr.Event; 53 import jdk.jfr.Label; 54 import jdk.jfr.MetadataDefinition; 55 import jdk.jfr.Name; 56 import jdk.jfr.SettingDescriptor; 57 import jdk.jfr.Timespan; 58 import jdk.jfr.Timestamp; 59 import jdk.jfr.ValueDescriptor; 60 61 public final class TypeLibrary { 62 63 private static TypeLibrary instance; 64 private static final Map<Long, Type> types = new LinkedHashMap<>(100); 65 static final ValueDescriptor DURATION_FIELD = createDurationField(); 66 static final ValueDescriptor THREAD_FIELD = createThreadField(); 67 static final ValueDescriptor STACK_TRACE_FIELD = createStackTraceField(); 68 static final ValueDescriptor START_TIME_FIELD = createStartTimeField(); 69 70 private TypeLibrary(List<Type> jvmTypes) { 71 visitReachable(jvmTypes, t -> !types.containsKey(t.getId()), t -> types.put(t.getId(), t)); 72 if (LogTag.JFR_SYSTEM_METADATA.shouldLog(LogLevel.INFO.level)) { 73 Stream<Type> s = types.values().stream().sorted((x, y) -> Long.compare(x.getId(), y.getId())); 74 s.forEach(t -> t.log("Added", LogTag.JFR_SYSTEM_METADATA, LogLevel.INFO)); 75 } 76 } 77 78 private static ValueDescriptor createStartTimeField() { 79 List<AnnotationElement> annos = createStandardAnnotations("Start Time", null); 80 annos.add(new jdk.jfr.AnnotationElement(Timestamp.class, Timestamp.TICKS)); 81 return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_START_TIME, Type.LONG, annos, 0, false, 82 EventInstrumentation.FIELD_START_TIME); 83 84 } 85 86 private static ValueDescriptor createStackTraceField() { 87 List<AnnotationElement> annos = new ArrayList<>(); 88 annos = createStandardAnnotations("Stack Trace", "Stack Trace starting from the method the event was committed in"); 89 return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_STACK_TRACE, Type.STACK_TRACE, annos, 0, true, 90 EventInstrumentation.FIELD_STACK_TRACE); 91 } 92 93 private static ValueDescriptor createThreadField() { 94 List<AnnotationElement> annos = new ArrayList<>(); 95 annos = createStandardAnnotations("Event Thread", "Thread in which event was committed in"); 96 return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_EVENT_THREAD, Type.THREAD, annos, 0, true, 97 EventInstrumentation.FIELD_EVENT_THREAD); 98 } 99 100 private static ValueDescriptor createDurationField() { 101 List<AnnotationElement> annos = new ArrayList<>(); 102 annos = createStandardAnnotations("Duration", null); 103 annos.add(new jdk.jfr.AnnotationElement(Timespan.class, Timespan.TICKS)); 104 return PrivateAccess.getInstance().newValueDescriptor(EventInstrumentation.FIELD_DURATION, Type.LONG, annos, 0, false, EventInstrumentation.FIELD_DURATION); 105 } 106 107 public static TypeLibrary getInstance() { 108 synchronized (TypeLibrary.class) { 109 if (instance == null) { 110 List<Type> jvmTypes; 111 try { 112 jvmTypes = MetadataHandler.createTypes(); 113 Collections.sort(jvmTypes, (a,b) -> Long.compare(a.getId(), b.getId())); 114 } catch (IOException e) { 115 throw new Error("JFR: Could not read metadata"); 116 } 117 instance = new TypeLibrary(jvmTypes); 118 } 119 return instance; 120 } 121 } 122 123 public List<Type> getTypes() { 124 return new ArrayList<>(types.values()); 125 } 126 127 public static Type createAnnotationType(Class<? extends Annotation> a) { 128 if (shouldPersist(a)) { 129 Type type = defineType(a, Type.SUPER_TYPE_ANNOTATION, false); 130 if (type != null) { 131 SecuritySupport.makeVisibleToJFR(a); 132 for (Method method : a.getDeclaredMethods()) { 133 type.add(PrivateAccess.getInstance().newValueDescriptor(method.getReturnType(), method.getName())); 134 } 135 ArrayList<AnnotationElement> aes = new ArrayList<>(); 136 for (Annotation annotation : resolveRepeatedAnnotations(a.getAnnotations())) { 137 AnnotationElement ae = createAnnotation(annotation); 138 if (ae != null) { 139 aes.add(ae); 140 } 141 } 142 aes.trimToSize(); 143 type.setAnnotations(aes); 144 } 145 return getType(a); 146 } 147 return null; 148 } 149 150 static AnnotationElement createAnnotation(Annotation annotation) { 151 Class<? extends Annotation> annotationType = annotation.annotationType(); 152 Type type = createAnnotationType(annotationType); 153 if (type != null) { 154 List<Object> values = new ArrayList<>(); 155 for (ValueDescriptor v : type.getFields()) { 156 values.add(invokeAnnotation(annotation, v.getName())); 157 } 158 159 return PrivateAccess.getInstance().newAnnotation(type, values, annotation.annotationType().getClassLoader() == null); 160 } 161 return null; 162 } 163 164 private static Object invokeAnnotation(Annotation annotation, String methodName) { 165 final Method m; 166 try { 167 m = annotation.getClass().getMethod(methodName, new Class<?>[0]); 168 } catch (NoSuchMethodException e1) { 169 throw (Error) new InternalError("Could not loacate method " + methodName + " in annotation " + annotation.getClass().getName()); 170 } 171 SecuritySupport.setAccessible(m); 172 try { 173 return m.invoke(annotation, new Object[0]); 174 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 175 throw (Error) new InternalError("Could not get value for method " + methodName + " in annotation " + annotation.getClass().getName()); 176 } 177 } 178 179 private static boolean shouldPersist(Class<? extends Annotation> a) { 180 if (a == MetadataDefinition.class || a.getAnnotation(MetadataDefinition.class) == null) { 181 return false; 182 } 183 return true; 184 } 185 186 private static boolean isDefined(Class<?> clazz) { 187 return types.containsKey(Type.getTypeId(clazz)); 188 } 189 190 private static Type getType(Class<?> clazz) { 191 return types.get(Type.getTypeId(clazz)); 192 } 193 194 private static Type defineType(Class<?> clazz, String superType, boolean eventType) { 195 if (!isDefined(clazz)) { 196 Name name = clazz.getAnnotation(Name.class); 197 String typeName = name != null ? name.value() : clazz.getName(); 198 long id = Type.getTypeId(clazz); 199 Type t; 200 if (eventType) { 201 t = new PlatformEventType(typeName, id, clazz.getClassLoader() == null, true); 202 } else { 203 t = new Type(typeName, superType, id); 204 } 205 types.put(t.getId(), t); 206 return t; 207 } 208 return null; 209 } 210 public static Type createType(Class<?> clazz) { 211 return createType(clazz, Collections.emptyList(), Collections.emptyList()); 212 } 213 214 public static Type createType(Class<?> clazz, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> dynamicFields) { 215 216 if (Thread.class == clazz) { 217 return Type.THREAD; 218 } 219 220 if (Class.class.isAssignableFrom(clazz)) { 221 return Type.CLASS; 222 } 223 224 if (String.class.equals(clazz)) { 225 return Type.STRING; 226 } 227 228 if (isDefined(clazz)) { 229 return getType(clazz); 230 } 231 232 if (clazz.isPrimitive()) { 233 return defineType(clazz, null,false); 234 } 235 236 if (clazz.isArray()) { 237 throw new InternalError("Arrays not supported"); 238 } 239 240 // STRUCT 241 String superType = null; 242 boolean eventType = false; 243 if (Event.class.isAssignableFrom(clazz)) { 244 superType = Type.SUPER_TYPE_EVENT; 245 eventType= true; 246 } 247 if (Control.class.isAssignableFrom(clazz)) { 248 superType = Type.SUPER_TYPE_SETTING; 249 } 250 251 // forward declare to avoid infinite recursion 252 defineType(clazz, superType, eventType); 253 Type type = getType(clazz); 254 255 if (eventType) { 256 addImplicitFields(type, true, true, true, true ,false); 257 addUserFields(clazz, type, dynamicFields); 258 type.trimFields(); 259 } 260 addAnnotations(clazz, type, dynamicAnnotations); 261 262 if (clazz.getClassLoader() == null) { 263 type.log("Added", LogTag.JFR_SYSTEM_METADATA, LogLevel.INFO); 264 } else { 265 type.log("Added", LogTag.JFR_METADATA, LogLevel.INFO); 266 } 267 return type; 268 } 269 270 private static void addAnnotations(Class<?> clazz, Type type, List<AnnotationElement> dynamicAnnotations) { 271 ArrayList<AnnotationElement> aes = new ArrayList<>(); 272 if (dynamicAnnotations.isEmpty()) { 273 for (Annotation a : Utils.getAnnotations(clazz)) { 274 AnnotationElement ae = createAnnotation(a); 275 if (ae != null) { 276 aes.add(ae); 277 } 278 } 279 } else { 280 List<Type> newTypes = new ArrayList<>(); 281 aes.addAll(dynamicAnnotations); 282 for (AnnotationElement ae : dynamicAnnotations) { 283 newTypes.add(PrivateAccess.getInstance().getType(ae)); 284 } 285 addTypes(newTypes); 286 } 287 type.setAnnotations(aes); 288 aes.trimToSize(); 289 } 290 291 private static void addUserFields(Class<?> clazz, Type type, List<ValueDescriptor> dynamicFields) { 292 Map<String, ValueDescriptor> dynamicFieldSet = new HashMap<>(); 293 for (ValueDescriptor dynamicField : dynamicFields) { 294 dynamicFieldSet.put(dynamicField.getName(), dynamicField); 295 } 296 List<Type> newTypes = new ArrayList<>(); 297 for (Field field : Utils.getVisibleEventFields(clazz)) { 298 ValueDescriptor vd = dynamicFieldSet.get(field.getName()); 299 if (vd != null) { 300 if (!vd.getTypeName().equals(field.getType().getName())) { 301 throw new InternalError("Type expected to match for field " + vd.getName() + " expected " + field.getName() + " but got " + vd.getName()); 302 } 303 for (AnnotationElement ae : vd.getAnnotationElements()) { 304 newTypes.add(PrivateAccess.getInstance().getType(ae)); 305 } 306 newTypes.add(PrivateAccess.getInstance().getType(vd)); 307 } else { 308 vd = createField(field); 309 } 310 if (vd != null) { 311 type.add(vd); 312 } 313 } 314 addTypes(newTypes); 315 } 316 317 // By convention all events have these fields. 318 static void addImplicitFields(Type type, boolean requestable, boolean hasDuration, boolean hasThread, boolean hasStackTrace, boolean hasCutoff) { 319 createAnnotationType(Timespan.class); 320 createAnnotationType(Timestamp.class); 321 createAnnotationType(Label.class); 322 defineType(long.class, null,false); 323 addFields(type, requestable, hasDuration, hasThread, hasStackTrace, hasCutoff); 324 } 325 326 private static void addFields(Type type, boolean requestable, boolean hasDuration, boolean hasThread, boolean hasStackTrace, boolean hasCutoff) { 327 type.add(START_TIME_FIELD); 328 if (hasDuration || hasCutoff) { 329 type.add(DURATION_FIELD); 330 } 331 if (hasThread) { 332 type.add(THREAD_FIELD); 333 } 334 if (hasStackTrace) { 335 type.add(STACK_TRACE_FIELD); 336 } 337 } 338 339 private static List<AnnotationElement> createStandardAnnotations(String name, String description) { 340 List<AnnotationElement> annotationElements = new ArrayList<>(2); 341 annotationElements.add(new jdk.jfr.AnnotationElement(Label.class, name)); 342 if (description != null) { 343 annotationElements.add(new jdk.jfr.AnnotationElement(Description.class, description)); 344 } 345 return annotationElements; 346 } 347 348 private static ValueDescriptor createField(Field field) { 349 int mod = field.getModifiers(); 350 if (Modifier.isTransient(mod)) { 351 return null; 352 } 353 if (Modifier.isStatic(mod)) { 354 return null; 355 } 356 Class<?> fieldType = field.getType(); 357 if (!Type.isKnownType(fieldType)) { 358 return null; 359 } 360 boolean constantPool = Thread.class == fieldType || fieldType == Class.class; 361 Type type = createType(fieldType); 362 String fieldName = field.getName(); 363 Name name = field.getAnnotation(Name.class); 364 String useName = fieldName; 365 if (name != null) { 366 useName = name.value(); 367 } 368 List<jdk.jfr.AnnotationElement> ans = new ArrayList<>(); 369 for (Annotation a : resolveRepeatedAnnotations(field.getAnnotations())) { 370 AnnotationElement ae = createAnnotation(a); 371 if (ae != null) { 372 ans.add(ae); 373 } 374 } 375 return PrivateAccess.getInstance().newValueDescriptor(useName, type, ans, 0, constantPool, fieldName); 376 } 377 378 private static List<Annotation> resolveRepeatedAnnotations(Annotation[] annotations) { 379 List<Annotation> annos = new ArrayList<>(annotations.length); 380 for (Annotation a : annotations) { 381 boolean repeated = false; 382 Method m; 383 try { 384 m = a.annotationType().getMethod("value"); 385 Class<?> returnType = m.getReturnType(); 386 if (returnType.isArray()) { 387 Class<?> ct = returnType.getComponentType(); 388 if (Annotation.class.isAssignableFrom(ct) && ct.getAnnotation(Repeatable.class) != null) { 389 Object res = m.invoke(a, new Object[0]); 390 if (res != null && Annotation[].class.isAssignableFrom(res.getClass())) { 391 for (Annotation rep : (Annotation[]) m.invoke(a, new Object[0])) { 392 annos.add(rep); 393 } 394 repeated = true; 395 } 396 } 397 } 398 } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 399 // Ignore, can't access repeatable information 400 } 401 if (!repeated) { 402 annos.add(a); 403 } 404 } 405 return annos; 406 } 407 408 // Purpose of this method is to mark types that are reachable 409 // from registered event types. Those types that are not reachable can 410 // safely be removed 411 public boolean clearUnregistered() { 412 Logger.log(LogTag.JFR_METADATA, LogLevel.TRACE, "Cleaning out obsolete metadata"); 413 List<Type> registered = new ArrayList<>(); 414 for (Type type : types.values()) { 415 if (type instanceof PlatformEventType) { 416 if (((PlatformEventType) type).isRegistered()) { 417 registered.add(type); 418 } 419 } 420 } 421 visitReachable(registered, t -> t.getRemove(), t -> t.setRemove(false)); 422 List<Long> removeIds = new ArrayList<>(); 423 for (Type type : types.values()) { 424 if (type.getRemove() && !Type.isDefinedByJVM(type.getId())) { 425 removeIds.add(type.getId()); 426 if (LogTag.JFR_METADATA.shouldLog(LogLevel.TRACE.level)) { 427 Logger.log(LogTag.JFR_METADATA, LogLevel.TRACE, "Removed obsolete metadata " + type.getName()); 428 } 429 } 430 // Optimization, set to true now to avoid iterating 431 // types first thing at next call to clearUnregistered 432 type.setRemove(true); 433 } 434 for (Long id : removeIds) { 435 types.remove(id); 436 } 437 return !removeIds.isEmpty(); 438 } 439 440 public void addType(Type type) { 441 addTypes(Collections.singletonList(type)); 442 } 443 444 public static void addTypes(List<Type> ts) { 445 if (!ts.isEmpty()) { 446 visitReachable(ts, t -> !types.containsKey(t.getId()), t -> types.put(t.getId(), t)); 447 } 448 } 449 450 /** 451 * Iterates all reachable types from a start collection 452 * 453 * @param rootSet the types to start from 454 * @param p if a type should be accepted 455 * @param c action to take on an accepted type 456 */ 457 private static void visitReachable(Collection<Type> rootSet, Predicate<Type> p, Consumer<Type> c) { 458 Queue<Type> typeQ = new ArrayDeque<>(rootSet); 459 while (!typeQ.isEmpty()) { 460 Type type = typeQ.poll(); 461 if (p.test(type)) { 462 c.accept(type); 463 visitAnnotations(typeQ, type.getAnnotationElements()); 464 for (ValueDescriptor v : type.getFields()) { 465 typeQ.add(PrivateAccess.getInstance().getType(v)); 466 visitAnnotations(typeQ, v.getAnnotationElements()); 467 } 468 if (type instanceof PlatformEventType) { 469 PlatformEventType pe = (PlatformEventType) type; 470 for (SettingDescriptor s : pe.getAllSettings()) { 471 typeQ.add(PrivateAccess.getInstance().getType(s)); 472 visitAnnotations(typeQ, s.getAnnotationElements()); 473 } 474 } 475 } 476 } 477 } 478 479 private static void visitAnnotations(Queue<Type> typeQ, List<AnnotationElement> aes) { 480 Queue<AnnotationElement> aQ = new ArrayDeque<>(aes); 481 Set<AnnotationElement> visited = new HashSet<>(); 482 while (!aQ.isEmpty()) { 483 AnnotationElement ae = aQ.poll(); 484 if (!visited.contains(ae)) { 485 Type ty = PrivateAccess.getInstance().getType(ae); 486 typeQ.add(ty); 487 visited.add(ae); 488 } 489 aQ.addAll(ae.getAnnotationElements()); 490 } 491 } 492 }