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