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 }