8149662: Refactor hotspot/test/gc/g1/plab/lib/LogParser.java

Reviewed-by: tschatzl, dfazunen
This commit is contained in:
Michail Chernov 2016-03-23 15:48:25 +03:00
parent a3b37b6372
commit cd1a1d139c
6 changed files with 402 additions and 92 deletions

View File

@ -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 @@ public class TestPLABPromotion {
// 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<String> 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 @@ public class TestPLABPromotion {
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 @@ public class TestPLABPromotion {
long memAllocated = testCase.getMemToFill();
LogParser logParser = new LogParser(output);
Map<String, Long> survivorStats = getPlabStats(logParser, LogParser.ReportType.SURVIVOR_STATS, GC_ID_SURVIVOR_STATS);
Map<String, Long> 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 @@ public class TestPLABPromotion {
return (Math.abs(checkedValue - controlValue) / controlValue) * 100L < MEM_DIFFERENCE_PCT;
}
private static Map<String, Long> getPlabStats(LogParser logParser, LogParser.ReportType type, long gc_id) {
Map<String, Long> survivorStats = logParser.getEntries()
.get(gc_id)
.get(type);
return survivorStats;
}
/**
* Description of one test case.
*/

View File

@ -41,16 +41,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;
@ -74,6 +73,8 @@ public class TestPLABResize {
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),
@ -109,16 +110,14 @@ public class TestPLABResize {
*/
private static void checkResults(String output, TestCase testCase) {
final LogParser log = new LogParser(output);
final Map<Long, Map<LogParser.ReportType, Map<String, Long>>> entries = log.getEntries();
final PlabReport report = log.getEntries();
final ArrayList<Long> plabSizes = entries.entrySet()
.stream()
.map(item -> {
return item.getValue()
.get(LogParser.ReportType.SURVIVOR_STATS)
.get("actual");
})
.collect(Collectors.toCollection(ArrayList::new));
final List<Long> 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

View File

@ -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 @@ import java.util.regex.Pattern;
*/
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 @@ final public class LogParser {
private final String log;
// Contains Map of PLAB statistics for given log.
private final Map<Long, Map<ReportType, Map<String, Long>>> 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 @@ final public class LogParser {
*
* @return The log entries for the Survivor and Old stats.
*/
public Map<Long, Map<ReportType, Map<String, Long>>> getEntries() {
public PlabReport getEntries() {
return report;
}
private Map<Long, Map<ReportType, Map<String, Long>>> parseLines() throws NumberFormatException {
private PlabReport parseLines() throws NumberFormatException {
Scanner lineScanner = new Scanner(log);
Map<Long, Map<ReportType, Map<String, Long>>> allocationStatistics = new HashMap<>();
PlabReport plabReport = new PlabReport();
Optional<Long> 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<ReportType, Map<String, Long>> 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<String, Long> 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<Long> getGcId(String line) {
Matcher number = GC_ID_PATTERN.matcher(line);
private static Optional<Long> 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<Long> 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<Long,PlabStatistics> 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<Long, PlabInfo> getSpecifiedStats(List<Long> specifiedGcId, LogParser.ReportType type, List<String> 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<String> fieldsName) {
return getSpecifiedStats(Arrays.asList(specifiedGcId), type, fieldsName, true).get(specifiedGcId);
}
/**
* Returns Map<Long,PlabStatistics> which contains specified statistics. Filters out specified gc ids.
* @param specifiedGcIdForExclude
* @param type
* @param fieldsName
* @return
**/
public Map<Long, PlabInfo> getExcludedSpecifiedStats(List<Long> specifiedGcIdForExclude, LogParser.ReportType type, List<String> fieldsName) {
return getSpecifiedStats(specifiedGcIdForExclude, type, fieldsName, false);
}
private Map<Long, PlabInfo> getSpecifiedStats(List<Long> gcIds, LogParser.ReportType type, List<String> 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)
)
)
);
}
}

View File

@ -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<ReportType, PlabInfo> 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);
}
}

View File

@ -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<String, Long> plabInfo;
public PlabInfo() {
plabInfo = new HashMap<>();
}
private PlabInfo(Map<String, Long> 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<Map.Entry<String, Long>> entryStream() {
return plabInfo.entrySet().stream();
}
/**
* Returns the PlabInfo narrowed for the given fields only
* @param fields
* @return PlabInfo
*/
public PlabInfo filter(List<String> 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<String> 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<Long> 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);
}
}

View File

@ -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<Long, PlabGCStatistics> 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<Map.Entry<Long, PlabGCStatistics>> entryStream() {
return report.entrySet().stream();
}
}