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.util.List;
  37 
  38 import jdk.jfr.AnnotationElement;
  39 import jdk.jfr.EventType;
  40 import jdk.jfr.Recording;
  41 import jdk.jfr.SettingDescriptor;
  42 import jdk.jfr.ValueDescriptor;
  43 import jdk.jfr.consumer.RecordingFile;
  44 import jdk.test.lib.Asserts;
  45 import jdk.jfr.consumer.RecordedClass;
  46 import jdk.jfr.consumer.RecordedEvent;
  47 import jdk.jfr.consumer.RecordedObject;
  48 import jdk.jfr.consumer.RecordedThread;
  49 import jdk.jfr.consumer.RecordedThreadGroup;
  50 
  51 
  52 /**
  53  * Helper class to verify RecordedEvent content
  54  */
  55 public class Events {
  56 
  57     public static EventField assertField(RecordedEvent event, String name)  {
  58         String[] partNames = name.split("\\.");
  59         RecordedObject struct = event;
  60         try {
  61             for (int i=0; i<partNames.length; ++i) {
  62                 final String partName =  partNames[i];
  63                 final boolean isLastPart = i == partNames.length - 1;
  64                 ValueDescriptor d = getValueDescriptor(struct, partName);
  65                 if (isLastPart) {
  66                     return new EventField(struct, d);
  67                 } else {
  68                     assertTrue(struct.getValue(partName) instanceof RecordedObject, "Expected '" + partName + "' to be a struct");
  69                     struct = struct.getValue(partName);
  70                 }
  71             }
  72         } catch (Exception e) {
  73             e.printStackTrace();
  74         }
  75         System.out.printf("Failed event:%n%s%n", event.toString());
  76         fail(String.format("Field %s not in event", name));
  77         return null;
  78     }
  79 
  80     private static RecordedObject getRecordedPackage(final RecordedClass rc) {
  81         if (rc == null) {
  82             throw new RuntimeException("RecordedClass must not be null!");
  83         }
  84         return rc.getValue("package");
  85     }
  86 
  87     private static RecordedObject getRecordedModule(final RecordedObject pkg) {
  88         if (pkg == null) {
  89             // null package is an unnamed module (null)
  90             return null;
  91         }
  92 
  93         return pkg.getValue("module");
  94     }
  95     /**
  96      * Validates the recored name field
  97      *
  98      * @param ro should be a Package or a Module
  99      * @param targetName name to match
 100      */
 101     private static boolean isMatchingTargetName(final RecordedObject ro, final String targetName) {
 102         if (ro == null) {
 103             return targetName == null;
 104         }
 105 
 106         final String recordedName = ro.getValue("name");
 107 
 108         if (recordedName == null) {
 109             return targetName == null;
 110         }
 111 
 112         return recordedName.equals(targetName);
 113     }
 114 
 115     public static void assertClassPackage(final RecordedClass rc, final String packageNameTarget) {
 116         final RecordedObject recordedPackage = getRecordedPackage(rc);
 117 
 118         if (recordedPackage == null) {
 119             if (packageNameTarget != null) {
 120                 throw new RuntimeException("RecordedClass package is null!");
 121             }
 122             return;
 123         }
 124         assertTrue(isMatchingTargetName(recordedPackage, packageNameTarget), "mismatched package name! Target is " + packageNameTarget);
 125     }
 126 
 127     public static void assertClassModule(final RecordedClass rc, final String moduleNameTarget) {
 128         final RecordedObject recordedPackage = getRecordedPackage(rc);
 129         final RecordedObject recordedModule = getRecordedModule(recordedPackage);
 130 
 131         if (recordedModule == null) {
 132             if (moduleNameTarget != null) {
 133                 throw new RuntimeException("RecordedClass module is null!");
 134             }
 135             return;
 136         }
 137 
 138        assertTrue(isMatchingTargetName(recordedModule, moduleNameTarget), "mismatched module name! Target is " + moduleNameTarget);
 139     }
 140 
 141     private static ValueDescriptor getValueDescriptor(RecordedObject struct, String name) throws Exception {
 142         List<ValueDescriptor> valueDescriptors = struct.getFields();
 143         for (ValueDescriptor d : valueDescriptors) {
 144             if (name.equals(d.getName())) {
 145                 return d;
 146             }
 147         }
 148         System.out.printf("Failed struct:%s", struct.toString());
 149         fail(String.format("Field %s not in struct", name));
 150         return null;
 151     }
 152 
 153     public static void hasEvents(List<RecordedEvent> events) {
 154         assertFalse(events.isEmpty(), "No events");
 155     }
 156 
 157     public static void hasEvents(RecordingFile file) {
 158         assertTrue(file.hasMoreEvents(), "No events");
 159     }
 160 
 161     public static void assertEventThread(RecordedEvent event) {
 162         RecordedThread eventThread = event.getThread();
 163         if (eventThread == null) {
 164             System.out.printf("Failed event:%n%s%n", event.toString());
 165             fail("No thread in event");
 166         }
 167     }
 168 
 169     public static void assertJavaMethod(RecordedEvent event) {
 170         assertField(event, "method.name").notEmpty();
 171         assertField(event, "method.descriptor").notEmpty();
 172         assertField(event, "method.modifiers").atLeast(0);
 173         assertField(event, "method.hidden");
 174         assertField(event, "method.type.name").notEmpty();
 175         assertField(event, "method.type.modifiers").atLeast(0);
 176     }
 177 
 178     public static void assertEventThread(RecordedEvent event, Thread thread) {
 179         assertThread(event.getThread(), thread);
 180     }
 181 
 182     public static void assertEventThread(RecordedEvent event, String structName, Thread thread) {
 183         assertThread(assertField(event, structName).notNull().getValue(), thread);
 184     }
 185 
 186     private static void assertThread(RecordedThread eventThread, Thread thread) {
 187         assertNotNull(eventThread, "Thread in event was null");
 188         assertEquals(eventThread.getJavaThreadId(), thread.getId(), "Wrong thread id");
 189         assertEquals(eventThread.getJavaName(), thread.getName(), "Wrong thread name");
 190 
 191         ThreadGroup threadGroup = thread.getThreadGroup();
 192         RecordedThreadGroup eventThreadGroup = eventThread.getThreadGroup();
 193         assertNotNull(eventThreadGroup, "eventThreadGroup was null");
 194 
 195         // Iterate and check all threadGroups
 196         while (eventThreadGroup != null) {
 197             final String groupName = eventThreadGroup.getName();
 198             if (threadGroup != null) {
 199                 assertEquals(groupName, threadGroup.getName(), "Wrong threadGroup name");
 200                 threadGroup = threadGroup.getParent();
 201             } else {
 202                 assertNotNull(groupName, "threadGroup name was null");
 203                 assertFalse(groupName.isEmpty(), "threadGroup name was empty");
 204             }
 205             eventThreadGroup = eventThreadGroup.getParent();
 206         }
 207     }
 208 
 209     public static boolean hasField(RecordedEvent event, String name) {
 210         return event.getFields().stream().map(vd -> vd.getName()).anyMatch(s -> s.equals(name));
 211     }
 212 
 213     public static boolean isEventType(RecordedEvent event, String typeName) {
 214         return typeName.equals(event.getEventType().getName());
 215     }
 216 
 217 
 218     /**
 219      * Creates a list of events from a recording.
 220      *
 221      * @param recording recording, not {@code null}
 222      * @return an a list, not null
 223      * @throws IOException if an event set could not be created due to I/O
 224      *         errors.
 225      */
 226     public static List<RecordedEvent> fromRecording(Recording recording) throws IOException {
 227         return RecordingFile.readAllEvents(makeCopy(recording));
 228     }
 229 
 230     public static RecordingFile copyTo(Recording r) throws IOException {
 231         return new RecordingFile(makeCopy(r));
 232     }
 233 
 234     private static Path makeCopy(Recording recording) throws IOException {
 235         Path p = recording.getDestination();
 236         if (p == null) {
 237             File directory = new File(".");
 238             // FIXME: Must come up with a way to give human-readable name
 239             // this will at least not clash when running parallel.
 240             ProcessHandle h = ProcessHandle.current();
 241             p = new File(directory.getAbsolutePath(), "recording-" + recording.getId() + "-pid" + h.pid() + ".jfr").toPath();
 242             recording.dump(p);
 243         }
 244         return p;
 245     }
 246 
 247    public static void hasAnnotation(ValueDescriptor field, Class<? extends java.lang.annotation.Annotation> annotationClass) throws Exception {
 248        AnnotationElement a = getAnnotation(field, annotationClass);
 249        if (a == null) {
 250            throw new Exception("Expected " + annotationClass.getSimpleName() + " on field " + field.getName());
 251        }
 252    }
 253 
 254    public static void assertAnnotation(ValueDescriptor field, Class<? extends java.lang.annotation.Annotation> annotationClass, String value) throws Exception {
 255        AnnotationElement a = getAnnotation(field, annotationClass);
 256        Object v = a.getValue("value");
 257        if (!v.equals(value)) {
 258            throw new Exception("Expected " + annotationClass.getSimpleName() + " on field " + field.getName() + " to have value " + value + ", but got " + v);
 259        }
 260    }
 261 
 262    // candidate for moving into API
 263    public static AnnotationElement getAnnotation(ValueDescriptor v, Class<?> clazz) throws Exception {
 264       for (AnnotationElement a : v.getAnnotationElements()) {
 265           if (a.getTypeName().equals(clazz.getName())) {
 266               return a;
 267           }
 268       }
 269 
 270       throw new Exception("Could not find annotation " + clazz.getName());
 271   }
 272 
 273    // candidate for moving into API
 274    public static AnnotationElement getAnnotationByName(EventType t, String name) throws Exception {
 275        for (AnnotationElement a : t.getAnnotationElements()) {
 276            if (a.getTypeName().equals(name)) {
 277                return a;
 278            }
 279        }
 280        throw new Exception("Could not find annotation '" + name + " in type " + t.getName());
 281    }
 282 
 283     // candidate for moving into API
 284     public static SettingDescriptor getSetting(EventType type, String name) {
 285         for (SettingDescriptor s : type.getSettingDescriptors()) {
 286             if (s.getName().equals(name)) {
 287                 return s;
 288             }
 289         }
 290         throw new IllegalArgumentException("Could not setting with name " + name);
 291     }
 292 
 293     public static void hasEvent(Recording r, String name) throws IOException {
 294         List<RecordedEvent> events = fromRecording(r);
 295         Events.hasEvents(events);
 296         Events.hasEvent(events, name);
 297     }
 298 
 299     public static void hasEvent(List<RecordedEvent> events, String name) throws IOException {
 300         if (!containsEvent(events, name)) {
 301             Asserts.fail("Missing event " + name  + " in recording " + events.toString());
 302         }
 303     }
 304 
 305     public static void hasNotEvent(List<RecordedEvent> events, String name) throws IOException {
 306         if (containsEvent(events, name)) {
 307             Asserts.fail("Rercording should not contain event " + name  + " " + events.toString());
 308         }
 309     }
 310 
 311     private static boolean containsEvent(List<RecordedEvent> events, String name) {
 312         for (RecordedEvent event : events) {
 313             if (event.getEventType().getName().equals(name)) {
 314                 return true;
 315             }
 316         }
 317         return false;
 318     }
 319 }