# HG changeset patch # User mchernov # Date 1453908145 -10800 # Node ID 6e62be7db2c89132f9f1bd249b440479cb7755b2 # Parent fb46176bb4c61e99a4ff72c36ae62fd372f7f0ba 8141278: New tests for PLAB testing Reviewed-by: tschatzl diff -r fb46176bb4c6 -r 6e62be7db2c8 hotspot/test/gc/g1/plab/TestPLABPromotion.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/g1/plab/TestPLABPromotion.java Wed Jan 27 18:22:25 2016 +0300 @@ -0,0 +1,327 @@ +/* + * 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. + */ + + /* + * @test TestPLABPromotion + * @bug 8141278 + * @summary Test PLAB promotion + * @requires vm.gc=="G1" | vm.gc=="null" + * @library /testlibrary /../../test/lib / + * @modules java.management + * @build ClassFileInstaller + * sun.hotspot.WhiteBox + * gc.g1.plab.lib.MemoryConsumer + * gc.g1.plab.lib.LogParser + * gc.g1.plab.lib.AppPLABPromotion + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main gc.g1.plab.TestPLABPromotion + */ +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 jdk.test.lib.OutputAnalyzer; +import jdk.test.lib.ProcessTools; +import jdk.test.lib.Platform; + +/** + * Test checks PLAB promotion of different size objects. + */ +public class TestPLABPromotion { + + // GC ID with survivor PLAB statistics + private final static long GC_ID_SURVIVOR_STATS = 1l; + // GC ID with old PLAB statistics + private final static long GC_ID_OLD_STATS = 2l; + + // Threshold to determine whether the correct amount of objects were promoted. + // This is only an approximate threshold for these checks. + private final static long MEM_CONSUMPTION_THRESHOLD = 256l * 1024l; + + private static final int PLAB_SIZE_SMALL = 1024; + private static final int PLAB_SIZE_MEDIUM = 4096; + private static final int PLAB_SIZE_HIGH = 65536; + private static final int OBJECT_SIZE_SMALL = 10; + private static final int OBJECT_SIZE_MEDIUM = 100; + private static final int OBJECT_SIZE_HIGH = 1000; + private static final int GC_NUM_SMALL = 1; + private static final int GC_NUM_MEDIUM = 3; + private static final int GC_NUM_HIGH = 7; + private static final int WASTE_PCT_SMALL = 10; + private static final int WASTE_PCT_MEDIUM = 20; + private static final int WASTE_PCT_HIGH = 30; + private static final int YOUNG_SIZE_LOW = 16; + private static final int YOUNG_SIZE_HIGH = 64; + private static final boolean PLAB_FIXED = true; + private static final boolean PLAB_DYNAMIC = false; + + private final static TestCase[] TEST_CASES = { + // Test cases for unreachable object, PLAB size is fixed + new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, false, false), + new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, false, false), + // Test cases for reachable objects, PLAB size is fixed + new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, true, true), + new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_MEDIUM, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, true, true), + new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, PLAB_FIXED, true, false), + new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, true, true), + new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, true, true), + new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_MEDIUM, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, PLAB_FIXED, true, true), + new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_SMALL, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_FIXED, true, true), + new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_HIGH, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_FIXED, true, true), + new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, YOUNG_SIZE_HIGH, PLAB_FIXED, true, false), + // Test cases for unreachable object, PLAB size is not fixed + new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_LOW, PLAB_DYNAMIC, false, false), + // Test cases for reachable objects, PLAB size is not fixed + new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_DYNAMIC, true, true), + new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_SMALL, YOUNG_SIZE_LOW, PLAB_DYNAMIC, true, true), + new TestCase(WASTE_PCT_SMALL, PLAB_SIZE_MEDIUM, OBJECT_SIZE_HIGH, GC_NUM_HIGH, YOUNG_SIZE_HIGH, PLAB_DYNAMIC, true, false), + new TestCase(WASTE_PCT_MEDIUM, PLAB_SIZE_SMALL, OBJECT_SIZE_MEDIUM, GC_NUM_MEDIUM, YOUNG_SIZE_LOW, PLAB_DYNAMIC, true, true), + new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_HIGH, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, YOUNG_SIZE_HIGH, PLAB_DYNAMIC, true, true), + new TestCase(WASTE_PCT_HIGH, PLAB_SIZE_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, YOUNG_SIZE_LOW, PLAB_DYNAMIC, true, true) + }; + + public static void main(String[] args) throws Throwable { + + for (TestCase testCase : TEST_CASES) { + // What we going to check. + testCase.print(System.out); + List options = PLABUtils.prepareOptions(testCase.toOptions()); + options.add(AppPLABPromotion.class.getName()); + OutputAnalyzer out = ProcessTools.executeTestJvm(options.toArray(new String[options.size()])); + if (out.getExitValue() != 0) { + System.out.println(out.getOutput()); + throw new RuntimeException("Expect exit code 0."); + } + checkResults(out.getOutput(), testCase); + } + } + + private static void checkResults(String output, TestCase testCase) { + long plabAllocatedSurvivor; + long directAllocatedSurvivor; + long plabAllocatedOld; + long directAllocatedOld; + long memAllocated = testCase.getMemToFill(); + long wordSize = Platform.is32bit() ? 4l : 8l; + 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); + + plabAllocatedSurvivor = wordSize * survivorStats.get("used"); + directAllocatedSurvivor = wordSize * survivorStats.get("direct_allocated"); + plabAllocatedOld = wordSize * oldStats.get("used"); + directAllocatedOld = wordSize * oldStats.get("direct_allocated"); + + 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 (plabAllocatedSurvivor > MEM_CONSUMPTION_THRESHOLD || directAllocatedSurvivor > MEM_CONSUMPTION_THRESHOLD) { + System.out.println(output); + throw new RuntimeException("Unreachable objects should not be allocated using PLAB or direct allocated to Survivor"); + } + if (plabAllocatedOld > MEM_CONSUMPTION_THRESHOLD || directAllocatedOld > MEM_CONSUMPTION_THRESHOLD) { + System.out.println(output); + throw new RuntimeException("Unreachable objects should not be allocated using PLAB or direct allocated to Old"); + } + } else { + // Live objects case + if (testCase.isPromotedByPLAB()) { + // All live small objects should be promoted using PLAB + if (Math.abs(plabAllocatedSurvivor - memAllocated) > MEM_CONSUMPTION_THRESHOLD) { + System.out.println(output); + throw new RuntimeException("Expect that Survivor PLAB allocation are similar to all mem consumed"); + } + if (Math.abs(plabAllocatedOld - memAllocated) > MEM_CONSUMPTION_THRESHOLD) { + System.out.println(output); + throw new RuntimeException("Expect that Old PLAB allocation are similar to all mem consumed"); + } + } else { + // All big objects should be directly allocated + if (Math.abs(directAllocatedSurvivor - memAllocated) > MEM_CONSUMPTION_THRESHOLD) { + System.out.println(output); + throw new RuntimeException("Test fails. Expect that Survivor direct allocation are similar to all mem consumed"); + } + if (Math.abs(directAllocatedOld - memAllocated) > MEM_CONSUMPTION_THRESHOLD) { + System.out.println(output); + throw new RuntimeException("Test fails. Expect that Old direct allocation are similar to all mem consumed"); + } + } + + // All promoted objects size should be similar to all consumed memory + if (Math.abs(plabAllocatedSurvivor + directAllocatedSurvivor - memAllocated) > MEM_CONSUMPTION_THRESHOLD) { + System.out.println(output); + throw new RuntimeException("Test fails. Expect that Survivor gen total allocation are similar to all mem consumed"); + } + if (Math.abs(plabAllocatedOld + directAllocatedOld - memAllocated) > MEM_CONSUMPTION_THRESHOLD) { + System.out.println(output); + throw new RuntimeException("Test fails. Expect that Old gen total allocation are similar to all mem consumed"); + } + } + System.out.println("Test passed!"); + } + + 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. + */ + private static class TestCase { + + private final int wastePct; + private final int plabSize; + private final int chunkSize; + private final int parGCThreads; + private final int edenSize; + private final boolean plabIsFixed; + private final boolean objectsAreReachable; + private final boolean promotedByPLAB; + + /** + * @param wastePct + * ParallelGCBufferWastePct + * @param plabSize + * -XX:OldPLABSize and -XX:YoungPLABSize + * @param chunkSize + * requested object size for memory consumption + * @param parGCThreads + * -XX:ParallelGCThreads + * @param edenSize + * NewSize and MaxNewSize + * @param plabIsFixed + * Use dynamic PLAB or fixed size PLAB + * @param objectsAreReachable + * true - allocate live objects + * false - allocate unreachable objects + * @param promotedByPLAB + * true - we expect to see PLAB allocation during promotion + * false - objects will be directly allocated during promotion + */ + public TestCase(int wastePct, + int plabSize, + int chunkSize, + int parGCThreads, + int edenSize, + boolean plabIsFixed, + boolean objectsAreReachable, + boolean promotedByPLAB + ) { + if (wastePct == 0 || plabSize == 0 || chunkSize == 0 || parGCThreads == 0 || edenSize == 0) { + throw new IllegalArgumentException("Parameters should not be 0"); + } + this.wastePct = wastePct; + this.plabSize = plabSize; + this.chunkSize = chunkSize; + this.parGCThreads = parGCThreads; + this.edenSize = edenSize; + this.plabIsFixed = plabIsFixed; + this.objectsAreReachable = objectsAreReachable; + this.promotedByPLAB = promotedByPLAB; + } + + /** + * Convert current TestCase to List of options. + * Assume test will fill half of existed eden. + * + * @return + * List of options + */ + public List toOptions() { + return Arrays.asList("-XX:ParallelGCThreads=" + parGCThreads, + "-XX:ParallelGCBufferWastePct=" + wastePct, + "-XX:OldPLABSize=" + plabSize, + "-XX:YoungPLABSize=" + plabSize, + "-XX:" + (plabIsFixed ? "-" : "+") + "ResizePLAB", + "-Dchunk.size=" + chunkSize, + "-Dreachable=" + objectsAreReachable, + "-XX:NewSize=" + edenSize + "m", + "-XX:MaxNewSize=" + edenSize + "m", + "-Dmem.to.fill=" + getMemToFill() + ); + } + + /** + * Print details about test case. + */ + public void print(PrintStream out) { + boolean expectPLABAllocation = promotedByPLAB && objectsAreReachable; + boolean expectDirectAllocation = (!promotedByPLAB) && objectsAreReachable; + + out.println("Test case details:"); + out.println(" Young gen size : " + edenSize + "M"); + out.println(" Predefined PLAB size : " + plabSize); + out.println(" Parallel GC buffer waste pct : " + wastePct); + out.println(" Chunk size : " + chunkSize); + out.println(" Parallel GC threads : " + parGCThreads); + out.println(" Objects are created : " + (objectsAreReachable ? "reachable" : "unreachable")); + out.println(" PLAB size is fixed: " + (plabIsFixed ? "yes" : "no")); + out.println("Test expectations:"); + out.println(" PLAB allocation : " + (expectPLABAllocation ? "expected" : "unexpected")); + out.println(" Direct allocation : " + (expectDirectAllocation ? "expected" : "unexpected")); + } + + /** + * @return + * true if we expect PLAB allocation + * false if no + */ + public boolean isPromotedByPLAB() { + return promotedByPLAB; + } + + /** + * @return + * true if it is test case for unreachable objects + * false for live objects + */ + public boolean isDeadObjectCase() { + return !objectsAreReachable; + } + + /** + * Returns amount of memory to fill + * + * @return amount of memory + */ + public long getMemToFill() { + return (long) (edenSize) * 1024l * 1024l / 2; + } + } +} diff -r fb46176bb4c6 -r 6e62be7db2c8 hotspot/test/gc/g1/plab/TestPLABResize.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/g1/plab/TestPLABResize.java Wed Jan 27 18:22:25 2016 +0300 @@ -0,0 +1,222 @@ +/* + * 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. + */ + + /* + * @test TestPLABResize + * @bug 8141278 + * @summary Test for PLAB resizing + * @requires vm.gc=="G1" | vm.gc=="null" + * @library /testlibrary /../../test/lib / + * @modules java.management + * @build ClassFileInstaller + * sun.hotspot.WhiteBox + * gc.g1.plab.lib.LogParser + * gc.g1.plab.lib.MemoryConsumer + * gc.g1.plab.lib.PLABUtils + * gc.g1.plab.lib.AppPLABResize + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main gc.g1.plab.TestPLABResize + */ +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 jdk.test.lib.OutputAnalyzer; +import jdk.test.lib.ProcessTools; + +/** + * Test for PLAB resizing. + */ +public class TestPLABResize { + + private static final int OBJECT_SIZE_SMALL = 10; + private static final int OBJECT_SIZE_MEDIUM = 70; + private static final int OBJECT_SIZE_HIGH = 150; + private static final int GC_NUM_SMALL = 1; + private static final int GC_NUM_MEDIUM = 3; + private static final int GC_NUM_HIGH = 7; + private static final int WASTE_PCT_SMALL = 10; + private static final int WASTE_PCT_MEDIUM = 20; + private static final int WASTE_PCT_HIGH = 30; + + private static final int ITERATIONS_SMALL = 3; + private static final int ITERATIONS_MEDIUM = 5; + private static final int ITERATIONS_HIGH = 8; + + 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), + new TestCase(WASTE_PCT_SMALL, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, ITERATIONS_HIGH), + new TestCase(WASTE_PCT_MEDIUM, OBJECT_SIZE_SMALL, GC_NUM_HIGH, ITERATIONS_MEDIUM), + new TestCase(WASTE_PCT_MEDIUM, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, ITERATIONS_SMALL), + new TestCase(WASTE_PCT_MEDIUM, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, ITERATIONS_HIGH), + new TestCase(WASTE_PCT_HIGH, OBJECT_SIZE_SMALL, GC_NUM_HIGH, ITERATIONS_MEDIUM), + new TestCase(WASTE_PCT_HIGH, OBJECT_SIZE_MEDIUM, GC_NUM_SMALL, ITERATIONS_SMALL), + new TestCase(WASTE_PCT_HIGH, OBJECT_SIZE_HIGH, GC_NUM_MEDIUM, ITERATIONS_HIGH) + }; + + public static void main(String[] args) throws Throwable { + for (TestCase testCase : TEST_CASES) { + testCase.print(System.out); + List options = PLABUtils.prepareOptions(testCase.toOptions()); + options.add(AppPLABResize.class.getName()); + OutputAnalyzer out = ProcessTools.executeTestJvm(options.toArray(new String[options.size()])); + if (out.getExitValue() != 0) { + System.out.println(out.getOutput()); + throw new RuntimeException("Exit code is not 0"); + } + checkResults(out.getOutput(), testCase); + } + } + + /** + * Checks testing results. + * Expected results - desired PLAB size is decreased and increased during promotion to Survivor. + * + * @param output - VM output + * @param testCase + */ + private static void checkResults(String output, TestCase testCase) { + final LogParser log = new LogParser(output); + final Map>> entries = log.getEntries(); + + final ArrayList plabSizes = entries.entrySet() + .stream() + .map(item -> { + return item.getValue() + .get(LogParser.ReportType.SURVIVOR_STATS) + .get("desired_plab_sz"); + }) + .collect(Collectors.toCollection(ArrayList::new)); + + // Check that desired plab size was changed during iterations. + // It should decrease during first half of iterations + // and increase after. + List decreasedPlabs = plabSizes.subList(testCase.getIterations(), testCase.getIterations() * 2); + List increasedPlabs = plabSizes.subList(testCase.getIterations() * 2, testCase.getIterations() * 3); + + Long prev = decreasedPlabs.get(0); + for (int index = 1; index < decreasedPlabs.size(); ++index) { + Long current = decreasedPlabs.get(index); + if (prev < current) { + System.out.println(output); + throw new RuntimeException("Test failed! Expect that previous PLAB size should be greater than current. Prev.size: " + prev + " Current size:" + current); + } + prev = current; + } + + prev = increasedPlabs.get(0); + for (int index = 1; index < increasedPlabs.size(); ++index) { + Long current = increasedPlabs.get(index); + if (prev > current) { + System.out.println(output); + throw new RuntimeException("Test failed! Expect that previous PLAB size should be less than current. Prev.size: " + prev + " Current size:" + current); + } + prev = current; + } + + System.out.println("Test passed!"); + } + + /** + * Description of one test case. + */ + private static class TestCase { + + private final int wastePct; + private final int chunkSize; + private final int parGCThreads; + private final int iterations; + + /** + * @param wastePct + * ParallelGCBufferWastePct + * @param chunkSize + * requested object size for memory consumption + * @param parGCThreads + * -XX:ParallelGCThreads + * @param iterations + * + */ + public TestCase(int wastePct, + int chunkSize, + int parGCThreads, + int iterations + ) { + if (wastePct == 0 || chunkSize == 0 || parGCThreads == 0 || iterations == 0) { + throw new IllegalArgumentException("Parameters should not be 0"); + } + this.wastePct = wastePct; + + this.chunkSize = chunkSize; + this.parGCThreads = parGCThreads; + this.iterations = iterations; + } + + /** + * Convert current TestCase to List of options. + * + * @return + * List of options + */ + public List toOptions() { + return Arrays.asList("-XX:ParallelGCThreads=" + parGCThreads, + "-XX:ParallelGCBufferWastePct=" + wastePct, + "-XX:+ResizePLAB", + "-Dthreads=" + parGCThreads, + "-Dchunk.size=" + chunkSize, + "-Diterations=" + iterations, + "-XX:NewSize=16m", + "-XX:MaxNewSize=16m" + ); + } + + /** + * Print details about test case. + */ + public void print(PrintStream out) { + out.println("Test case details:"); + out.println(" Parallel GC buffer waste pct : " + wastePct); + out.println(" Chunk size : " + chunkSize); + out.println(" Parallel GC threads : " + parGCThreads); + out.println(" Iterations: " + iterations); + } + + /** + * @return iterations + */ + public int getIterations() { + return iterations; + } + } +} diff -r fb46176bb4c6 -r 6e62be7db2c8 hotspot/test/gc/g1/plab/lib/AppPLABPromotion.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/g1/plab/lib/AppPLABPromotion.java Wed Jan 27 18:22:25 2016 +0300 @@ -0,0 +1,85 @@ +/* + * 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 sun.hotspot.WhiteBox; + +/** + * This application is part of PLAB promotion test. + * The application fills a part of young gen with a number of small objects. + * Then it calls young GC twice to promote objects from eden to survivor, and from survivor to old. + * The test which running the application is responsible to set up test parameters + * and VM flags including flags turning GC logging on. The test will then check the produced log. + */ +final public class AppPLABPromotion { + + private final static WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + + /** + * AppPLABPromotion is used for testing PLAB promotion. + * Expects the following properties to be set: + * - chunk.size - size of one object (byte array) + * - mem.to.fill - amount of memory to be consumed + * - reachable - memory should be consumed by live or dead objects + * + * @param args + */ + public static void main(String[] args) { + long chunkSize = Long.getLong("chunk.size"); + long memToFill = Long.getLong("mem.to.fill"); + boolean reachable = Boolean.getBoolean("reachable"); + if (chunkSize == 0) { + throw new IllegalArgumentException("Chunk size must be not 0"); + } + if (memToFill <= 0) { + throw new IllegalArgumentException("mem.to.fill property should be above 0"); + } + // Fill requested amount of memory + allocate(reachable, memToFill, chunkSize); + // Promote all allocated objects from Eden to Survivor + WHITE_BOX.youngGC(); + // Promote all allocated objects from Survivor to Old + WHITE_BOX.youngGC(); + } + + /** + * + * @param reachable - should allocate reachable object + * @param memSize - Memory to fill + * @param chunkSize - requested bytes per objects. + * Actual size of bytes per object will be greater + */ + private static void allocate(boolean reachable, long memSize, long chunkSize) { + long realSize = WHITE_BOX.getObjectSize(new byte[(int) chunkSize]); + int items = (int) ((memSize - 1) / (realSize)) + 1; + MemoryConsumer storage; + if (reachable) { + storage = new MemoryConsumer(items, (int) chunkSize); + } else { + storage = new MemoryConsumer(1, (int) chunkSize); + } + // Make all young gen available. + WHITE_BOX.fullGC(); + storage.consume(items * chunkSize); + } +} diff -r fb46176bb4c6 -r 6e62be7db2c8 hotspot/test/gc/g1/plab/lib/AppPLABResize.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/g1/plab/lib/AppPLABResize.java Wed Jan 27 18:22:25 2016 +0300 @@ -0,0 +1,106 @@ +/* + * 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 jdk.test.lib.Platform; +import sun.hotspot.WhiteBox; + +/** + * This application is part of PLAB Resize test. + * The application allocates objects in 3 iterations: + * 1. Objects of fixed size + * 2. Objects of decreasing size + * 3. Objects of increasing size + * The application doesn't have any assumptions about expected behavior. + * It's supposed to be executed by a test which should set up test parameters (object sizes, number of allocations, etc) + * and VM flags including flags turning GC logging on. The test will then check the produced log. + * + * Expects the following properties to be set: + * - iterations - amount of iteration per cycle. + * - chunk.size - size of objects to be allocated + * - threads - number of gc threads (-XX:ParallelGCThreads) to calculate PLAB sizes. + */ +final public class AppPLABResize { + + // Memory to be promoted by PLAB for one thread. + private static final long MEM_ALLOC_WORDS = 32768; + // Defined by properties. + private static final int ITERATIONS = Integer.getInteger("iterations"); + private static final long CHUNK = Long.getLong("chunk.size"); + private static final int GC_THREADS = Integer.getInteger("threads"); + + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + + /** + * Main method for AppPLABResizing. Application expect for next properties: + * iterations, chunk.size and threads. + * + * @param args + */ + public static void main(String[] args) { + + if (ITERATIONS == 0 || CHUNK == 0 || GC_THREADS == 0) { + throw new IllegalArgumentException("Properties should be set"); + } + + long wordSize = Platform.is32bit() ? 4l : 8l; + // PLAB size is shared between threads. + long initialMemorySize = wordSize * GC_THREADS * MEM_ALLOC_WORDS; + + // Expect changing memory to half during all iterations. + long memChangeStep = initialMemorySize / 2 / ITERATIONS; + + WHITE_BOX.fullGC(); + + // Warm the PLAB. Fill memory ITERATIONS times without changing memory size. + iterateAllocation(initialMemorySize, 0); + // Fill memory ITERATIONS times. + // Initial size is initialMemorySize and step is -memChangeStep + iterateAllocation(initialMemorySize, -memChangeStep); + // Fill memory ITERATIONS times. + // Initial size is memoryAfterChanging, step is memChangeStep. + // Memory size at start should be greater then last size on previous step. + // Last size on previous step is initialMemorySize - memChangeStep*(ITERATIONS - 1) + long memoryAfterChanging = initialMemorySize - memChangeStep * (ITERATIONS - 2); + iterateAllocation(memoryAfterChanging, memChangeStep); + } + + private static void iterateAllocation(long memoryToFill, long change) { + int items; + if (change > 0) { + items = (int) ((memoryToFill + change * ITERATIONS) / CHUNK) + 1; + } else { + items = (int) (memoryToFill / CHUNK) + 1; + } + + long currentMemToFill = memoryToFill; + for (int iteration = 0; iteration < ITERATIONS; ++iteration) { + MemoryConsumer storage = new MemoryConsumer(items, (int) CHUNK); + storage.consume(currentMemToFill); + // Promote all objects to survivor + WHITE_BOX.youngGC(); + storage.clear(); + currentMemToFill += change; + } + } +} diff -r fb46176bb4c6 -r 6e62be7db2c8 hotspot/test/gc/g1/plab/lib/LogParser.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/g1/plab/lib/LogParser.java Wed Jan 27 18:22:25 2016 +0300 @@ -0,0 +1,142 @@ +/* + * 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.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * LogParser class parses VM output to get PLAB and ConsumptionStats values. + * + * Typical GC log with PLAB statistics (options - -Xlog:gc=debug,gc+plab=debug) looks like: + * + * [2,244s][info ][gc ] GC(30) Concurrent Mark abort + * [2,245s][debug ][gc,plab] GC(33) (allocated = 1 wasted = 0 unused = 0 used = 1 undo_waste = 0 region_end_waste = 0 regions filled = 0 direct_allocated = 0 failure_used = 0 failure_waste = 0) (plab_sz = 0 desired_plab_sz = 258) + * [2,245s][debug ][gc,plab] GC(33) (allocated = 1 wasted = 0 unused = 0 used = 1 undo_waste = 0 region_end_waste = 0 regions filled = 0 direct_allocated = 0 failure_used = 0 failure_waste = 0) (plab_sz = 0 desired_plab_sz = 258) + * [2,245s][info ][gc ] GC(33) Pause Young (G1 Evacuation Pause) 127M->127M(128M) (2,244s, 2,245s) 0,899ms + * [2,246s][debug ][gc,plab] GC(34) (allocated = 1 wasted = 0 unused = 0 used = 1 undo_waste = 0 region_end_waste = 0 regions filled = 0 direct_allocated = 0 failure_used = 0 failure_waste = 0) (plab_sz = 0 desired_plab_sz = 258) + * [2,246s][debug ][gc,plab] GC(34) (allocated = 1 wasted = 0 unused = 0 used = 1 undo_waste = 0 region_end_waste = 0 regions filled = 0 direct_allocated = 0 failure_used = 0 failure_waste = 0) (plab_sz = 0 desired_plab_sz = 258) + * [2,246s][info ][gc ] GC(34) Pause Initial Mark (G1 Evacuation Pause) 127M->127M(128M) (2,245s, 2,246s) 0,907ms + + */ +final public class LogParser { + + // Name for GC ID field in report. + public final static String GC_ID = "gc_id"; + + /** + * Type of parsed log element. + */ + public static enum ReportType { + + SURVIVOR_STATS, + OLD_STATS + } + + private final String log; + + private final Map>> reportHolder; + + // GC ID + private static final Pattern GC_ID_PATTERN = Pattern.compile("\\[gc,plab\\s*\\] GC\\((\\d+)\\)"); + // Pattern for extraction pair = + private static final Pattern PAIRS_PATTERN = Pattern.compile("\\w+\\s+=\\s+\\d+"); + + /** + * Construct LogParser Object + * + * @param log - VM Output + */ + public LogParser(String log) { + if (log == null) { + throw new IllegalArgumentException("Parameter log should not be null."); + } + this.log = log; + reportHolder = parseLines(); + } + + /** + * @return log which is being processed + */ + public String getLog() { + return log; + } + + /** + * Returns list of log entries. + * + * @return list of Pair with ReportType and Map of parameters/values. + */ + public Map>> getEntries() { + return reportHolder; + } + + private Map>> parseLines() throws NumberFormatException { + Scanner lineScanner = new Scanner(log); + Map>> allocationStatistics = new HashMap<>(); + Optional gc_id; + while (lineScanner.hasNextLine()) { + String line = lineScanner.nextLine(); + gc_id = getGcId(line); + if ( gc_id.isPresent() ) { + Matcher matcher = PAIRS_PATTERN.matcher(line); + if (matcher.find()) { + Map> oneReportItem; + ReportType reportType; + // Second line in log is statistics for Old PLAB allocation + if ( !allocationStatistics.containsKey(gc_id.get()) ) { + oneReportItem = new EnumMap<>(ReportType.class); + reportType = ReportType.SURVIVOR_STATS; + allocationStatistics.put(gc_id.get(), oneReportItem); + } else { + oneReportItem = allocationStatistics.get(gc_id.get()); + reportType = ReportType.OLD_STATS; + } + + // Extract all pairs from log. + HashMap plabStats = new HashMap<>(); + do { + String pair = matcher.group(); + String[] nameValue = pair.replaceAll(" ", "").split("="); + plabStats.put(nameValue[0], Long.parseLong(nameValue[1])); + } while (matcher.find()); + oneReportItem.put(reportType,plabStats); + } + } + } + return allocationStatistics; + } + + private Optional getGcId(String line) { + Matcher number = GC_ID_PATTERN.matcher(line); + if (number.find()) { + return Optional.of(Long.parseLong(number.group(1))); + } + return Optional.empty(); + } +} diff -r fb46176bb4c6 -r 6e62be7db2c8 hotspot/test/gc/g1/plab/lib/MemoryConsumer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/g1/plab/lib/MemoryConsumer.java Wed Jan 27 18:22:25 2016 +0300 @@ -0,0 +1,86 @@ +/* + * 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; + +/** + * The MemoryConsumer is used for consuming different amount of memory. + * Class will store not more than 'capacity' number of objects with 'chunk' size. + * If we exceed capacity, object will be stored at existing entries, + * all previously added objects will be overwritten. + * If capacity=1, only last object will be saved. + */ +public class MemoryConsumer { + + private int capacity; + private int chunk; + + private Object[] array; + private int index; + + /** + * Create MemoryConsumer object with defined capacity + * + * @param capacity + * @param chunk + */ + public MemoryConsumer(int capacity, int chunk) { + if (capacity <= 0) { + throw new IllegalArgumentException("Items number should be greater than 0."); + } + if (chunk <= 0) { + throw new IllegalArgumentException("Chunk size should be greater than 0."); + } + this.capacity = capacity; + this.chunk = chunk; + index = 0; + array = new Object[this.capacity]; + } + + /** + * Store object into MemoryConsumer. + * + * @param o - Object to store + */ + private void store(Object o) { + if (array == null) { + throw new RuntimeException("Capacity should be set before storing"); + } + array[index % capacity] = o; + ++index; + } + + public void consume(long memoryToFill) { + long allocated = 0; + while (allocated < memoryToFill) { + store(new byte[chunk]); + allocated += chunk; + } + } + + /** + * Clear all stored objects. + */ + public void clear() { + array = null; + } +} diff -r fb46176bb4c6 -r 6e62be7db2c8 hotspot/test/gc/g1/plab/lib/PLABUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/g1/plab/lib/PLABUtils.java Wed Jan 27 18:22:25 2016 +0300 @@ -0,0 +1,83 @@ +/* + * 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.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import jdk.test.lib.Utils; + +/** + * Utilities for PLAB testing. + */ +public class PLABUtils { + + /** + * PLAB tests default options list + */ + private final static String[] GC_TUNE_OPTIONS = { + "-XX:+UseG1GC", + "-XX:G1HeapRegionSize=1m", + "-XX:OldSize=64m", + "-XX:-UseAdaptiveSizePolicy", + "-XX:-UseTLAB", + "-XX:SurvivorRatio=1" + }; + + /** + * GC logging options list. + */ + private final static String G1_PLAB_LOGGING_OPTIONS[] = { + "-Xlog:gc=debug,gc+plab=debug" + }; + + /** + * List of options required to use WhiteBox. + */ + private final static String WB_DIAGNOSTIC_OPTIONS[] = { + "-Xbootclasspath/a:.", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI" + }; + + /** + * Prepares options for testing. + * + * @param options - additional options for testing + * @return List of options + */ + public static List prepareOptions(List options) { + if (options == null) { + throw new IllegalArgumentException("Options cannot be null"); + } + List executionOtions = new ArrayList<>( + Arrays.asList(Utils.getTestJavaOpts()) + ); + Collections.addAll(executionOtions, WB_DIAGNOSTIC_OPTIONS); + Collections.addAll(executionOtions, G1_PLAB_LOGGING_OPTIONS); + Collections.addAll(executionOtions, GC_TUNE_OPTIONS); + executionOtions.addAll(options); + return executionOtions; + } +}