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