1 /* 2 * Copyright (c) 2015, 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.jfr.event.oldobject; 26 27 import java.io.IOException; 28 import java.util.List; 29 import java.util.function.Predicate; 30 31 import jdk.jfr.Recording; 32 import jdk.jfr.consumer.RecordedClass; 33 import jdk.jfr.consumer.RecordedEvent; 34 import jdk.jfr.consumer.RecordedFrame; 35 import jdk.jfr.consumer.RecordedMethod; 36 import jdk.jfr.consumer.RecordedObject; 37 import jdk.jfr.consumer.RecordedStackTrace; 38 import jdk.test.lib.jfr.Events; 39 40 /** 41 * Utility class to perform Old Object provocation/detection and 42 * stack trace/object verification for the Old Object Sample JFR event 43 */ 44 final public class OldObjects { 45 46 public static final int MIN_SIZE = 99901; // prime number 47 public final static int LEAK_CONTEXT = 100; // length of chain assiociated with the object sample 48 public final static int ROOT_CONTEXT = 100; // length of chain assoicated with the root 49 public final static int MAX_CHAIN_LENGTH = LEAK_CONTEXT + ROOT_CONTEXT; // the VM should not construct chains longer than this 50 getFrames(String expectedFrame)51 private static String[] getFrames(String expectedFrame) { 52 if (expectedFrame != null) { 53 return new String[] { expectedFrame }; 54 } else { 55 return null; 56 } 57 } 58 59 /** 60 * 61 * @param r 62 * A recording 63 * @param expectedFrame 64 * A frame that must be found on the stack. Null if no check is required. 65 * @param fieldType 66 * The object type (of the field). Null if no check is required. 67 * @param fieldName 68 * The field name. Null if no check is required. 69 * @param referrerType 70 * The class name. Null if no check is required. 71 * @param minDuration 72 * The minimum duration of the event, -1 if not applicable. 73 * @return The count of matching events 74 * @throws IOException 75 */ countMatchingEvents(Recording r, String expectedFrame, Class<?> fieldType, String fieldName, Class<?> referrerType, long minDuration)76 public static long countMatchingEvents(Recording r, String expectedFrame, Class<?> fieldType, String fieldName, Class<?> referrerType, long minDuration) throws IOException { 77 return countMatchingEvents(r, getFrames(expectedFrame), fieldType, fieldName, referrerType, minDuration); 78 } 79 80 /** 81 * Gets the OldObjectSample events from the provided recording through a dump 82 * and counts how many events matches the provided parameters. 83 * 84 * @param r 85 * A recording 86 * @param expectedStack 87 * Some frames that must be found on the stack. Null if no check is required. 88 * @param fieldType 89 * The object type (of the field). Null if no check is required. 90 * @param fieldName 91 * The field name. Null if no check is required. 92 * @param referrerType 93 * The class name. Null if no check is required. 94 * @param minDuration 95 * The minimum duration of the event, -1 if not applicable. 96 * @return The count of matching events 97 * @throws IOException 98 */ countMatchingEvents(Recording r, String[] expectedStack, Class<?> fieldType, String fieldName, Class<?> referrerType, long minDuration)99 public static long countMatchingEvents(Recording r, String[] expectedStack, Class<?> fieldType, String fieldName, Class<?> referrerType, long minDuration) throws IOException { 100 return countMatchingEvents(Events.fromRecording(r), fieldType, fieldName, referrerType, minDuration, expectedStack); 101 } 102 103 /** 104 * 105 * @param events 106 * A list of RecordedEvent. 107 * @param expectedFrame 108 * A frame that must be found on the stack. Null if no check is required. 109 * @param fieldType 110 * The object type (of the field). Null if no check is required. 111 * @param fieldName 112 * The field name. Null if no check is required. 113 * @param referrerType 114 * The class name. Null if no check is required. 115 * @param minDuration 116 * The minimum duration of the event, -1 if not applicable. 117 * @return The count of matching events 118 * @throws IOException 119 */ countMatchingEvents(List<RecordedEvent> events, String expectedFrame, Class<?> fieldType, String fieldName, Class<?> referrerType, long minDuration)120 public static long countMatchingEvents(List<RecordedEvent> events, String expectedFrame, Class<?> fieldType, String fieldName, Class<?> referrerType, long minDuration) throws IOException { 121 return countMatchingEvents(events, fieldType, fieldName, referrerType, minDuration, getFrames(expectedFrame)); 122 } 123 124 /** 125 * 126 * @param events 127 * The list of events to find matching events in 128 * @param expectedStack 129 * Some frames that must be found on the stack. Null if no check is required. 130 * @param fieldType 131 * The object type (of the field). Null if no check is required. 132 * @param fieldName 133 * The field name. Null if no check is required. 134 * @param referrerType 135 * The class name. Null if no check is required. 136 * @param minDuration 137 * The minimum duration of the event, -1 if not applicable. 138 * @return The count of matching events 139 * @throws IOException 140 */ countMatchingEvents(List<RecordedEvent> events, Class<?> fieldType, String fieldName, Class<?> referrerType, long minDuration, String... expectedStack)141 public static long countMatchingEvents(List<RecordedEvent> events, Class<?> fieldType, String fieldName, Class<?> referrerType, long minDuration, String... expectedStack) throws IOException { 142 String currentThread = Thread.currentThread().getName(); 143 return events.stream() 144 .filter(hasJavaThread(currentThread)) 145 .filter(fieldIsType(fieldType)) 146 .filter(hasFieldName(fieldName)) 147 .filter(isReferrerType(referrerType)) 148 .filter(durationAtLeast(minDuration)) 149 .filter(hasStackTrace(expectedStack)) 150 .count(); 151 } 152 hasJavaThread(String expectedThread)153 private static Predicate<RecordedEvent> hasJavaThread(String expectedThread) { 154 if (expectedThread != null) { 155 return e -> e.getThread() != null && expectedThread.equals(e.getThread().getJavaName()); 156 } else { 157 return e -> true; 158 } 159 } 160 hasStackTrace(String[] expectedStack)161 private static Predicate<RecordedEvent> hasStackTrace(String[] expectedStack) { 162 if (expectedStack != null) { 163 return e -> matchingStackTrace(e.getStackTrace(), expectedStack); 164 } else { 165 return e -> true; 166 } 167 } 168 fieldIsType(Class<?> fieldType)169 private static Predicate<RecordedEvent> fieldIsType(Class<?> fieldType) { 170 if (fieldType != null) { 171 return e -> e.hasField("object.type") && ((RecordedClass) e.getValue("object.type")).getName().equals(fieldType.getName()); 172 } else { 173 return e -> true; 174 } 175 } 176 hasFieldName(String fieldName)177 private static Predicate<RecordedEvent> hasFieldName(String fieldName) { 178 if (fieldName != null) { 179 return e -> { 180 RecordedObject referrer = e.getValue("object.referrer"); 181 return referrer != null ? referrer.hasField("field.name") && referrer.getValue("field.name").equals(fieldName) : false; 182 }; 183 } else { 184 return e -> true; 185 } 186 } 187 isReferrerType(Class<?> referrerType)188 private static Predicate<RecordedEvent> isReferrerType(Class<?> referrerType) { 189 if (referrerType != null) { 190 return e -> { 191 RecordedObject referrer = e.getValue("object.referrer"); 192 return referrer != null ? referrer.hasField("object.type") && 193 ((RecordedClass) referrer.getValue("object.type")).getName().equals(referrerType.getName()) : false; 194 }; 195 } else { 196 return e -> true; 197 } 198 } 199 200 private static Predicate<RecordedEvent> durationAtLeast(long minDurationMs) { 201 if (minDurationMs > 0) { 202 return e -> e.getDuration().toMillis() >= minDurationMs; 203 } else { 204 return e -> true; 205 } 206 } 207 208 public static boolean matchingReferrerClass(RecordedEvent event, String className) { 209 RecordedObject referrer = event.getValue("object.referrer"); 210 if (referrer != null) { 211 if (!referrer.hasField("object.type")) { 212 return false; 213 } 214 215 String reportedClass = ((RecordedClass) referrer.getValue("object.type")).getName(); 216 if (reportedClass.equals(className)) { 217 return true; 218 } 219 } 220 return false; 221 } 222 223 public static String getReferrerFieldName(RecordedEvent event) { 224 RecordedObject referrer = event.getValue("object.referrer"); 225 return referrer != null && referrer.hasField("field.name") ? referrer.getValue("field.name") : null; 226 } 227 228 public static boolean matchingStackTrace(RecordedStackTrace stack, String[] expectedStack) { 229 if (stack == null) { 230 return false; 231 } 232 233 List<RecordedFrame> frames = stack.getFrames(); 234 int pos = findFramePos(frames, expectedStack[0]); 235 236 if (pos == -1) { 237 return false; 238 } 239 240 for (String expectedFrame : expectedStack) { 241 RecordedFrame f = frames.get(pos++); 242 String frame = frameToString(f); 243 244 if (!frame.contains(expectedFrame)) { 245 return false; 246 } 247 } 248 return true; 249 } 250 251 private static int findFramePos(List<RecordedFrame> frames, String frame) { 252 int pos = 0; 253 for (RecordedFrame f : frames) { 254 if (frameToString(f).contains(frame)) { 255 return pos; 256 } 257 pos++; 258 } 259 return -1; 260 } 261 262 private static String frameToString(RecordedFrame f) { 263 RecordedMethod m = f.getMethod(); 264 String methodName = m.getName(); 265 String className = m.getType().getName(); 266 return className + "." + methodName; 267 } 268 269 public static void validateReferenceChainLimit(RecordedEvent e, int maxLength) { 270 int length = 0; 271 RecordedObject object = e.getValue("object"); 272 while (object != null) { 273 ++length; 274 RecordedObject referrer = object.getValue("referrer"); 275 object = referrer != null ? referrer.getValue("object") : null; 276 } 277 if (length > maxLength) { 278 throw new RuntimeException("Reference chain max length not respected. Found a chain of length " + length); 279 } 280 } 281 } 282