1 /* 2 * Copyright (c) 2013, 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 package jdk.test.lib.jfr; 26 27 import static jdk.test.lib.Asserts.assertEquals; 28 import static jdk.test.lib.Asserts.assertFalse; 29 import static jdk.test.lib.Asserts.assertNotNull; 30 import static jdk.test.lib.Asserts.assertTrue; 31 import static jdk.test.lib.Asserts.fail; 32 33 import java.io.File; 34 import java.io.IOException; 35 import java.nio.file.Path; 36 import java.time.Duration; 37 import java.time.Instant; 38 import java.util.List; 39 import java.lang.management.ManagementFactory; 40 41 import jdk.jfr.AnnotationElement; 42 import jdk.jfr.EventType; 43 import jdk.jfr.Recording; 44 import jdk.jfr.SettingDescriptor; 45 import jdk.jfr.Timespan; 46 import jdk.jfr.Timestamp; 47 import jdk.jfr.ValueDescriptor; 48 import jdk.jfr.consumer.RecordingFile; 49 import jdk.test.lib.Asserts; 50 import jdk.jfr.consumer.RecordedClass; 51 import jdk.jfr.consumer.RecordedEvent; 52 import jdk.jfr.consumer.RecordedObject; 53 import jdk.jfr.consumer.RecordedThread; 54 import jdk.jfr.consumer.RecordedThreadGroup; 55 56 57 /** 58 * Helper class to verify RecordedEvent content 59 */ 60 public class Events { 61 62 public static EventField assertField(RecordedEvent event, String name) { 63 String[] partNames = name.split("\\."); 64 RecordedObject struct = event; 65 try { 66 for (int i=0; i<partNames.length; ++i) { 67 final String partName = partNames[i]; 68 final boolean isLastPart = i == partNames.length - 1; 69 ValueDescriptor d = getValueDescriptor(struct, partName); 70 if (isLastPart) { 71 return new EventField(struct, d); 72 } else { 73 assertTrue(struct.getValue(partName) instanceof RecordedObject, "Expected '" + partName + "' to be a struct"); 74 struct = struct.getValue(partName); 75 } 76 } 77 } catch (Exception e) { 78 e.printStackTrace(); 79 } 80 System.out.printf("Failed event:%n%s%n", event.toString()); 81 fail(String.format("Field %s not in event", name)); 82 return null; 83 } 84 85 private static RecordedObject getRecordedPackage(final RecordedClass rc) { 86 if (rc == null) { 87 throw new RuntimeException("RecordedClass must not be null!"); 88 } 89 return rc.getValue("package"); 90 } 91 92 private static RecordedObject getRecordedModule(final RecordedObject pkg) { 93 if (pkg == null) { 94 // null package is an unnamed module (null) 95 return null; 96 } 97 98 return pkg.getValue("module"); 99 } 100 /** 101 * Validates the recored name field 102 * 103 * @param ro should be a Package or a Module 104 * @param targetName name to match 105 */ 106 private static boolean isMatchingTargetName(final RecordedObject ro, final String targetName) { 107 if (ro == null) { 108 return targetName == null; 109 } 110 111 final String recordedName = ro.getValue("name"); 112 113 if (recordedName == null) { 114 return targetName == null; 115 } 116 117 return recordedName.equals(targetName); 118 } 119 120 public static void assertClassPackage(final RecordedClass rc, final String packageNameTarget) { 121 final RecordedObject recordedPackage = getRecordedPackage(rc); 122 123 if (recordedPackage == null) { 124 if (packageNameTarget != null) { 125 throw new RuntimeException("RecordedClass package is null!"); 126 } 127 return; 128 } 129 assertTrue(isMatchingTargetName(recordedPackage, packageNameTarget), "mismatched package name! Target is " + packageNameTarget); 130 } 131 132 public static void assertClassModule(final RecordedClass rc, final String moduleNameTarget) { 133 final RecordedObject recordedPackage = getRecordedPackage(rc); 134 final RecordedObject recordedModule = getRecordedModule(recordedPackage); 135 136 if (recordedModule == null) { 137 if (moduleNameTarget != null) { 138 throw new RuntimeException("RecordedClass module is null!"); 139 } 140 return; 141 } 142 143 assertTrue(isMatchingTargetName(recordedModule, moduleNameTarget), "mismatched module name! Target is " + moduleNameTarget); 144 } 145 146 private static ValueDescriptor getValueDescriptor(RecordedObject struct, String name) throws Exception { 147 List<ValueDescriptor> valueDescriptors = struct.getFields(); 148 for (ValueDescriptor d : valueDescriptors) { 149 if (name.equals(d.getName())) { 150 return d; 151 } 152 } 153 System.out.printf("Failed struct:%s", struct.toString()); 154 fail(String.format("Field %s not in struct", name)); 155 return null; 156 } 157 158 public static void hasEvents(List<RecordedEvent> events) { 159 assertFalse(events.isEmpty(), "No events"); 160 } 161 162 public static void hasEvents(RecordingFile file) { 163 assertTrue(file.hasMoreEvents(), "No events"); 164 } 165 166 public static void assertEventThread(RecordedEvent event) { 167 RecordedThread eventThread = event.getThread(); 168 if (eventThread == null) { 169 System.out.printf("Failed event:%n%s%n", event.toString()); 170 fail("No thread in event"); 171 } 172 } 173 174 public static void assertJavaMethod(RecordedEvent event) { 175 assertField(event, "method.name").notEmpty(); 176 assertField(event, "method.descriptor").notEmpty(); 177 assertField(event, "method.modifiers").atLeast(0); 178 assertField(event, "method.hidden"); 179 assertField(event, "method.type.name").notEmpty(); 180 assertField(event, "method.type.modifiers").atLeast(0); 181 } 182 183 public static void assertEventThread(RecordedEvent event, Thread thread) { 184 assertThread(event.getThread(), thread); 185 } 186 187 public static void assertEventThread(RecordedEvent event, String structName, Thread thread) { 188 assertThread(assertField(event, structName).notNull().getValue(), thread); 189 } 190 191 public static void assertDuration(RecordedEvent event, String name, Duration duration) { 192 assertEquals(event.getDuration(name), duration); 193 } 194 195 public static void assertInstant(RecordedEvent event, String name, Instant instant) { 196 assertEquals(event.getInstant(name), instant); 197 } 198 199 public static void assertMissingValue(RecordedEvent event, String name) { 200 ValueDescriptor v = event.getEventType().getField(name); 201 if (v.getAnnotation(Timespan.class) != null) { 202 Duration d = event.getDuration(name); 203 assertTrue(d.getSeconds() == Long.MIN_VALUE && d.getNano() == 0); 204 return; 205 } 206 if (v.getAnnotation(Timestamp.class) != null) { 207 Instant instant = event.getInstant(name); 208 assertTrue(instant.equals(Instant.MIN)); 209 return; 210 } 211 if (v.getTypeName().equals("double")) { 212 double d = event.getDouble(name); 213 assertTrue(Double.isNaN(d) || d == Double.NEGATIVE_INFINITY); 214 return; 215 } 216 if (v.getTypeName().equals("float")) { 217 float f = event.getFloat(name); 218 assertTrue(Float.isNaN(f) || f == Float.NEGATIVE_INFINITY); 219 return; 220 } 221 if (v.getTypeName().equals("int")) { 222 int i = event.getInt(name); 223 assertTrue(i == Integer.MIN_VALUE); 224 return; 225 } 226 if (v.getTypeName().equals("long")) { 227 assertEquals(event.getLong(name), Long.MIN_VALUE); 228 return; 229 } 230 Object o = event.getValue(name); 231 Asserts.assertNull(o); 232 } 233 234 private static void assertThread(RecordedThread eventThread, Thread thread) { 235 assertNotNull(eventThread, "Thread in event was null"); 236 assertEquals(eventThread.getJavaThreadId(), thread.getId(), "Wrong thread id"); 237 assertEquals(eventThread.getJavaName(), thread.getName(), "Wrong thread name"); 238 239 ThreadGroup threadGroup = thread.getThreadGroup(); 240 RecordedThreadGroup eventThreadGroup = eventThread.getThreadGroup(); 241 assertNotNull(eventThreadGroup, "eventThreadGroup was null"); 242 243 // Iterate and check all threadGroups 244 while (eventThreadGroup != null) { 245 final String groupName = eventThreadGroup.getName(); 246 if (threadGroup != null) { 247 assertEquals(groupName, threadGroup.getName(), "Wrong threadGroup name"); 248 threadGroup = threadGroup.getParent(); 249 } else { 250 assertNotNull(groupName, "threadGroup name was null"); 251 assertFalse(groupName.isEmpty(), "threadGroup name was empty"); 252 } 253 eventThreadGroup = eventThreadGroup.getParent(); 254 } 255 } 256 257 public static boolean hasField(RecordedEvent event, String name) { 258 return event.getFields().stream().map(vd -> vd.getName()).anyMatch(s -> s.equals(name)); 259 } 260 261 public static boolean isEventType(RecordedEvent event, String typeName) { 262 return typeName.equals(event.getEventType().getName()); 263 } 264 265 266 /** 267 * Creates a list of events from a recording. 268 * 269 * @param recording recording, not {@code null} 270 * @return an a list, not null 271 * @throws IOException if an event set could not be created due to I/O 272 * errors. 273 */ 274 public static List<RecordedEvent> fromRecording(Recording recording) throws IOException { 275 return RecordingFile.readAllEvents(makeCopy(recording)); 276 } 277 278 public static RecordingFile copyTo(Recording r) throws IOException { 279 return new RecordingFile(makeCopy(r)); 280 } 281 282 private static String getProcessId(final String fallback) { 283 // Note: may fail in some JVM implementations 284 // therefore fallback has to be provided 285 286 // something like '<pid>@<hostname>', at least in SUN / Oracle JVMs 287 final String jvmName = ManagementFactory.getRuntimeMXBean().getName(); 288 289 final int index = jvmName.indexOf('@'); 290 291 if (index < 1) { 292 // part before '@' empty (index = 0) / '@' not found (index = -1) 293 return fallback; 294 } 295 296 try { 297 return Long.toString(Long.parseLong(jvmName.substring(0, index))); 298 } catch (NumberFormatException e) { 299 // ignore 300 } 301 return fallback; 302 } 303 304 private static Path makeCopy(Recording recording) throws IOException { 305 Path p = recording.getDestination(); 306 if (p == null) { 307 File directory = new File("."); 308 // FIXME: Must come up with a way to give human-readable name 309 // this will at least not clash when running parallel. 310 //ProcessHandle h = ProcessHandle.current(); 311 //p = new File(directory.getAbsolutePath(), "recording-" + recording.getId() + "-pid" + h.pid() + ".jfr").toPath(); 312 p = new File(directory.getAbsolutePath(), "recording-" + recording.getId() + "-pid" + getProcessId("666") + ".jfr").toPath(); 313 recording.dump(p); 314 } 315 return p; 316 } 317 318 public static void hasAnnotation(ValueDescriptor field, Class<? extends java.lang.annotation.Annotation> annotationClass) throws Exception { 319 AnnotationElement a = getAnnotation(field, annotationClass); 320 if (a == null) { 321 throw new Exception("Expected " + annotationClass.getSimpleName() + " on field " + field.getName()); 322 } 323 } 324 325 public static void assertAnnotation(ValueDescriptor field, Class<? extends java.lang.annotation.Annotation> annotationClass, String value) throws Exception { 326 AnnotationElement a = getAnnotation(field, annotationClass); 327 Object v = a.getValue("value"); 328 if (!v.equals(value)) { 329 throw new Exception("Expected " + annotationClass.getSimpleName() + " on field " + field.getName() + " to have value " + value + ", but got " + v); 330 } 331 } 332 333 // candidate for moving into API 334 public static AnnotationElement getAnnotation(ValueDescriptor v, Class<?> clazz) throws Exception { 335 for (AnnotationElement a : v.getAnnotationElements()) { 336 if (a.getTypeName().equals(clazz.getName())) { 337 return a; 338 } 339 } 340 341 throw new Exception("Could not find annotation " + clazz.getName()); 342 } 343 344 // candidate for moving into API 345 public static AnnotationElement getAnnotationByName(EventType t, String name) throws Exception { 346 for (AnnotationElement a : t.getAnnotationElements()) { 347 if (a.getTypeName().equals(name)) { 348 return a; 349 } 350 } 351 throw new Exception("Could not find annotation '" + name + " in type " + t.getName()); 352 } 353 354 // candidate for moving into API 355 public static SettingDescriptor getSetting(EventType type, String name) { 356 for (SettingDescriptor s : type.getSettingDescriptors()) { 357 if (s.getName().equals(name)) { 358 return s; 359 } 360 } 361 throw new IllegalArgumentException("Could not setting with name " + name); 362 } 363 364 public static void hasEvent(Recording r, String name) throws IOException { 365 List<RecordedEvent> events = fromRecording(r); 366 Events.hasEvents(events); 367 Events.hasEvent(events, name); 368 } 369 370 public static void hasEvent(List<RecordedEvent> events, String name) throws IOException { 371 if (!containsEvent(events, name)) { 372 Asserts.fail("Missing event " + name + " in recording " + events.toString()); 373 } 374 } 375 376 public static void hasNotEvent(List<RecordedEvent> events, String name) throws IOException { 377 if (containsEvent(events, name)) { 378 Asserts.fail("Rercording should not contain event " + name + " " + events.toString()); 379 } 380 } 381 382 private static boolean containsEvent(List<RecordedEvent> events, String name) { 383 for (RecordedEvent event : events) { 384 if (event.getEventType().getName().equals(name)) { 385 return true; 386 } 387 } 388 return false; 389 } 390 }