--- /dev/null 2018-06-17 23:18:20.806999507 +0800 +++ new/test/jdk/jfr/event/oldobject/OldObjects.java 2019-01-29 11:04:02.730879119 +0800 @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.jfr.event.oldobject; + +import java.io.IOException; +import java.util.List; +import java.util.function.Predicate; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedClass; +import jdk.jfr.consumer.RecordedEvent; +import jdk.jfr.consumer.RecordedFrame; +import jdk.jfr.consumer.RecordedMethod; +import jdk.jfr.consumer.RecordedObject; +import jdk.jfr.consumer.RecordedStackTrace; +import jdk.testlibrary.jfr.Events; + +/** + * Utility class to perform Old Object provocation/detection and + * stack trace/object verification for the Old Object Sample JFR event + */ +final public class OldObjects { + + public static final int MIN_SIZE = 99901; // prime number + public final static int LEAK_CONTEXT = 100; // length of chain assiociated with the object sample + public final static int ROOT_CONTEXT = 100; // length of chain assoicated with the root + public final static int MAX_CHAIN_LENGTH = LEAK_CONTEXT + ROOT_CONTEXT; // the VM should not construct chains longer than this + + private static String[] getFrames(String expectedFrame) { + if (expectedFrame != null) { + return new String[] { expectedFrame }; + } else { + return null; + } + } + + /** + * + * @param r + * A recording + * @param expectedFrame + * A frame that must be found on the stack. Null if no check is required. + * @param fieldType + * The object type (of the field). Null if no check is required. + * @param fieldName + * The field name. Null if no check is required. + * @param referrerType + * The class name. Null if no check is required. + * @param minDuration + * The minimum duration of the event, -1 if not applicable. + * @return The count of matching events + * @throws IOException + */ + public static long countMatchingEvents(Recording r, String expectedFrame, Class fieldType, String fieldName, Class referrerType, long minDuration) throws IOException { + return countMatchingEvents(r, getFrames(expectedFrame), fieldType, fieldName, referrerType, minDuration); + } + + /** + * Gets the OldObjectSample events from the provided recording through a dump + * and counts how many events matches the provided parameters. + * + * @param r + * A recording + * @param expectedStack + * Some frames that must be found on the stack. Null if no check is required. + * @param fieldType + * The object type (of the field). Null if no check is required. + * @param fieldName + * The field name. Null if no check is required. + * @param referrerType + * The class name. Null if no check is required. + * @param minDuration + * The minimum duration of the event, -1 if not applicable. + * @return The count of matching events + * @throws IOException + */ + public static long countMatchingEvents(Recording r, String[] expectedStack, Class fieldType, String fieldName, Class referrerType, long minDuration) throws IOException { + return countMatchingEvents(Events.fromRecording(r), fieldType, fieldName, referrerType, minDuration, expectedStack); + } + + /** + * + * @param events + * A list of RecordedEvent. + * @param expectedFrame + * A frame that must be found on the stack. Null if no check is required. + * @param fieldType + * The object type (of the field). Null if no check is required. + * @param fieldName + * The field name. Null if no check is required. + * @param referrerType + * The class name. Null if no check is required. + * @param minDuration + * The minimum duration of the event, -1 if not applicable. + * @return The count of matching events + * @throws IOException + */ + public static long countMatchingEvents(List events, String expectedFrame, Class fieldType, String fieldName, Class referrerType, long minDuration) throws IOException { + return countMatchingEvents(events, fieldType, fieldName, referrerType, minDuration, getFrames(expectedFrame)); + } + + /** + * + * @param events + * The list of events to find matching events in + * @param expectedStack + * Some frames that must be found on the stack. Null if no check is required. + * @param fieldType + * The object type (of the field). Null if no check is required. + * @param fieldName + * The field name. Null if no check is required. + * @param referrerType + * The class name. Null if no check is required. + * @param minDuration + * The minimum duration of the event, -1 if not applicable. + * @return The count of matching events + * @throws IOException + */ + public static long countMatchingEvents(List events, Class fieldType, String fieldName, Class referrerType, long minDuration, String... expectedStack) throws IOException { + String currentThread = Thread.currentThread().getName(); + return events.stream() + .filter(hasJavaThread(currentThread)) + .filter(fieldIsType(fieldType)) + .filter(hasFieldName(fieldName)) + .filter(isReferrerType(referrerType)) + .filter(durationAtLeast(minDuration)) + .filter(hasStackTrace(expectedStack)) + .count(); + } + + private static Predicate hasJavaThread(String expectedThread) { + if (expectedThread != null) { + return e -> e.getThread() != null && expectedThread.equals(e.getThread().getJavaName()); + } else { + return e -> true; + } + } + + private static Predicate hasStackTrace(String[] expectedStack) { + if (expectedStack != null) { + return e -> matchingStackTrace(e.getStackTrace(), expectedStack); + } else { + return e -> true; + } + } + + private static Predicate fieldIsType(Class fieldType) { + if (fieldType != null) { + return e -> e.hasField("object.type") && ((RecordedClass) e.getValue("object.type")).getName().equals(fieldType.getName()); + } else { + return e -> true; + } + } + + private static Predicate hasFieldName(String fieldName) { + if (fieldName != null) { + return e -> { + RecordedObject referrer = e.getValue("object.referrer"); + return referrer != null ? referrer.hasField("field.name") && referrer.getValue("field.name").equals(fieldName) : false; + }; + } else { + return e -> true; + } + } + + private static Predicate isReferrerType(Class referrerType) { + if (referrerType != null) { + return e -> { + RecordedObject referrer = e.getValue("object.referrer"); + return referrer != null ? referrer.hasField("object.type") && + ((RecordedClass) referrer.getValue("object.type")).getName().equals(referrerType.getName()) : false; + }; + } else { + return e -> true; + } + } + + private static Predicate durationAtLeast(long minDurationMs) { + if (minDurationMs > 0) { + return e -> e.getDuration().toMillis() >= minDurationMs; + } else { + return e -> true; + } + } + + public static boolean matchingReferrerClass(RecordedEvent event, String className) { + RecordedObject referrer = event.getValue("object.referrer"); + if (referrer != null) { + if (!referrer.hasField("object.type")) { + return false; + } + + String reportedClass = ((RecordedClass) referrer.getValue("object.type")).getName(); + if (reportedClass.equals(className)) { + return true; + } + } + return false; + } + + public static String getReferrerFieldName(RecordedEvent event) { + RecordedObject referrer = event.getValue("object.referrer"); + return referrer != null && referrer.hasField("field.name") ? referrer.getValue("field.name") : null; + } + + public static boolean matchingStackTrace(RecordedStackTrace stack, String[] expectedStack) { + if (stack == null) { + return false; + } + + List frames = stack.getFrames(); + int pos = findFramePos(frames, expectedStack[0]); + + if (pos == -1) { + return false; + } + + for (String expectedFrame : expectedStack) { + RecordedFrame f = frames.get(pos++); + String frame = frameToString(f); + + if (!frame.contains(expectedFrame)) { + return false; + } + } + return true; + } + + private static int findFramePos(List frames, String frame) { + int pos = 0; + for (RecordedFrame f : frames) { + if (frameToString(f).contains(frame)) { + return pos; + } + pos++; + } + return -1; + } + + private static String frameToString(RecordedFrame f) { + RecordedMethod m = f.getMethod(); + String methodName = m.getName(); + String className = m.getType().getName(); + return className + "." + methodName; + } + + public static void validateReferenceChainLimit(RecordedEvent e, int maxLength) { + int length = 0; + RecordedObject object = e.getValue("object"); + while (object != null) { + ++length; + RecordedObject referrer = object.getValue("referrer"); + object = referrer != null ? referrer.getValue("object") : null; + } + if (length > maxLength) { + throw new RuntimeException("Reference chain max length not respected. Found a chain of length " + length); + } + } +}