/* * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.oracle.java.testlibrary.jfr; import static com.oracle.java.testlibrary.Asserts.assertEquals; import static com.oracle.java.testlibrary.Asserts.assertFalse; import static com.oracle.java.testlibrary.Asserts.assertNotNull; import static com.oracle.java.testlibrary.Asserts.assertTrue; import static com.oracle.java.testlibrary.Asserts.fail; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.List; import jdk.jfr.AnnotationElement; import jdk.jfr.EventType; import jdk.jfr.Recording; import jdk.jfr.SettingDescriptor; import jdk.jfr.ValueDescriptor; import jdk.jfr.consumer.RecordingFile; import com.oracle.java.testlibrary.Asserts; import com.oracle.java.testlibrary.ProcessTools; import jdk.jfr.consumer.RecordedClass; import jdk.jfr.consumer.RecordedEvent; import jdk.jfr.consumer.RecordedObject; import jdk.jfr.consumer.RecordedThread; import jdk.jfr.consumer.RecordedThreadGroup; /** * Helper class to verify RecordedEvent content */ public class Events { public static EventField assertField(RecordedEvent event, String name) { String[] partNames = name.split("\\."); RecordedObject struct = event; try { for (int i=0; i valueDescriptors = struct.getFields(); for (ValueDescriptor d : valueDescriptors) { if (name.equals(d.getName())) { return d; } } System.out.printf("Failed struct:%s", struct.toString()); fail(String.format("Field %s not in struct", name)); return null; } public static void hasEvents(List events) { assertFalse(events.isEmpty(), "No events"); } public static void hasEvents(RecordingFile file) { assertTrue(file.hasMoreEvents(), "No events"); } public static void assertEventThread(RecordedEvent event) { RecordedThread eventThread = event.getThread(); if (eventThread == null) { System.out.printf("Failed event:%n%s%n", event.toString()); fail("No thread in event"); } } public static void assertJavaMethod(RecordedEvent event) { assertField(event, "method.name").notEmpty(); assertField(event, "method.descriptor").notEmpty(); assertField(event, "method.modifiers").atLeast(0); assertField(event, "method.hidden"); assertField(event, "method.type.name").notEmpty(); assertField(event, "method.type.modifiers").atLeast(0); } public static void assertEventThread(RecordedEvent event, Thread thread) { assertThread(event.getThread(), thread); } public static void assertEventThread(RecordedEvent event, String structName, Thread thread) { assertThread(assertField(event, structName).notNull().getValue(), thread); } private static void assertThread(RecordedThread eventThread, Thread thread) { assertNotNull(eventThread, "Thread in event was null"); assertEquals(eventThread.getJavaThreadId(), thread.getId(), "Wrong thread id"); assertEquals(eventThread.getJavaName(), thread.getName(), "Wrong thread name"); ThreadGroup threadGroup = thread.getThreadGroup(); RecordedThreadGroup eventThreadGroup = eventThread.getThreadGroup(); assertNotNull(eventThreadGroup, "eventThreadGroup was null"); // Iterate and check all threadGroups while (eventThreadGroup != null) { final String groupName = eventThreadGroup.getName(); if (threadGroup != null) { assertEquals(groupName, threadGroup.getName(), "Wrong threadGroup name"); threadGroup = threadGroup.getParent(); } else { assertNotNull(groupName, "threadGroup name was null"); assertFalse(groupName.isEmpty(), "threadGroup name was empty"); } eventThreadGroup = eventThreadGroup.getParent(); } } public static boolean hasField(RecordedEvent event, String name) { return event.getFields().stream().map(vd -> vd.getName()).anyMatch(s -> s.equals(name)); } public static boolean isEventType(RecordedEvent event, String typeName) { return typeName.equals(event.getEventType().getName()); } /** * Creates a list of events from a recording. * * @param recording recording, not {@code null} * @return an a list, not null * @throws IOException if an event set could not be created due to I/O * errors. */ public static List fromRecording(Recording recording) throws IOException { return RecordingFile.readAllEvents(makeCopy(recording)); } public static RecordingFile copyTo(Recording r) throws IOException { return new RecordingFile(makeCopy(r)); } private static Path makeCopy(Recording recording) throws IOException { Path p = recording.getDestination(); int pid = 0; try { pid = ProcessTools.getProcessId(); } catch (Exception e) { //do nothing, let's use 0 } if (p == null) { File directory = new File("."); // FIXME: Must come up with a way to give human-readable name // this will at least not clash when running parallel. p = new File(directory.getAbsolutePath(), "recording-" + recording.getId() + "-pid" + pid + ".jfr").toPath(); recording.dump(p); } return p; } public static void hasAnnotation(ValueDescriptor field, Class annotationClass) throws Exception { AnnotationElement a = getAnnotation(field, annotationClass); if (a == null) { throw new Exception("Expected " + annotationClass.getSimpleName() + " on field " + field.getName()); } } public static void assertAnnotation(ValueDescriptor field, Class annotationClass, String value) throws Exception { AnnotationElement a = getAnnotation(field, annotationClass); Object v = a.getValue("value"); if (!v.equals(value)) { throw new Exception("Expected " + annotationClass.getSimpleName() + " on field " + field.getName() + " to have value " + value + ", but got " + v); } } // candidate for moving into API public static AnnotationElement getAnnotation(ValueDescriptor v, Class clazz) throws Exception { for (AnnotationElement a : v.getAnnotationElements()) { if (a.getTypeName().equals(clazz.getName())) { return a; } } throw new Exception("Could not find annotation " + clazz.getName()); } // candidate for moving into API public static AnnotationElement getAnnotationByName(EventType t, String name) throws Exception { for (AnnotationElement a : t.getAnnotationElements()) { if (a.getTypeName().equals(name)) { return a; } } throw new Exception("Could not find annotation '" + name + " in type " + t.getName()); } // candidate for moving into API public static SettingDescriptor getSetting(EventType type, String name) { for (SettingDescriptor s : type.getSettingDescriptors()) { if (s.getName().equals(name)) { return s; } } throw new IllegalArgumentException("Could not setting with name " + name); } public static void hasEvent(Recording r, String name) throws IOException { List events = fromRecording(r); Events.hasEvents(events); Events.hasEvent(events, name); } public static void hasEvent(List events, String name) throws IOException { if (!containsEvent(events, name)) { Asserts.fail("Missing event " + name + " in recording " + events.toString()); } } public static void hasNotEvent(List events, String name) throws IOException { if (containsEvent(events, name)) { Asserts.fail("Rercording should not contain event " + name + " " + events.toString()); } } private static boolean containsEvent(List events, String name) { for (RecordedEvent event : events) { if (event.getEventType().getName().equals(name)) { return true; } } return false; } }