1 /*
   2  * Copyright (c) 2017, 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  * @requires docker.support
  29  * @library /test/lib
  30  * @modules java.base/jdk.internal.misc
  31  *          java.management
  32  *          jdk.jartool/sun.tools.jar
  33  * @build Common
  34  * @run driver TestCPUAwareness
  35  */
  36 import jdk.test.lib.containers.docker.DockerRunOptions;
  37 import jdk.test.lib.containers.docker.DockerTestUtils;
  38 
  39 
  40 public class TestCPUAwareness {
  41     private static final String imageName = Common.imageName("cpu");
  42 
  43     public static void main(String[] args) throws Exception {
  44         if (!DockerTestUtils.canTestDocker()) {
  45             return;
  46         }
  47 
  48         int availableCPUs = Runtime.getRuntime().availableProcessors();
  49         System.out.println("Test Environment: detected availableCPUs = " + availableCPUs);
  50         DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker");
  51 
  52         try {
  53             // cpuset, period, shares, expected Active Processor Count
  54             testAPCCombo("0", 200*1000, 100*1000,   4*1024, 1);
  55             testAPCCombo("0,1", 200*1000, 100*1000, 4*1024, 2);
  56             testAPCCombo("0,1", 200*1000, 100*1000, 1*1024, 2);
  57 
  58             testCpuShares(256, 1);
  59             testCpuShares(2048, 2);
  60             testCpuShares(4096, 4);
  61 
  62             // leave one CPU for system and tools, otherwise this test may be unstable
  63             int maxNrOfAvailableCpus =  availableCPUs - 1;
  64             for (int i=1; i < maxNrOfAvailableCpus; i = i * 2) {
  65                 testCpus(i, i);
  66             }
  67 
  68             // If ActiveProcessorCount is set, the VM should use it, regardless of other
  69             // container settings, host settings or available CPUs on the host.
  70             testActiveProcessorCount(1, 1);
  71             testActiveProcessorCount(2, 2);
  72 
  73             testCpuQuotaAndPeriod(50*1000, 100*1000);
  74             testCpuQuotaAndPeriod(100*1000, 100*1000);
  75             testCpuQuotaAndPeriod(150*1000, 100*1000);
  76 
  77         } finally {
  78             DockerTestUtils.removeDockerImage(imageName);
  79         }
  80     }
  81 
  82 
  83     private static void testActiveProcessorCount(int valueToSet, int expectedValue) throws Exception {
  84         Common.logNewTestCase("Test ActiveProcessorCount: valueToSet = " + valueToSet);
  85 
  86         DockerRunOptions opts = Common.newOpts(imageName)
  87             .addJavaOpts("-XX:ActiveProcessorCount=" + valueToSet, "-Xlog:os=trace");
  88         Common.run(opts)
  89             .shouldMatch("active processor count set by user.*" + expectedValue);
  90     }
  91 
  92 
  93     private static void testCpus(int valueToSet, int expectedTraceValue) throws Exception {
  94         Common.logNewTestCase("test cpus: " + valueToSet);
  95         DockerRunOptions opts = Common.newOpts(imageName)
  96             .addDockerOpts("--cpus", "" + valueToSet);
  97         Common.run(opts)
  98             .shouldMatch("active_processor_count.*" + expectedTraceValue);
  99     }
 100 
 101 
 102     private static void testCpuQuotaAndPeriod(int quota, int period)
 103         throws Exception {
 104         Common.logNewTestCase("test cpu quota and period: ");
 105         System.out.println("quota = " + quota);
 106         System.out.println("period = " + period);
 107 
 108         int expectedAPC = (int) Math.ceil((float) quota / (float) period);
 109         System.out.println("expectedAPC = " + expectedAPC);
 110 
 111         DockerRunOptions opts = Common.newOpts(imageName)
 112             .addDockerOpts("--cpu-period=" + period)
 113             .addDockerOpts("--cpu-quota=" + quota);
 114 
 115         Common.run(opts)
 116             .shouldMatch("CPU Period is.*" + period)
 117             .shouldMatch("CPU Quota is.*" + quota)
 118             .shouldMatch("active_processor_count.*" + expectedAPC);
 119     }
 120 
 121 
 122     // Test correctess of automatically selected active processor cound
 123     private static void testAPCCombo(String cpuset, int quota, int period, int shares,
 124                                          int expectedAPC) throws Exception {
 125         Common.logNewTestCase("test APC Combo");
 126         System.out.println("cpuset = " + cpuset);
 127         System.out.println("quota = " + quota);
 128         System.out.println("period = " + period);
 129         System.out.println("shares = " + period);
 130         System.out.println("expectedAPC = " + expectedAPC);
 131 
 132         DockerRunOptions opts = Common.newOpts(imageName)
 133             .addDockerOpts("--cpuset-cpus", "" + cpuset)
 134             .addDockerOpts("--cpu-period=" + period)
 135             .addDockerOpts("--cpu-quota=" + quota)
 136             .addDockerOpts("--cpu-shares=" + shares);
 137         Common.run(opts)
 138             .shouldMatch("active_processor_count.*" + expectedAPC);
 139     }
 140 
 141 
 142     private static void testCpuShares(int shares, int expectedAPC) throws Exception {
 143         Common.logNewTestCase("test cpu shares, shares = " + shares);
 144         DockerRunOptions opts = Common.newOpts(imageName)
 145             .addDockerOpts("--cpu-shares=" + shares);
 146         Common.run(opts)
 147             .shouldMatch("CPU Shares is.*" + shares)
 148             .shouldMatch("active_processor_count.*" + expectedAPC);
 149     }
 150 }