1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 /* 26 * @test 27 * @summary Test JVM's CPU resource awareness when running inside docker container 28 * @library /testlibrary 29 * @run driver TestCPUAwareness 30 */ 31 32 import java.util.List; 33 import com.oracle.java.testlibrary.Common; 34 import com.oracle.java.testlibrary.DockerTestUtils; 35 import com.oracle.java.testlibrary.DockerRunOptions; 36 37 public class TestCPUAwareness { 38 private static final String imageName = Common.imageName("cpu"); 39 private static final int availableCPUs = Runtime.getRuntime().availableProcessors(); 40 41 public static void main(String[] args) throws Exception { 42 if (!DockerTestUtils.canTestDocker()) { 43 return; 44 } 45 46 System.out.println("Test Environment: detected availableCPUs = " + availableCPUs); 47 DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker"); 48 49 try { 50 // cpuset, period, shares, expected Active Processor Count 51 testComboWithCpuSets(); 52 53 // cpu shares - it should be safe to use CPU shares exceeding available CPUs 54 testCpuShares(256, 1); 55 testCpuShares(2048, 2); 56 testCpuShares(4096, 4); 57 58 // leave one CPU for system and tools, otherwise this test may be unstable 59 int maxNrOfAvailableCpus = availableCPUs - 1; 60 for (int i=1; i < maxNrOfAvailableCpus; i = i * 2) { 61 testCpus(i, i); 62 } 63 64 // If ActiveProcessorCount is set, the VM should use it, regardless of other 65 // container settings, host settings or available CPUs on the host. 66 testActiveProcessorCount(1, 1); 67 testActiveProcessorCount(2, 2); 68 69 // cpu quota and period 70 testCpuQuotaAndPeriod(50*1000, 100*1000); 71 testCpuQuotaAndPeriod(100*1000, 100*1000); 72 testCpuQuotaAndPeriod(150*1000, 100*1000); 73 testCpuQuotaAndPeriod(400*1000, 100*1000); 74 75 } finally { 76 DockerTestUtils.removeDockerImage(imageName); 77 } 78 } 79 80 81 private static void testComboWithCpuSets() throws Exception { 82 String cpuSetStr = CPUSetsReader.readFromProcStatus("Cpus_allowed_list"); 83 System.out.println("cpuSetStr = " + cpuSetStr); 84 85 if (cpuSetStr == null) { 86 System.out.printf("The cpuset test cases are skipped"); 87 } else { 88 List<Integer> cpuSet = CPUSetsReader.parseCpuSet(cpuSetStr); 89 90 // Test subset of cpuset with one element 91 if (cpuSet.size() >= 1) { 92 String testCpuSet = CPUSetsReader.listToString(cpuSet, 1); 93 testAPCCombo(testCpuSet, 200*1000, 100*1000, 4*1024, true, 1); 94 } 95 96 // Test subset of cpuset with two elements 97 if (cpuSet.size() >= 2) { 98 String testCpuSet = CPUSetsReader.listToString(cpuSet, 2); 99 testAPCCombo(testCpuSet, 200*1000, 100*1000, 4*1024, true, 2); 100 testAPCCombo(testCpuSet, 200*1000, 100*1000, 1023, true, 2); 101 testAPCCombo(testCpuSet, 200*1000, 100*1000, 1023, false, 1); 102 } 103 104 // Test subset of cpuset with three elements 105 if (cpuSet.size() >= 3) { 106 String testCpuSet = CPUSetsReader.listToString(cpuSet, 3); 107 testAPCCombo(testCpuSet, 100*1000, 100*1000, 2*1024, true, 1); 108 testAPCCombo(testCpuSet, 200*1000, 100*1000, 1023, true, 2); 109 testAPCCombo(testCpuSet, 200*1000, 100*1000, 1023, false, 1); 110 } 111 } 112 } 113 114 115 private static void testActiveProcessorCount(int valueToSet, int expectedValue) throws Exception { 116 Common.logNewTestCase("Test ActiveProcessorCount: valueToSet = " + valueToSet); 117 118 DockerRunOptions opts = Common.newOpts(imageName) 119 .addJavaOpts("-XX:ActiveProcessorCount=" + valueToSet, "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintActiveCpus"); 120 Common.run(opts) 121 .shouldMatch("active processor count set by user.*" + expectedValue); 122 } 123 124 125 private static void testCpus(int valueToSet, int expectedTraceValue) throws Exception { 126 Common.logNewTestCase("test cpus: " + valueToSet); 127 DockerRunOptions opts = Common.newOpts(imageName) 128 .addDockerOpts("--cpus", "" + valueToSet); 129 Common.run(opts) 130 .shouldMatch("active_processor_count.*" + expectedTraceValue); 131 } 132 133 134 // Expected active processor count can not exceed available CPU count 135 private static int adjustExpectedAPCForAvailableCPUs(int expectedAPC) { 136 if (expectedAPC > availableCPUs) { 137 expectedAPC = availableCPUs; 138 System.out.println("Adjusted expectedAPC = " + expectedAPC); 139 } 140 return expectedAPC; 141 } 142 143 144 private static void testCpuQuotaAndPeriod(int quota, int period) 145 throws Exception { 146 Common.logNewTestCase("test cpu quota and period: "); 147 System.out.println("quota = " + quota); 148 System.out.println("period = " + period); 149 150 int expectedAPC = (int) Math.ceil((float) quota / (float) period); 151 System.out.println("expectedAPC = " + expectedAPC); 152 expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC); 153 154 DockerRunOptions opts = Common.newOpts(imageName) 155 .addDockerOpts("--cpu-period=" + period) 156 .addDockerOpts("--cpu-quota=" + quota); 157 158 Common.run(opts) 159 .shouldMatch("CPU Period is.*" + period) 160 .shouldMatch("CPU Quota is.*" + quota) 161 .shouldMatch("active_processor_count.*" + expectedAPC); 162 } 163 164 165 // Test correctess of automatically selected active processor cound 166 private static void testAPCCombo(String cpuset, int quota, int period, int shares, 167 boolean usePreferContainerQuotaForCPUCount, 168 int expectedAPC) throws Exception { 169 Common.logNewTestCase("test APC Combo"); 170 System.out.println("cpuset = " + cpuset); 171 System.out.println("quota = " + quota); 172 System.out.println("period = " + period); 173 System.out.println("shares = " + period); 174 System.out.println("usePreferContainerQuotaForCPUCount = " + usePreferContainerQuotaForCPUCount); 175 System.out.println("expectedAPC = " + expectedAPC); 176 177 expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC); 178 179 DockerRunOptions opts = Common.newOpts(imageName) 180 .addDockerOpts("--cpuset-cpus", "" + cpuset) 181 .addDockerOpts("--cpu-period=" + period) 182 .addDockerOpts("--cpu-quota=" + quota) 183 .addDockerOpts("--cpu-shares=" + shares); 184 185 if (!usePreferContainerQuotaForCPUCount) opts.addJavaOpts("-XX:-PreferContainerQuotaForCPUCount"); 186 187 Common.run(opts) 188 .shouldMatch("active_processor_count.*" + expectedAPC); 189 } 190 191 192 private static void testCpuShares(int shares, int expectedAPC) throws Exception { 193 Common.logNewTestCase("test cpu shares, shares = " + shares); 194 System.out.println("expectedAPC = " + expectedAPC); 195 196 expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC); 197 198 DockerRunOptions opts = Common.newOpts(imageName) 199 .addDockerOpts("--cpu-shares=" + shares); 200 Common.run(opts) 201 .shouldMatch("CPU Shares is.*" + shares) 202 .shouldMatch("active_processor_count.*" + expectedAPC); 203 } 204 }