test/jdk/jdk/jfr/event/metadata/TestEventMetadata.java
changeset 50113 caf115bb98ad
child 51214 67736b4846a0
equal deleted inserted replaced
50112:7a2a740815b7 50113:caf115bb98ad
       
     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 
       
    26 package jdk.jfr.event.metadata;
       
    27 
       
    28 import java.util.HashSet;
       
    29 import java.util.List;
       
    30 import java.util.Set;
       
    31 
       
    32 import jdk.jfr.EventType;
       
    33 import jdk.jfr.FlightRecorder;
       
    34 import jdk.jfr.ValueDescriptor;
       
    35 import jdk.test.lib.Asserts;
       
    36 import jdk.test.lib.jfr.EventNames;
       
    37 
       
    38 /*
       
    39  * @test
       
    40  * @key jfr
       
    41  * @library /test/lib
       
    42  * @run main/othervm jdk.jfr.event.metadata.TestEventMetadata
       
    43  */
       
    44 
       
    45 public class TestEventMetadata {
       
    46 
       
    47     /*
       
    48      * Short guide to writing event metadata
       
    49      * =====================================
       
    50 
       
    51      * Name
       
    52      * ----
       
    53      *
       
    54      * Symbolic name that is used to identify an event, or a field. Referred to
       
    55      * as "id" and "field" in trace.xml-files and @Name in the Java API. If it is
       
    56      * the name of an event, the name should be prefixed "jdk.", which
       
    57      * happens automatically for native events.
       
    58      *
       
    59      * The name should be short, but not so brief that collision is likely with
       
    60      * future events or fields. It should only consist of letters and numbers.
       
    61      * Use Java naming convention , i.e. "FileRead" for an event and
       
    62      * "allocationRate" for a field. Do not use "_" and don't add the word
       
    63      * "Event" to the event name.
       
    64      *
       
    65      * Abbreviations should be avoided, but may be acceptable if the name
       
    66      * becomes long, or if it is a well established acronym. Write whole words,
       
    67      * i.e. "allocation" instead of "alloc". The name should not be a reserved
       
    68      * Java keyword, i.e "void" or "class".
       
    69      *
       
    70      * Label
       
    71      * -----
       
    72      *
       
    73      * Describes a human-readable name, typically 1-3 words. Use headline-style
       
    74      * capitalization, capitalize the first and last words, and all nouns,
       
    75      * pronouns, adjectives, verbs and adverbs. Do not include ending
       
    76      * punctuation.
       
    77      *
       
    78      * Description
       
    79      * -----------
       
    80      *
       
    81      * Describes an event with a sentence or two. It's better to omit the
       
    82      * description then copying the label. Use sentence-style
       
    83      * capitalization, capitalize the first letter of the first word, and any
       
    84      * proper names such as the word Java. If the description is one sentence,
       
    85      * period should not be included.
       
    86      *
       
    87      *
       
    88      * Do not forget to set proper units for fields, i.e "NANOS", "MILLS",
       
    89      * "TICKSPAN" ,"BYETS", "PECENTAGE" etc. in native and @Timespan, @Timespan
       
    90      * etc. in Java.
       
    91      */
       
    92     public static void main(String[] args) throws Exception {
       
    93         Set<String> types = new HashSet<>();
       
    94         List<EventType> eventTypes = FlightRecorder.getFlightRecorder().getEventTypes();
       
    95         Set<String> eventNames= new HashSet<>();
       
    96         for (EventType eventType : eventTypes) {
       
    97             verifyEventType(eventType);
       
    98             verifyValueDesscriptors(eventType.getFields(), types);
       
    99             System.out.println();
       
   100             String eventName = eventType.getName();
       
   101             if (eventNames.contains(eventName)) {
       
   102                 throw new Exception("Event with name " +eventName+ " already exists");
       
   103             }
       
   104             eventNames.add(eventName);
       
   105             Set<String> fieldNames = new HashSet<>();
       
   106             for (ValueDescriptor v : eventType.getFields()) {
       
   107                 String fieldName = v.getName();
       
   108                 if (fieldNames.contains(fieldName)) {
       
   109                     throw new Exception("Field with name " + fieldName +" is already in use in event name " +eventName);
       
   110                 }
       
   111                 fieldNames.add(fieldName);
       
   112             }
       
   113         }
       
   114     }
       
   115 
       
   116     private static void verifyValueDesscriptors(List<ValueDescriptor> fields, Set<String> visitedTypes) {
       
   117         for (ValueDescriptor v : fields) {
       
   118             if (!visitedTypes.contains(v.getTypeName())) {
       
   119                 visitedTypes.add(v.getTypeName());
       
   120                 verifyValueDesscriptors(v.getFields(), visitedTypes);
       
   121             }
       
   122             verifyValueDescriptor(v);
       
   123         }
       
   124     }
       
   125 
       
   126     private static void verifyValueDescriptor(ValueDescriptor v) {
       
   127         verifyName(v.getName());
       
   128         verifyLabel(v.getLabel());
       
   129         verifyDescription(v.getDescription());
       
   130     }
       
   131 
       
   132     private static void verifyDescription(String description) {
       
   133         if (description == null) {
       
   134             return;
       
   135         }
       
   136         Asserts.assertTrue(description.length() > 10, "Description must be at least ten characters");
       
   137         Asserts.assertTrue(description.length() < 300, "Description should not exceed 300 characters. Found " + description);
       
   138         Asserts.assertTrue(description.length() == description.trim().length(), "Description should not have trim character at start or end");
       
   139         Asserts.assertFalse(description.endsWith(".") && description.indexOf(".") == description.length() - 1, "Single sentence descriptions should not use end punctuation");
       
   140     }
       
   141 
       
   142     private static void verifyName(String name) {
       
   143         System.out.println("Verifying name: " + name);
       
   144         Asserts.assertNotEquals(name, null, "Name not allowed to be null");
       
   145         Asserts.assertTrue(name.length() > 1, "Name must be at least two characters");
       
   146         Asserts.assertTrue(name.length() < 32, "Name should not exceed 32 characters");
       
   147         Asserts.assertFalse(isReservedKeyword(name),"Name must not be reserved keyword in the Java language (" + name + ")");
       
   148         char firstChar = name.charAt(0);
       
   149         Asserts.assertTrue(Character.isAlphabetic(firstChar), "Name must start with a character");
       
   150         Asserts.assertTrue(Character.isLowerCase(firstChar), "Name must start with lower case letter");
       
   151         Asserts.assertTrue(Character.isJavaIdentifierStart(firstChar), "Not valid first character for Java identifier");
       
   152         for (int i = 1; i < name.length(); i++) {
       
   153             Asserts.assertTrue(Character.isJavaIdentifierPart(name.charAt(i)), "Not valid character for a Java identifier");
       
   154             Asserts.assertTrue(Character.isAlphabetic(name.charAt(i)), "Name must consists of characters, found '" + name.charAt(i) + "'");
       
   155         }
       
   156         Asserts.assertFalse(name.contains("ID"), "'ID' should not be used in name, consider using 'Id'");
       
   157         checkCommonAbbreviations(name);
       
   158     }
       
   159 
       
   160     private static void verifyLabel(String label) {
       
   161         Asserts.assertNotEquals(label, null, "Label not allowed to be null");
       
   162         Asserts.assertTrue(label.length() > 1, "Name must be at least two characters");
       
   163         Asserts.assertTrue(label.length() < 45, "Label should not exceed 45 characters, use description to explain " + label);
       
   164         Asserts.assertTrue(label.length() == label.trim().length(), "Label should not have trim character at start and end");
       
   165         Asserts.assertTrue(Character.isUpperCase(label.charAt(0)), "Label should start with upper case letter");
       
   166         for (int i = 0; i < label.length(); i++) {
       
   167             char c = label.charAt(i);
       
   168             Asserts.assertTrue(Character.isDigit(c) || Character.isAlphabetic(label.charAt(i)) || c == ' ' || c == '(' || c == ')' || c == '-', "Label should only consist of letters or space, found '" + label.charAt(i)
       
   169                     + "'");
       
   170         }
       
   171     }
       
   172 
       
   173     private static void verifyEventType(EventType eventType) {
       
   174         System.out.println("Verifying event: " + eventType.getName());
       
   175         verifyDescription(eventType.getDescription());
       
   176         verifyLabel(eventType.getLabel());
       
   177         Asserts.assertNotEquals(eventType.getName(), null, "Name not allowed to be null");
       
   178         Asserts.assertTrue(eventType.getName().startsWith(EventNames.PREFIX), "OpenJDK events must start with " + EventNames.PREFIX);
       
   179         String name = eventType.getName().substring(EventNames.PREFIX.length());
       
   180         Asserts.assertFalse(isReservedKeyword(name),"Name must not be reserved keyword in the Java language (" + name + ")");
       
   181         checkCommonAbbreviations(name);
       
   182           char firstChar = name.charAt(0);
       
   183         Asserts.assertFalse(name.contains("ID"), "'ID' should not be used in name, consider using 'Id'");
       
   184         Asserts.assertTrue(Character.isAlphabetic(firstChar), "Name " + name + " must start with a character");
       
   185         Asserts.assertTrue(Character.isUpperCase(firstChar), "Name " + name + " must start with upper case letter");
       
   186         for (int i = 0; i < name.length(); i++) {
       
   187             char c = name.charAt(i);
       
   188             Asserts.assertTrue(Character.isAlphabetic(c) || Character.isDigit(c), "Name " + name + " must consists of characters or numbers, found '" + name.charAt(i) + "'");
       
   189         }
       
   190     }
       
   191 
       
   192     static boolean isReservedKeyword(String s) {
       
   193         String[] keywords = new String[] {
       
   194                 // "module", "requires", "exports", "to", "uses", "provides", "with", module-info.java
       
   195                 "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum",
       
   196                 "extends", "false", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "null", "package", "private",
       
   197                 "protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try", "void", "volatile", "while" };
       
   198         for (int i = 0; i < keywords.length; i++) {
       
   199             if (s.equals(keywords[i])) {
       
   200                 return true;
       
   201             }
       
   202         }
       
   203         return false;
       
   204     }
       
   205 
       
   206     private static void checkCommonAbbreviations(String name) {
       
   207         String lowerCased = name.toLowerCase();
       
   208         Asserts.assertFalse(lowerCased.contains("info") && !lowerCased.contains("information"), "Use 'information' instead 'info' in name");
       
   209         Asserts.assertFalse(lowerCased.contains("alloc") && !lowerCased.contains("alloca"), "Use 'allocation' instead 'alloc' in name");
       
   210         Asserts.assertFalse(lowerCased.contains("config") && !lowerCased.contains("configuration"), "Use 'configuration' instead of 'config' in name");
       
   211         Asserts.assertFalse(lowerCased.contains("evac") && !lowerCased.contains("evacu"), "Use 'evacuation' instead of 'evac' in name");
       
   212         Asserts.assertFalse(lowerCased.contains("stat") && !(lowerCased.contains("state") ||lowerCased.contains("statistic")) , "Use 'statistics' instead of 'stat' in name");
       
   213         Asserts.assertFalse(name.contains("ID") , "Use 'id' or 'Id' instead of 'ID' in name");
       
   214     }
       
   215 }