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 static java.util.concurrent.TimeUnit.MICROSECONDS; 29 import static java.util.concurrent.TimeUnit.MILLISECONDS; 30 import static java.util.concurrent.TimeUnit.NANOSECONDS; 31 import static java.util.concurrent.TimeUnit.SECONDS; 32 33 import java.io.FileOutputStream; 34 import java.io.FileWriter; 35 import java.io.IOException; 36 import java.io.PrintWriter; 37 import java.io.RandomAccessFile; 38 import java.lang.annotation.Annotation; 39 import java.lang.annotation.Repeatable; 40 import java.lang.reflect.Field; 41 import java.lang.reflect.InvocationTargetException; 42 import java.lang.reflect.Method; 43 import java.lang.reflect.Modifier; 44 import java.nio.file.Path; 45 import java.time.Duration; 46 import java.time.LocalDateTime; 47 import java.util.ArrayList; 48 import java.util.Arrays; 49 import java.util.Collections; 50 import java.util.HashMap; 51 import java.util.List; 52 import java.util.Map; 53 import java.util.Objects; 54 55 import jdk.internal.org.objectweb.asm.ClassReader; 56 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter; 57 import jdk.jfr.Event; 58 import jdk.jfr.FlightRecorderPermission; 59 import jdk.jfr.Recording; 60 import jdk.jfr.RecordingState; 61 import jdk.jfr.internal.handlers.EventHandler; 62 import jdk.jfr.internal.settings.PeriodSetting; 63 import jdk.jfr.internal.settings.StackTraceSetting; 64 import jdk.jfr.internal.settings.ThresholdSetting; 65 66 public final class Utils { 67 68 private static final String INFINITY = "infinity"; 69 70 private static Boolean SAVE_GENERATED; 71 72 public static final String EVENTS_PACKAGE_NAME = "jdk.jfr.events"; 73 public static final String INSTRUMENT_PACKAGE_NAME = "jdk.jfr.internal.instrument"; 74 public static final String HANDLERS_PACKAGE_NAME = "jdk.jfr.internal.handlers"; 75 public static final String REGISTER_EVENT = "registerEvent"; 76 public static final String ACCESS_FLIGHT_RECORDER = "accessFlightRecorder"; 77 78 private final static String LEGACY_EVENT_NAME_PREFIX = "com.oracle.jdk."; 79 checkAccessFlightRecorder()80 public static void checkAccessFlightRecorder() throws SecurityException { 81 SecurityManager sm = System.getSecurityManager(); 82 if (sm != null) { 83 sm.checkPermission(new FlightRecorderPermission(ACCESS_FLIGHT_RECORDER)); 84 } 85 } 86 checkRegisterPermission()87 public static void checkRegisterPermission() throws SecurityException { 88 SecurityManager sm = System.getSecurityManager(); 89 if (sm != null) { 90 sm.checkPermission(new FlightRecorderPermission(REGISTER_EVENT)); 91 } 92 } 93 94 private static enum TimespanUnit { 95 NANOSECONDS("ns", 1000), MICROSECONDS("us", 1000), MILLISECONDS("ms", 1000), SECONDS("s", 60), MINUTES("m", 60), HOURS("h", 24), DAYS("d", 7); 96 97 final String text; 98 final long amount; 99 TimespanUnit(String unit, long amount)100 TimespanUnit(String unit, long amount) { 101 this.text = unit; 102 this.amount = amount; 103 } 104 } 105 106 // Tjis method can't handle Long.MIN_VALUE because absolute value is negative formatDataAmount(String formatter, long amount)107 private static String formatDataAmount(String formatter, long amount) { 108 int exp = (int) (Math.log(Math.abs(amount)) / Math.log(1024)); 109 char unitPrefix = "kMGTPE".charAt(exp - 1); 110 return String.format(formatter, amount / Math.pow(1024, exp), unitPrefix); 111 } 112 formatBytesCompact(long bytes)113 public static String formatBytesCompact(long bytes) { 114 if (bytes < 1024) { 115 return String.valueOf(bytes); 116 } 117 return formatDataAmount("%.1f%cB", bytes); 118 } 119 formatBits(long bits)120 public static String formatBits(long bits) { 121 if (bits == 1 || bits == -1) { 122 return bits + " bit"; 123 } 124 if (bits < 1024 && bits > -1024) { 125 return bits + " bits"; 126 } 127 return formatDataAmount("%.1f %cbit", bits); 128 } 129 formatBytes(long bytes)130 public static String formatBytes(long bytes) { 131 if (bytes == 1 || bytes == -1) { 132 return bytes + " byte"; 133 } 134 if (bytes < 1024 && bytes > -1024) { 135 return bytes + " bytes"; 136 } 137 return formatDataAmount("%.1f %cB", bytes); 138 } 139 formatBytesPerSecond(long bytes)140 public static String formatBytesPerSecond(long bytes) { 141 if (bytes < 1024 && bytes > -1024) { 142 return bytes + " byte/s"; 143 } 144 return formatDataAmount("%.1f %cB/s", bytes); 145 } 146 formatBitsPerSecond(long bits)147 public static String formatBitsPerSecond(long bits) { 148 if (bits < 1024 && bits > -1024) { 149 return bits + " bps"; 150 } 151 return formatDataAmount("%.1f %cbps", bits); 152 } formatTimespan(Duration dValue, String separation)153 public static String formatTimespan(Duration dValue, String separation) { 154 if (dValue == null) { 155 return "0"; 156 } 157 long value = dValue.toNanos(); 158 TimespanUnit result = TimespanUnit.NANOSECONDS; 159 for (TimespanUnit unit : TimespanUnit.values()) { 160 result = unit; 161 long amount = unit.amount; 162 if (result == TimespanUnit.DAYS || value < amount || value % amount != 0) { 163 break; 164 } 165 value /= amount; 166 } 167 return String.format("%d%s%s", value, separation, result.text); 168 } 169 parseTimespanWithInfinity(String s)170 public static long parseTimespanWithInfinity(String s) { 171 if (INFINITY.equals(s)) { 172 return Long.MAX_VALUE; 173 } 174 return parseTimespan(s); 175 } 176 parseTimespan(String s)177 public static long parseTimespan(String s) { 178 if (s.endsWith("ns")) { 179 return Long.parseLong(s.substring(0, s.length() - 2).trim()); 180 } 181 if (s.endsWith("us")) { 182 return NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 2).trim()), MICROSECONDS); 183 } 184 if (s.endsWith("ms")) { 185 return NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 2).trim()), MILLISECONDS); 186 } 187 if (s.endsWith("s")) { 188 return NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS); 189 } 190 if (s.endsWith("m")) { 191 return 60 * NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS); 192 } 193 if (s.endsWith("h")) { 194 return 60 * 60 * NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS); 195 } 196 if (s.endsWith("d")) { 197 return 24 * 60 * 60 * NANOSECONDS.convert(Long.parseLong(s.substring(0, s.length() - 1).trim()), SECONDS); 198 } 199 200 try { 201 Long.parseLong(s); 202 } catch (NumberFormatException nfe) { 203 throw new NumberFormatException("'" + s + "' is not a valid timespan. Shoule be numeric value followed by a unit, i.e. 20 ms. Valid units are ns, us, s, m, h and d."); 204 } 205 // Only accept values with units 206 throw new NumberFormatException("Timespan + '" + s + "' is missing unit. Valid units are ns, us, s, m, h and d."); 207 } 208 209 /** 210 * Return all annotations as they are visible in the source code 211 * 212 * @param clazz class to return annotations from 213 * 214 * @return list of annotation 215 * 216 */ getAnnotations(Class<?> clazz)217 static List<Annotation> getAnnotations(Class<?> clazz) { 218 List<Annotation> annos = new ArrayList<>(); 219 for (Annotation a : clazz.getAnnotations()) { 220 annos.addAll(getAnnotation(a)); 221 } 222 return annos; 223 } 224 getAnnotation(Annotation a)225 private static List<? extends Annotation> getAnnotation(Annotation a) { 226 Class<?> annotated = a.annotationType(); 227 Method valueMethod = getValueMethod(annotated); 228 if (valueMethod != null) { 229 Class<?> returnType = valueMethod.getReturnType(); 230 if (returnType.isArray()) { 231 Class<?> candidate = returnType.getComponentType(); 232 Repeatable r = candidate.getAnnotation(Repeatable.class); 233 if (r != null) { 234 Class<?> repeatClass = r.value(); 235 if (annotated == repeatClass) { 236 return getAnnotationValues(a, valueMethod); 237 } 238 } 239 } 240 } 241 List<Annotation> annos = new ArrayList<>(); 242 annos.add(a); 243 return annos; 244 } 245 isAfter(RecordingState stateToTest, RecordingState b)246 static boolean isAfter(RecordingState stateToTest, RecordingState b) { 247 return stateToTest.ordinal() > b.ordinal(); 248 } 249 isBefore(RecordingState stateToTest, RecordingState b)250 static boolean isBefore(RecordingState stateToTest, RecordingState b) { 251 return stateToTest.ordinal() < b.ordinal(); 252 } 253 isState(RecordingState stateToTest, RecordingState... states)254 static boolean isState(RecordingState stateToTest, RecordingState... states) { 255 for (RecordingState s : states) { 256 if (s == stateToTest) { 257 return true; 258 } 259 } 260 return false; 261 } 262 getAnnotationValues(Annotation a, Method valueMethod)263 private static List<Annotation> getAnnotationValues(Annotation a, Method valueMethod) { 264 try { 265 return Arrays.asList((Annotation[]) valueMethod.invoke(a, new Object[0])); 266 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { 267 return new ArrayList<>(); 268 } 269 } 270 getValueMethod(Class<?> annotated)271 private static Method getValueMethod(Class<?> annotated) { 272 try { 273 return annotated.getMethod("value", new Class<?>[0]); 274 } catch (NoSuchMethodException e) { 275 return null; 276 } 277 } 278 touch(Path dumpFile)279 public static void touch(Path dumpFile) throws IOException { 280 RandomAccessFile raf = new RandomAccessFile(dumpFile.toFile(), "rw"); 281 raf.close(); 282 } 283 unboxType(Class<?> t)284 public static Class<?> unboxType(Class<?> t) { 285 if (t == Integer.class) { 286 return int.class; 287 } 288 if (t == Long.class) { 289 return long.class; 290 } 291 if (t == Float.class) { 292 return float.class; 293 } 294 if (t == Double.class) { 295 return double.class; 296 } 297 if (t == Byte.class) { 298 return byte.class; 299 } 300 if (t == Short.class) { 301 return short.class; 302 } 303 if (t == Boolean.class) { 304 return boolean.class; 305 } 306 if (t == Character.class) { 307 return char.class; 308 } 309 return t; 310 } 311 nanosToTicks(long nanos)312 static long nanosToTicks(long nanos) { 313 return (long) (nanos * JVM.getJVM().getTimeConversionFactor()); 314 } 315 getHandler(Class<? extends jdk.internal.event.Event> eventClass)316 static synchronized EventHandler getHandler(Class<? extends jdk.internal.event.Event> eventClass) { 317 Utils.ensureValidEventSubclass(eventClass); 318 try { 319 Field f = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER); 320 SecuritySupport.setAccessible(f); 321 return (EventHandler) f.get(null); 322 } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { 323 throw new InternalError("Could not access event handler"); 324 } 325 } 326 setHandler(Class<? extends jdk.internal.event.Event> eventClass, EventHandler handler)327 static synchronized void setHandler(Class<? extends jdk.internal.event.Event> eventClass, EventHandler handler) { 328 Utils.ensureValidEventSubclass(eventClass); 329 try { 330 Field field = eventClass.getDeclaredField(EventInstrumentation.FIELD_EVENT_HANDLER); 331 SecuritySupport.setAccessible(field); 332 field.set(null, handler); 333 } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { 334 throw new InternalError("Could not access event handler"); 335 } 336 } 337 sanitizeNullFreeStringMap(Map<String, String> settings)338 public static Map<String, String> sanitizeNullFreeStringMap(Map<String, String> settings) { 339 HashMap<String, String> map = new HashMap<>(settings.size()); 340 for (Map.Entry<String, String> e : settings.entrySet()) { 341 String key = e.getKey(); 342 if (key == null) { 343 throw new NullPointerException("Null key is not allowed in map"); 344 } 345 String value = e.getValue(); 346 if (value == null) { 347 throw new NullPointerException("Null value is not allowed in map"); 348 } 349 map.put(key, value); 350 } 351 return map; 352 } 353 sanitizeNullFreeList(List<T> elements, Class<T> clazz)354 public static <T> List<T> sanitizeNullFreeList(List<T> elements, Class<T> clazz) { 355 List<T> sanitized = new ArrayList<>(elements.size()); 356 for (T element : elements) { 357 if (element == null) { 358 throw new NullPointerException("Null is not an allowed element in list"); 359 } 360 if (element.getClass() != clazz) { 361 throw new ClassCastException(); 362 } 363 sanitized.add(element); 364 } 365 return sanitized; 366 } 367 getVisibleEventFields(Class<?> clazz)368 static List<Field> getVisibleEventFields(Class<?> clazz) { 369 Utils.ensureValidEventSubclass(clazz); 370 List<Field> fields = new ArrayList<>(); 371 for (Class<?> c = clazz; c != jdk.internal.event.Event.class; c = c.getSuperclass()) { 372 for (Field field : c.getDeclaredFields()) { 373 // skip private field in base classes 374 if (c == clazz || !Modifier.isPrivate(field.getModifiers())) { 375 fields.add(field); 376 } 377 } 378 } 379 return fields; 380 } 381 ensureValidEventSubclass(Class<?> eventClass)382 public static void ensureValidEventSubclass(Class<?> eventClass) { 383 if (jdk.internal.event.Event.class.isAssignableFrom(eventClass) && Modifier.isAbstract(eventClass.getModifiers())) { 384 throw new IllegalArgumentException("Abstract event classes are not allowed"); 385 } 386 if (eventClass == Event.class || eventClass == jdk.internal.event.Event.class || !jdk.internal.event.Event.class.isAssignableFrom(eventClass)) { 387 throw new IllegalArgumentException("Must be a subclass to " + Event.class.getName()); 388 } 389 } 390 writeGeneratedASM(String className, byte[] bytes)391 public static void writeGeneratedASM(String className, byte[] bytes) { 392 if (SAVE_GENERATED == null) { 393 // We can't calculate value statically because it will force 394 // initialization of SecuritySupport, which cause 395 // UnsatisfiedLinkedError on JDK 8 or non-Oracle JDKs 396 SAVE_GENERATED = SecuritySupport.getBooleanProperty("jfr.save.generated.asm"); 397 } 398 if (SAVE_GENERATED) { 399 try { 400 try (FileOutputStream fos = new FileOutputStream(className + ".class")) { 401 fos.write(bytes); 402 } 403 404 try (FileWriter fw = new FileWriter(className + ".asm"); PrintWriter pw = new PrintWriter(fw)) { 405 ClassReader cr = new ClassReader(bytes); 406 CheckClassAdapter.verify(cr, true, pw); 407 } 408 Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.INFO, "Instrumented code saved to " + className + ".class and .asm"); 409 } catch (IOException e) { 410 Logger.log(LogTag.JFR_SYSTEM_BYTECODE, LogLevel.INFO, "Could not save instrumented code, for " + className + ".class and .asm"); 411 } 412 } 413 } 414 ensureInitialized(Class<? extends jdk.internal.event.Event> eventClass)415 public static void ensureInitialized(Class<? extends jdk.internal.event.Event> eventClass) { 416 SecuritySupport.ensureClassIsInitialized(eventClass); 417 } 418 makePrimitiveArray(String typeName, List<Object> values)419 public static Object makePrimitiveArray(String typeName, List<Object> values) { 420 int length = values.size(); 421 switch (typeName) { 422 case "int": 423 int[] ints = new int[length]; 424 for (int i = 0; i < length; i++) { 425 ints[i] = (int) values.get(i); 426 } 427 return ints; 428 case "long": 429 long[] longs = new long[length]; 430 for (int i = 0; i < length; i++) { 431 longs[i] = (long) values.get(i); 432 } 433 return longs; 434 435 case "float": 436 float[] floats = new float[length]; 437 for (int i = 0; i < length; i++) { 438 floats[i] = (float) values.get(i); 439 } 440 return floats; 441 442 case "double": 443 double[] doubles = new double[length]; 444 for (int i = 0; i < length; i++) { 445 doubles[i] = (double) values.get(i); 446 } 447 return doubles; 448 449 case "short": 450 short[] shorts = new short[length]; 451 for (int i = 0; i < length; i++) { 452 shorts[i] = (short) values.get(i); 453 } 454 return shorts; 455 case "char": 456 char[] chars = new char[length]; 457 for (int i = 0; i < length; i++) { 458 chars[i] = (char) values.get(i); 459 } 460 return chars; 461 case "byte": 462 byte[] bytes = new byte[length]; 463 for (int i = 0; i < length; i++) { 464 bytes[i] = (byte) values.get(i); 465 } 466 return bytes; 467 case "boolean": 468 boolean[] booleans = new boolean[length]; 469 for (int i = 0; i < length; i++) { 470 booleans[i] = (boolean) values.get(i); 471 } 472 return booleans; 473 case "java.lang.String": 474 String[] strings = new String[length]; 475 for (int i = 0; i < length; i++) { 476 strings[i] = (String) values.get(i); 477 } 478 return strings; 479 } 480 return null; 481 } 482 isSettingVisible(Control c, boolean hasEventHook)483 public static boolean isSettingVisible(Control c, boolean hasEventHook) { 484 if (c instanceof ThresholdSetting) { 485 return !hasEventHook; 486 } 487 if (c instanceof PeriodSetting) { 488 return hasEventHook; 489 } 490 if (c instanceof StackTraceSetting) { 491 return !hasEventHook; 492 } 493 return true; 494 } 495 isSettingVisible(long typeId, boolean hasEventHook)496 public static boolean isSettingVisible(long typeId, boolean hasEventHook) { 497 if (ThresholdSetting.isType(typeId)) { 498 return !hasEventHook; 499 } 500 if (PeriodSetting.isType(typeId)) { 501 return hasEventHook; 502 } 503 if (StackTraceSetting.isType(typeId)) { 504 return !hasEventHook; 505 } 506 return true; 507 } 508 getValidType(Class<?> type, String name)509 public static Type getValidType(Class<?> type, String name) { 510 Objects.requireNonNull(type, "Null is not a valid type for value descriptor " + name); 511 if (type.isArray()) { 512 type = type.getComponentType(); 513 if (type != String.class && !type.isPrimitive()) { 514 throw new IllegalArgumentException("Only arrays of primitives and Strings are allowed"); 515 } 516 } 517 518 Type knownType = Type.getKnownType(type); 519 if (knownType == null || knownType == Type.STACK_TRACE) { 520 throw new IllegalArgumentException("Only primitive types, java.lang.Thread, java.lang.String and java.lang.Class are allowed for value descriptors. " + type.getName()); 521 } 522 return knownType; 523 } 524 smallUnmodifiable(List<T> list)525 public static <T> List<T> smallUnmodifiable(List<T> list) { 526 if (list.isEmpty()) { 527 return Collections.emptyList(); 528 } 529 if (list.size() == 1) { 530 return Collections.singletonList(list.get(0)); 531 } 532 return Collections.unmodifiableList(list); 533 } 534 upgradeLegacyJDKEvent(String eventName)535 public static String upgradeLegacyJDKEvent(String eventName) { 536 if (eventName.length() <= LEGACY_EVENT_NAME_PREFIX.length()) { 537 return eventName; 538 } 539 if (eventName.startsWith(LEGACY_EVENT_NAME_PREFIX)) { 540 int index = eventName.lastIndexOf("."); 541 if (index == LEGACY_EVENT_NAME_PREFIX.length() - 1) { 542 return Type.EVENT_NAME_PREFIX + eventName.substring(index + 1); 543 } 544 } 545 return eventName; 546 } 547 verifyMirror(Class<?> mirror, Class<?> real)548 public static void verifyMirror(Class<?> mirror, Class<?> real) { 549 Class<?> cMirror = Objects.requireNonNull(mirror); 550 Class<?> cReal = Objects.requireNonNull(real); 551 552 while (cReal != null) { 553 Map<String, Field> mirrorFields = new HashMap<>(); 554 if (cMirror != null) { 555 for (Field f : cMirror.getDeclaredFields()) { 556 if (isSupportedType(f.getType())) { 557 mirrorFields.put(f.getName(), f); 558 } 559 } 560 } 561 for (Field realField : cReal.getDeclaredFields()) { 562 if (isSupportedType(realField.getType())) { 563 String fieldName = realField.getName(); 564 Field mirrorField = mirrorFields.get(fieldName); 565 if (mirrorField == null) { 566 throw new InternalError("Missing mirror field for " + cReal.getName() + "#" + fieldName); 567 } 568 if (realField.getModifiers() != mirrorField.getModifiers()) { 569 throw new InternalError("Incorrect modifier for mirror field "+ cMirror.getName() + "#" + fieldName); 570 } 571 mirrorFields.remove(fieldName); 572 } 573 } 574 if (!mirrorFields.isEmpty()) { 575 throw new InternalError( 576 "Found additional fields in mirror class " + cMirror.getName() + " " + mirrorFields.keySet()); 577 } 578 if (cMirror != null) { 579 cMirror = cMirror.getSuperclass(); 580 } 581 cReal = cReal.getSuperclass(); 582 } 583 } 584 isSupportedType(Class<?> type)585 private static boolean isSupportedType(Class<?> type) { 586 if (Modifier.isTransient(type.getModifiers()) || Modifier.isStatic(type.getModifiers())) { 587 return false; 588 } 589 return Type.isValidJavaFieldType(type.getName()); 590 } 591 makeFilename(Recording recording)592 public static String makeFilename(Recording recording) { 593 String pid = JVM.getJVM().getPid(); 594 String date = Repository.REPO_DATE_FORMAT.format(LocalDateTime.now()); 595 String idText = recording == null ? "" : "-id-" + Long.toString(recording.getId()); 596 return "hotspot-" + "pid-" + pid + idText + "-" + date + ".jfr"; 597 } 598 } 599