Skip to content

Commit cf8cfec

Browse files
committed
8333446: Add tests for hierarchical container support
Reviewed-by: mbaesken Backport-of: d9fdf69c34c20e0f2d526c2f04450acb904c3e80
1 parent bb0d4c1 commit cf8cfec

File tree

9 files changed

+655
-7
lines changed

9 files changed

+655
-7
lines changed

src/hotspot/share/prims/whitebox.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2418,6 +2418,13 @@ WB_ENTRY(jint, WB_ValidateCgroup(JNIEnv* env,
24182418
return ret;
24192419
WB_END
24202420

2421+
// Available cpus of the host machine, Linux only.
2422+
// Used in container testing.
2423+
WB_ENTRY(jint, WB_HostCPUs(JNIEnv* env, jobject o))
2424+
LINUX_ONLY(return os::Linux::active_processor_count();)
2425+
return -1; // Not used/implemented on other platforms
2426+
WB_END
2427+
24212428
WB_ENTRY(void, WB_PrintOsInfo(JNIEnv* env, jobject o))
24222429
os::print_os_info(tty);
24232430
WB_END
@@ -2824,6 +2831,7 @@ static JNINativeMethod methods[] = {
28242831
(void*)&WB_ValidateCgroup },
28252832
{CC"hostPhysicalMemory", CC"()J", (void*)&WB_HostPhysicalMemory },
28262833
{CC"hostPhysicalSwap", CC"()J", (void*)&WB_HostPhysicalSwap },
2834+
{CC"hostCPUs", CC"()I", (void*)&WB_HostCPUs },
28272835
{CC"printOsInfo", CC"()V", (void*)&WB_PrintOsInfo },
28282836
{CC"disableElfSectionCache", CC"()V", (void*)&WB_DisableElfSectionCache },
28292837
{CC"resolvedMethodItemsCount", CC"()J", (void*)&WB_ResolvedMethodItemsCount },

test/hotspot/jtreg/TEST.ROOT

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ requires.properties= \
8585
vm.musl \
8686
vm.flagless \
8787
docker.support \
88+
systemd.support \
8889
jdk.containerized
8990

9091
# Minimum jtreg version
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2024, Red Hat, Inc.
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+
public class HelloSystemd {
25+
public static void main(String args[]) {
26+
System.out.println("Hello Systemd");
27+
}
28+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright (c) 2024, Red Hat, Inc.
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+
import jdk.test.lib.containers.systemd.SystemdRunOptions;
25+
import jdk.test.lib.containers.systemd.SystemdTestUtils;
26+
import jdk.test.lib.process.OutputAnalyzer;
27+
import jdk.test.whitebox.WhiteBox;
28+
import jtreg.SkippedException;
29+
30+
/*
31+
* @test
32+
* @bug 8322420 8217338
33+
* @summary Memory/CPU awareness test for JDK-under-test inside a systemd slice.
34+
* @requires systemd.support
35+
* @library /test/lib
36+
* @modules java.base/jdk.internal.platform
37+
* @build HelloSystemd jdk.test.whitebox.WhiteBox
38+
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar whitebox.jar jdk.test.whitebox.WhiteBox
39+
* @run main/othervm -Xbootclasspath/a:whitebox.jar -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI SystemdMemoryAwarenessTest
40+
*/
41+
public class SystemdMemoryAwarenessTest {
42+
43+
private static final int MB = 1024 * 1024;
44+
private static final WhiteBox wb = WhiteBox.getWhiteBox();
45+
private static final String TEST_SLICE_NAME = SystemdMemoryAwarenessTest.class.getSimpleName() + "HS";
46+
47+
public static void main(String[] args) throws Exception {
48+
testHelloSystemd();
49+
}
50+
51+
private static void testHelloSystemd() throws Exception {
52+
SystemdRunOptions opts = SystemdTestUtils.newOpts("HelloSystemd");
53+
// 1 GB memory, but the limit in the lower hierarchy is 512M
54+
opts.memoryLimit("1024M");
55+
int expectedMemLimit = 512;
56+
// expected detected limit we test for, 512MB
57+
opts.sliceDMemoryLimit(String.format("%dM", expectedMemLimit));
58+
int physicalCpus = wb.hostCPUs();
59+
if (physicalCpus < 2) {
60+
System.err.println("WARNING: host system only has " + physicalCpus + " cpus. Expected >= 2");
61+
System.err.println("The active_processor_count assertion will trivially pass.");
62+
}
63+
// Use a CPU core limit of 1 for best coverage
64+
int coreLimit = 1;
65+
System.out.println("DEBUG: Running test with a CPU limit of " + coreLimit);
66+
opts.cpuLimit(String.format("%d%%", coreLimit * 100));
67+
opts.sliceName(TEST_SLICE_NAME);
68+
69+
OutputAnalyzer out = SystemdTestUtils.buildAndRunSystemdJava(opts);
70+
out.shouldHaveExitValue(0)
71+
.shouldContain("Hello Systemd")
72+
.shouldContain(String.format("Memory Limit is: %d", (expectedMemLimit * MB)));
73+
try {
74+
out.shouldContain("OSContainer::active_processor_count: " + coreLimit);
75+
} catch (RuntimeException e) {
76+
// CPU delegation needs to be enabled when run as user on cg v2
77+
if (SystemdTestUtils.RUN_AS_USER) {
78+
String hint = "When run as user on cg v2 cpu delegation needs to be configured!";
79+
throw new SkippedException(hint);
80+
}
81+
throw e;
82+
}
83+
}
84+
85+
}

test/jdk/TEST.ROOT

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ requires.properties= \
100100
vm.jvmci.enabled \
101101
vm.jvmti \
102102
docker.support \
103+
systemd.support \
103104
release.implementor \
104105
jdk.containerized \
105106
jdk.foreign.linker

test/jtreg-ext/requires/VMProps.java

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ public Map<String, String> call() {
129129
map.put("vm.compiler1.enabled", this::isCompiler1Enabled);
130130
map.put("vm.compiler2.enabled", this::isCompiler2Enabled);
131131
map.put("docker.support", this::dockerSupport);
132+
map.put("systemd.support", this::systemdSupport);
132133
map.put("vm.musl", this::isMusl);
133134
map.put("release.implementor", this::implementor);
134135
map.put("jdk.containerized", this::jdkContainerized);
@@ -561,7 +562,7 @@ protected String dockerSupport() {
561562

562563
if (isSupported) {
563564
try {
564-
isSupported = checkDockerSupport();
565+
isSupported = checkProgramSupport("checkDockerSupport()", Container.ENGINE_COMMAND);
565566
} catch (Exception e) {
566567
isSupported = false;
567568
}
@@ -571,6 +572,27 @@ protected String dockerSupport() {
571572
return "" + isSupported;
572573
}
573574

575+
/**
576+
* A simple check for systemd support
577+
*
578+
* @return true if systemd is supported in a given environment
579+
*/
580+
protected String systemdSupport() {
581+
log("Entering systemdSupport()");
582+
583+
boolean isSupported = Platform.isLinux();
584+
if (isSupported) {
585+
try {
586+
isSupported = checkProgramSupport("checkSystemdSupport()", "systemd-run");
587+
} catch (Exception e) {
588+
isSupported = false;
589+
}
590+
}
591+
592+
log("systemdSupport(): returning isSupported = " + isSupported);
593+
return "" + isSupported;
594+
}
595+
574596
// Configures process builder to redirect process stdout and stderr to a file.
575597
// Returns file names for stdout and stderr.
576598
private Map<String, String> redirectOutputToLogFile(String msg, ProcessBuilder pb, String fileNameBase) {
@@ -605,17 +627,17 @@ private void printLogfileContent(Map<String, String> logFileNames) {
605627
});
606628
}
607629

608-
private boolean checkDockerSupport() throws IOException, InterruptedException {
609-
log("checkDockerSupport(): entering");
610-
ProcessBuilder pb = new ProcessBuilder("which", Container.ENGINE_COMMAND);
630+
private boolean checkProgramSupport(String logString, String cmd) throws IOException, InterruptedException {
631+
log(logString + ": entering");
632+
ProcessBuilder pb = new ProcessBuilder("which", cmd);
611633
Map<String, String> logFileNames =
612-
redirectOutputToLogFile("checkDockerSupport(): which <container-engine>",
613-
pb, "which-container");
634+
redirectOutputToLogFile(logString + ": which " + cmd,
635+
pb, "which-cmd");
614636
Process p = pb.start();
615637
p.waitFor(10, TimeUnit.SECONDS);
616638
int exitValue = p.exitValue();
617639

618-
log(String.format("checkDockerSupport(): exitValue = %s, pid = %s", exitValue, p.pid()));
640+
log(String.format("%s: exitValue = %s, pid = %s", logString, exitValue, p.pid()));
619641
if (exitValue != 0) {
620642
printLogfileContent(logFileNames);
621643
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
* Copyright (c) 2024, Red Hat, Inc.
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+
package jdk.test.lib.containers.systemd;
25+
26+
import static jdk.test.lib.Asserts.assertNotNull;
27+
28+
import java.util.ArrayList;
29+
import java.util.Collections;
30+
31+
32+
// This class represents options for running java inside systemd slices
33+
// in test environment.
34+
public class SystemdRunOptions {
35+
public ArrayList<String> javaOpts = new ArrayList<>();
36+
public String classToRun; // class or "-version"
37+
public ArrayList<String> classParams = new ArrayList<>();
38+
public String memoryLimit; // used in slice for MemoryLimit property
39+
public String cpuLimit; // used in slice for CPUQuota property
40+
public String sliceName; // name of the slice (nests CPU in memory)
41+
public String sliceDMemoryLimit; // used in jdk_internal.slice.d
42+
public String sliceDCpuLimit; // used in jdk_internal.slice.d
43+
44+
/**
45+
* Convenience constructor for most common use cases in testing.
46+
* @param classToRun a class to run, or "-version"
47+
* @param javaOpts java options to use
48+
*
49+
* @return Default docker run options
50+
*/
51+
public SystemdRunOptions(String classToRun, String... javaOpts) {
52+
this.classToRun = classToRun;
53+
Collections.addAll(this.javaOpts, javaOpts);
54+
this.sliceName = defaultSliceName();
55+
}
56+
57+
private static String defaultSliceName() {
58+
// Create a unique name for a systemd slice
59+
// jtreg guarantees that test.name is unique among all concurrently executing
60+
// tests. For example, if you have two test roots:
61+
//
62+
// $ find test -type f
63+
// test/foo/TEST.ROOT
64+
// test/foo/my/TestCase.java
65+
// test/bar/TEST.ROOT
66+
// test/bar/my/TestCase.java
67+
// $ jtreg -concur:2 test/foo test/bar
68+
//
69+
// jtreg will first run all the tests under test/foo. When they are all finished, then
70+
// jtreg will run all the tests under test/bar. So you will never have two concurrent
71+
// test cases whose test.name is "my/TestCase.java"
72+
String testname = System.getProperty("test.name");
73+
assertNotNull(testname, "must be set by jtreg");
74+
testname = testname.replace(".java", "");
75+
testname = testname.replace("/", "_");
76+
testname = testname.replace("\\", "_");
77+
testname = testname.replace("-", "_");
78+
79+
// Example:
80+
// Memory: "test_containers_systemd_TestMemoryAwareness"
81+
// CPU: "test_containers_systemd_TestMemoryAwareness-cpu" => derived
82+
return testname;
83+
}
84+
85+
/**
86+
* The memory limit set with a .slice file in the systemd
87+
* config directory.
88+
*
89+
* @param memLimit The memory limit to set (e.g. 1000M).
90+
* @return The run options.
91+
*/
92+
public SystemdRunOptions memoryLimit(String memLimit) {
93+
this.memoryLimit = memLimit;
94+
return this;
95+
}
96+
97+
/**
98+
* The memory limit to set in the top-level jdk_internal.slice.d
99+
* systemd config directory.
100+
*
101+
* @param memoryLimit The memory limit to set.
102+
* @return The run options.
103+
*/
104+
public SystemdRunOptions sliceDMemoryLimit(String memoryLimit) {
105+
this.sliceDMemoryLimit = memoryLimit;
106+
return this;
107+
}
108+
109+
/**
110+
* The CPU limit set with a .slice file in the systemd
111+
* config directory.
112+
*
113+
* @param cpuLimit
114+
* @return The run options.
115+
*/
116+
public SystemdRunOptions cpuLimit(String cpuLimit) {
117+
this.cpuLimit = cpuLimit;
118+
return this;
119+
}
120+
121+
/**
122+
* The Cpu limit set in the top-level jdk_internal.slice.d
123+
* systemd config directory.
124+
*
125+
* @param cpuLimit The CPU limit to set to.
126+
* @return The run options.
127+
*/
128+
public SystemdRunOptions sliceDCpuLimit(String cpuLimit) {
129+
this.sliceDCpuLimit = cpuLimit;
130+
return this;
131+
}
132+
133+
public SystemdRunOptions sliceName(String name) {
134+
this.sliceName = name;
135+
return this;
136+
}
137+
138+
public SystemdRunOptions addJavaOpts(String... opts) {
139+
Collections.addAll(javaOpts, opts);
140+
return this;
141+
}
142+
143+
public SystemdRunOptions addClassOptions(String... opts) {
144+
Collections.addAll(classParams,opts);
145+
return this;
146+
}
147+
148+
public boolean hasSliceDLimit() {
149+
return this.sliceDMemoryLimit != null ||
150+
this.sliceDCpuLimit != null;
151+
}
152+
}

0 commit comments

Comments
 (0)