--- old/test/gc/g1/plab/TestPLABPromotion.java 2016-03-18 18:05:46.728066416 +0300 +++ new/test/gc/g1/plab/TestPLABPromotion.java 2016-03-18 18:05:46.632064881 +0300 @@ -41,17 +41,16 @@ package gc.g1.plab; import java.util.List; -import java.util.Map; import java.util.Arrays; import java.io.PrintStream; import gc.g1.plab.lib.AppPLABPromotion; import gc.g1.plab.lib.LogParser; import gc.g1.plab.lib.PLABUtils; +import gc.g1.plab.lib.PlabInfo; import jdk.test.lib.OutputAnalyzer; import jdk.test.lib.ProcessTools; -import jdk.test.lib.Platform; /** * Test checks PLAB promotion of different size objects. @@ -63,6 +62,12 @@ // GC ID with old PLAB statistics private final static long GC_ID_OLD_STATS = 2l; + private final static String PLAB_USED_FIELD_NAME = "used"; + private final static String PLAB_DIRECT_ALLOCATED_FIELD_NAME = "direct allocated"; + private final static List FIELDS_TO_EXTRACT = Arrays.asList(PLAB_USED_FIELD_NAME, PLAB_DIRECT_ALLOCATED_FIELD_NAME); + + private static String output; + // Allowable difference for memory consumption (percentage) private final static long MEM_DIFFERENCE_PCT = 5; @@ -120,11 +125,12 @@ System.out.println(out.getOutput()); throw new RuntimeException("Expect exit code 0."); } - checkResults(out.getOutput(), testCase); + output = out.getOutput(); + checkResults(testCase); } } - private static void checkResults(String output, TestCase testCase) { + private static void checkResults(TestCase testCase) { long plabAllocatedSurvivor; long directAllocatedSurvivor; long plabAllocatedOld; @@ -132,65 +138,89 @@ long memAllocated = testCase.getMemToFill(); LogParser logParser = new LogParser(output); - Map survivorStats = getPlabStats(logParser, LogParser.ReportType.SURVIVOR_STATS, GC_ID_SURVIVOR_STATS); - Map oldStats = getPlabStats(logParser, LogParser.ReportType.OLD_STATS, GC_ID_OLD_STATS); + PlabInfo survivorPlabInfo = logParser.getSpecifiedStats(GC_ID_SURVIVOR_STATS, LogParser.ReportType.SURVIVOR_STATS, FIELDS_TO_EXTRACT); + PlabInfo oldPlabInfo = logParser.getSpecifiedStats(GC_ID_OLD_STATS, LogParser.ReportType.OLD_STATS, FIELDS_TO_EXTRACT); - plabAllocatedSurvivor = survivorStats.get("used"); - directAllocatedSurvivor = survivorStats.get("direct allocated"); - plabAllocatedOld = oldStats.get("used"); - directAllocatedOld = oldStats.get("direct allocated"); + checkFields(survivorPlabInfo); + checkFields(oldPlabInfo); + + plabAllocatedSurvivor = survivorPlabInfo.get(PLAB_USED_FIELD_NAME); + directAllocatedSurvivor = survivorPlabInfo.get(PLAB_DIRECT_ALLOCATED_FIELD_NAME); + plabAllocatedOld = oldPlabInfo.get(PLAB_USED_FIELD_NAME); + directAllocatedOld = oldPlabInfo.get(PLAB_DIRECT_ALLOCATED_FIELD_NAME); System.out.printf("Survivor PLAB allocated:%17d Direct allocated: %17d Mem consumed:%17d%n", plabAllocatedSurvivor, directAllocatedSurvivor, memAllocated); System.out.printf("Old PLAB allocated:%17d Direct allocated: %17d Mem consumed:%17d%n", plabAllocatedOld, directAllocatedOld, memAllocated); // Unreachable objects case if (testCase.isDeadObjectCase()) { - // No dead objects should be promoted - if (!(checkRatio(plabAllocatedSurvivor, memAllocated) && checkRatio(directAllocatedSurvivor, memAllocated))) { - System.out.println(output); - throw new RuntimeException("Unreachable objects should not be allocated using PLAB or direct allocated to Survivor"); - } - if (!(checkRatio(plabAllocatedOld, memAllocated) && checkRatio(directAllocatedOld, memAllocated))) { - System.out.println(output); - throw new RuntimeException("Unreachable objects should not be allocated using PLAB or direct allocated to Old"); - } + checkDeadObjectsPromotion(plabAllocatedSurvivor, directAllocatedSurvivor, memAllocated); + checkDeadObjectsPromotion(plabAllocatedOld, directAllocatedOld, memAllocated); + } else { // Live objects case if (testCase.isPromotedByPLAB()) { - // All live small objects should be promoted using PLAB - if (!checkDifferenceRatio(plabAllocatedSurvivor, memAllocated)) { - System.out.println(output); - throw new RuntimeException("Expect that Survivor PLAB allocation are similar to all mem consumed"); - } - if (!checkDifferenceRatio(plabAllocatedOld, memAllocated)) { - System.out.println(output); - throw new RuntimeException("Expect that Old PLAB allocation are similar to all mem consumed"); - } + checkLiveObjectsPromotion(plabAllocatedSurvivor, memAllocated, "Expect that Survivor PLAB allocation are similar to all mem consumed"); + checkLiveObjectsPromotion(plabAllocatedOld, memAllocated, "Expect that Old PLAB allocation are similar to all mem consumed"); } else { // All big objects should be directly allocated - if (!checkDifferenceRatio(directAllocatedSurvivor, memAllocated)) { - System.out.println(output); - throw new RuntimeException("Test fails. Expect that Survivor direct allocation are similar to all mem consumed"); - } - if (!checkDifferenceRatio(directAllocatedOld, memAllocated)) { - System.out.println(output); - throw new RuntimeException("Test fails. Expect that Old direct allocation are similar to all mem consumed"); - } + checkLiveObjectsPromotion(directAllocatedSurvivor, memAllocated, "Expect that Survivor direct allocation are similar to all mem consumed"); + checkLiveObjectsPromotion(directAllocatedOld, memAllocated, "Expect that Old direct allocation are similar to all mem consumed"); } - // All promoted objects size should be similar to all consumed memory - if (!checkDifferenceRatio(plabAllocatedSurvivor + directAllocatedSurvivor, memAllocated)) { - System.out.println(output); - throw new RuntimeException("Test fails. Expect that Survivor gen total allocation are similar to all mem consumed"); - } - if (!checkDifferenceRatio(plabAllocatedOld + directAllocatedOld, memAllocated)) { - System.out.println(output); - throw new RuntimeException("Test fails. Expect that Old gen total allocation are similar to all mem consumed"); - } + checkTotalPromotion(plabAllocatedSurvivor, directAllocatedSurvivor, memAllocated, "Expect that Survivor gen total allocation are similar to all mem consumed"); + checkTotalPromotion(plabAllocatedOld, directAllocatedOld, memAllocated, "Expect that Old gen total allocation are similar to all mem consumed"); } System.out.println("Test passed!"); } + private static void checkTotalPromotion(long plabAllocatedSurvivor, long directAllocatedSurvivor, long memAllocated, String exceptionMessage) { + // All promoted objects size should be similar to all consumed memory + if (!checkDifferenceRatio(plabAllocatedSurvivor + directAllocatedSurvivor, memAllocated)) { + System.out.println(output); + throw new RuntimeException(exceptionMessage); + } + } + + /** + * Checks that live objects were promoted as expected. + * @param plabAllocated + * @param totalMemAllocated + * @param exceptionMessage + */ + private static void checkLiveObjectsPromotion(long plabAllocated, long totalMemAllocated, String exceptionMessage) { + // All live small objects should be promoted using PLAB + if (!checkDifferenceRatio(plabAllocated, totalMemAllocated)) { + System.out.println(output); + throw new RuntimeException(exceptionMessage); + } + } + + /** + * Checks that dead objects are not promoted. + * @param plabPromoted promoted by PLAB + * @param directlyPromoted + * @param memoryAllocated total memory allocated + */ + private static void checkDeadObjectsPromotion(long plabPromoted, long directlyPromoted, long memoryAllocated) { + // No dead objects should be promoted + if (!(checkRatio(plabPromoted, memoryAllocated) && checkRatio(directlyPromoted, memoryAllocated))) { + System.out.println(output); + throw new RuntimeException("Unreachable objects should not be allocated using PLAB or directly allocated to Survivor/Old"); + } + } + + /** + * Checks that PLAB statistics contains expected fields. + * @param info + */ + private static void checkFields(PlabInfo info) { + if (!info.checkFields(FIELDS_TO_EXTRACT)) { + System.out.println(output); + throw new RuntimeException("PLAB log does not contain expected fields"); + } + } + /** * Returns true if checkedValue is less than MEM_DIFFERENCE_PCT percent of controlValue. * @@ -215,14 +245,6 @@ return (Math.abs(checkedValue - controlValue) / controlValue) * 100L < MEM_DIFFERENCE_PCT; } - private static Map getPlabStats(LogParser logParser, LogParser.ReportType type, long gc_id) { - - Map survivorStats = logParser.getEntries() - .get(gc_id) - .get(type); - return survivorStats; - } - /** * Description of one test case. */ --- old/test/gc/g1/plab/TestPLABResize.java 2016-03-18 18:05:47.072071917 +0300 +++ new/test/gc/g1/plab/TestPLABResize.java 2016-03-18 18:05:46.976070382 +0300 @@ -42,16 +42,15 @@ */ package gc.g1.plab; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; import java.io.PrintStream; import gc.g1.plab.lib.LogParser; import gc.g1.plab.lib.PLABUtils; import gc.g1.plab.lib.AppPLABResize; +import gc.g1.plab.lib.PlabReport; import jdk.test.lib.OutputAnalyzer; import jdk.test.lib.ProcessTools; @@ -75,6 +74,8 @@ private static final int ITERATIONS_MEDIUM = 5; private static final int ITERATIONS_HIGH = 8; + private static final String PLAB_SIZE_FIELD_NAME = "actual"; + private final static TestCase[] TEST_CASES = { new TestCase(WASTE_PCT_SMALL, OBJECT_SIZE_SMALL, GC_NUM_SMALL, ITERATIONS_MEDIUM), new TestCase(WASTE_PCT_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_HIGH, ITERATIONS_SMALL), @@ -110,16 +111,14 @@ */ private static void checkResults(String output, TestCase testCase) { final LogParser log = new LogParser(output); - final Map>> entries = log.getEntries(); + final PlabReport report = log.getEntries(); - final ArrayList plabSizes = entries.entrySet() - .stream() - .map(item -> { - return item.getValue() - .get(LogParser.ReportType.SURVIVOR_STATS) - .get("actual"); - }) - .collect(Collectors.toCollection(ArrayList::new)); + final List plabSizes = report.entryStream() + .map(item -> item.getValue() + .get(LogParser.ReportType.SURVIVOR_STATS) + .get(PLAB_SIZE_FIELD_NAME) + ) + .collect(Collectors.toList()); // Check that desired plab size was changed during iterations. // The test case does 3 rounds of allocations. The second round of N allocations and GC's --- old/test/gc/g1/plab/lib/LogParser.java 2016-03-18 18:05:47.420077482 +0300 +++ new/test/gc/g1/plab/lib/LogParser.java 2016-03-18 18:05:47.320075883 +0300 @@ -22,13 +22,15 @@ */ package gc.g1.plab.lib; -import java.util.EnumMap; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * LogParser class parses VM output to get PLAB and ConsumptionStats values. @@ -44,9 +46,6 @@ */ final public class LogParser { - // Name for GC ID field in report. - public final static String GC_ID = "gc_id"; - /** * Type of parsed log element. */ @@ -58,7 +57,7 @@ private final String log; // Contains Map of PLAB statistics for given log. - private final Map>> report; + private final PlabReport report; // GC ID private static final Pattern GC_ID_PATTERN = Pattern.compile("\\[gc,plab\\s*\\] GC\\((\\d+)\\)"); @@ -91,56 +90,107 @@ * * @return The log entries for the Survivor and Old stats. */ - public Map>> getEntries() { + public PlabReport getEntries() { return report; } - private Map>> parseLines() throws NumberFormatException { + private PlabReport parseLines() throws NumberFormatException { Scanner lineScanner = new Scanner(log); - Map>> allocationStatistics = new HashMap<>(); + PlabReport plabReport = new PlabReport(); Optional gc_id; while (lineScanner.hasNextLine()) { String line = lineScanner.nextLine(); - gc_id = getGcId(line); + gc_id = getGcId(line, GC_ID_PATTERN); if (gc_id.isPresent()) { Matcher matcher = PAIRS_PATTERN.matcher(line); if (matcher.find()) { - Map> oneReportItem; - ReportType reportType; - - if (!allocationStatistics.containsKey(gc_id.get())) { - allocationStatistics.put(gc_id.get(), new EnumMap<>(ReportType.class)); + if (!plabReport.containsKey(gc_id.get())) { + plabReport.put(gc_id.get(), new PlabGCStatistics()); } + ReportType reportType = line.contains("Young") ? ReportType.SURVIVOR_STATS : ReportType.OLD_STATS; - if (line.contains("Young")) { - reportType = ReportType.SURVIVOR_STATS; - } else { - reportType = ReportType.OLD_STATS; - } - - oneReportItem = allocationStatistics.get(gc_id.get()); - if (!oneReportItem.containsKey(reportType)) { - oneReportItem.put(reportType, new HashMap<>()); + PlabGCStatistics gcStat = plabReport.get(gc_id.get()); + if (!gcStat.containsKey(reportType)) { + gcStat.put(reportType, new PlabInfo()); } // Extract all pairs from log. - Map plabStats = oneReportItem.get(reportType); + PlabInfo plabInfo = gcStat.get(reportType); do { String pair = matcher.group(); String[] nameValue = pair.replaceAll(": ", ":").split(":"); - plabStats.put(nameValue[0].trim(), Long.parseLong(nameValue[1])); + plabInfo.put(nameValue[0].trim(), Long.parseLong(nameValue[1])); } while (matcher.find()); } } } - return allocationStatistics; + return plabReport; } - private Optional getGcId(String line) { - Matcher number = GC_ID_PATTERN.matcher(line); + private static Optional getGcId(String line, Pattern pattern) { + Matcher number = pattern.matcher(line); if (number.find()) { return Optional.of(Long.parseLong(number.group(1))); } return Optional.empty(); } + + /** + * Extracts GC ID from log. + * + * @param line - one line of log. + * @return GC ID + */ + public static Long getGcIdFromLine(String line, Pattern pattern) { + Optional gcId = getGcId(line, pattern); + if (!gcId.isPresent()) { + System.out.println(line); + throw new RuntimeException("Cannot find GC ID in log."); + } + return gcId.get(); + } + + /** + * Returns Map which contains specified statistics for specified gc ids. + * @param specifiedGcId gc id to get + * @param type PLAB type + * @param fieldsName name of fields in PlabStatistics + * @return + **/ + public Map getSpecifiedStats(List specifiedGcId, LogParser.ReportType type, List fieldsName) { + return getSpecifiedStats(specifiedGcId, type, fieldsName, true); + } + + /** + * Returns PlabStatistics for specified GC ID. + * @param specifiedGcId + * @param type type of statistics + * @param fieldsName name of fields in PlabStatistics + * @return + **/ + public PlabInfo getSpecifiedStats(long specifiedGcId, LogParser.ReportType type, List fieldsName) { + return getSpecifiedStats(Arrays.asList(specifiedGcId), type, fieldsName, true).get(specifiedGcId); + } + + /** + * Returns Map which contains specified statistics. Filters out specified gc ids. + * @param specifiedGcIdForExclude + * @param type + * @param fieldsName + * @return + **/ + public Map getExcludedSpecifiedStats(List specifiedGcIdForExclude, LogParser.ReportType type, List fieldsName) { + return getSpecifiedStats(specifiedGcIdForExclude, type, fieldsName, false); + } + + private Map getSpecifiedStats(List gcIds, LogParser.ReportType type, List fieldNames, boolean extractId) { + return new HashMap<>( + getEntries().entryStream() + .filter(gcLogItem -> extractId == gcIds.contains(gcLogItem.getKey())) + .collect(Collectors.toMap(gcLogItem -> gcLogItem.getKey(), + gcLogItem -> gcLogItem.getValue().get(type).filter(fieldNames) + ) + ) + ); + } } --- /dev/null 2016-02-08 00:02:26.507791838 +0300 +++ new/test/gc/g1/plab/lib/PlabGCStatistics.java 2016-03-18 18:05:47.668081448 +0300 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016, 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. + * + * 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 gc.g1.plab.lib; + +import java.util.EnumMap; +import java.util.Map; + +import gc.g1.plab.lib.LogParser.ReportType; + +/** + * Class that represents PLAB statistics for a single GC. + * It includes both Survivor and Old PLAB statistics. + */ +public class PlabGCStatistics { + + private final Map plabGCStatistics; + + public PlabGCStatistics() { + plabGCStatistics = new EnumMap<>(ReportType.class); + } + + /** + * Checks if the statistics contains the requested type. + * @param reportType + * @returns true, if contains, false otherwise + */ + public boolean containsKey(ReportType reportType) { + return plabGCStatistics.containsKey(reportType); + } + + /** + * Put pair of ReportType and PlabInfo to statistics. + * @param reportType + * @param plabInfo + */ + public void put(ReportType reportType, PlabInfo plabInfo) { + plabGCStatistics.put(reportType, plabInfo); + } + + /** + * Returns PlabInfo of specified type + * @param reportType + * @return + */ + public PlabInfo get(ReportType reportType) { + return plabGCStatistics.get(reportType); + } +} --- /dev/null 2016-02-08 00:02:26.507791838 +0300 +++ new/test/gc/g1/plab/lib/PlabInfo.java 2016-03-18 18:05:47.996086694 +0300 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2016, 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. + * + * 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 gc.g1.plab.lib; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class PlabInfo { + + private final Map plabInfo; + + public PlabInfo() { + plabInfo = new HashMap<>(); + } + + private PlabInfo(Map map) { + plabInfo = new HashMap<>(map); + } + + /** + * Add key and value to underlying Map. + * @param key PLAB info field name + * @param value PLAB info value for field + */ + public void put(String key, long value) { + plabInfo.put(key, value); + } + + /** + * Get stream of Map.Entry representing underlying Map with PLAB information. + */ + public Stream> entryStream() { + return plabInfo.entrySet().stream(); + } + + /** + * Returns the PlabInfo narrowed for the given fields only + * @param fields + * @return PlabInfo + */ + public PlabInfo filter(List fields) { + return new PlabInfo(entryStream() + .filter(field -> fields.contains(field.getKey())) + .collect(Collectors.toMap( + item -> item.getKey(), + item -> item.getValue()) + ) + ); + } + + /** + * Checks if statistic contains expected fields. + * @param fields fields which should be in statistic + * @return true if all fields are in statistic, false otherwise + */ + public boolean checkFields(List fields) { + for (String key : fields) { + if (!plabInfo.containsKey(key)) { + return false; + } + } + return true; + } + + /** + * Return a collection of the values. + * @return collection of values + */ + public Collection values() { + return plabInfo.values(); + } + + /** + * Get value for specified field. + * @param field + * @return long value which is contained in specified field + */ + public long get(String field) { + return plabInfo.get(field); + } +} --- /dev/null 2016-02-08 00:02:26.507791838 +0300 +++ new/test/gc/g1/plab/lib/PlabReport.java 2016-03-18 18:05:48.320091875 +0300 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016, 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. + * + * 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 gc.g1.plab.lib; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +/** + * Class contains representation of GC PLAB log. + */ +public class PlabReport { + + private final Map report = new HashMap<>(); + + public PlabReport() { + } + + /** + * Checks if underlying Map contains requested GC ID. + */ + public boolean containsKey(Long gcId) { + return report.containsKey(gcId); + } + + /** + * Puts GC ID and PlabGCStatistics to underlying Map. + */ + public void put(Long gcId, PlabGCStatistics plabStat) { + report.put(gcId, plabStat); + } + + /** + * Returns PlabGCStatistics for specified GC ID. + */ + public PlabGCStatistics get(Long gcId) { + return report.get(gcId); + } + + /** + * Returns Stream of Map.Entry of underlying Map. + */ + public Stream> entryStream() { + return report.entrySet().stream(); + } +}