mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-12 03:18:37 +00:00
8313781: Add regression tests for large page logging and user-facing error messages
Reviewed-by: sjohanss, dholmes
This commit is contained in:
parent
9123961aaa
commit
abad0408e8
@ -30,17 +30,46 @@ import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
// This class allows us to parse system hugepage config from
|
||||
// - a) the Operating System (the truth)
|
||||
// - b) the JVM log (-Xlog:pagesize)
|
||||
// This is used e.g. in TestHugePageDetection to determine if the JVM detects the correct settings from the OS.
|
||||
class HugePageConfiguration {
|
||||
|
||||
Set<Long> _staticHugePageSizes;
|
||||
long _staticDefaultHugePageSize;
|
||||
public static class StaticHugePageConfig implements Comparable<StaticHugePageConfig> {
|
||||
public long pageSize = -1;
|
||||
public long nr_hugepages = -1;
|
||||
public long nr_overcommit_hugepages = -1;
|
||||
|
||||
enum THPMode {always, never, madvise, unknown}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(pageSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StaticHugePageConfig{" +
|
||||
"pageSize=" + pageSize +
|
||||
", nr_hugepages=" + nr_hugepages +
|
||||
", nr_overcommit_hugepages=" + nr_overcommit_hugepages +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(StaticHugePageConfig o) {
|
||||
return (int) (pageSize - o.pageSize);
|
||||
}
|
||||
}
|
||||
|
||||
Set<StaticHugePageConfig> _staticHugePageConfigurations;
|
||||
long _staticDefaultHugePageSize = -1;
|
||||
|
||||
enum THPMode {always, never, madvise}
|
||||
THPMode _thpMode;
|
||||
long _thpPageSize;
|
||||
|
||||
public Set<Long> getStaticHugePageSizes() {
|
||||
return _staticHugePageSizes;
|
||||
public Set<StaticHugePageConfig> getStaticHugePageConfigurations() {
|
||||
return _staticHugePageConfigurations;
|
||||
}
|
||||
|
||||
public long getStaticDefaultHugePageSize() {
|
||||
@ -55,8 +84,18 @@ class HugePageConfiguration {
|
||||
return _thpPageSize;
|
||||
}
|
||||
|
||||
public HugePageConfiguration(Set<Long> _staticHugePageSizes, long _staticDefaultHugePageSize, THPMode _thpMode, long _thpPageSize) {
|
||||
this._staticHugePageSizes = _staticHugePageSizes;
|
||||
// Returns true if the THP support is enabled
|
||||
public boolean supportsTHP() {
|
||||
return _thpMode == THPMode.always || _thpMode == THPMode.madvise;
|
||||
}
|
||||
|
||||
// Returns true if static huge pages are supported (whether or not we have configured the pools)
|
||||
public boolean supportsStaticHugePages() {
|
||||
return _staticDefaultHugePageSize > 0 && _staticHugePageConfigurations.size() > 0;
|
||||
}
|
||||
|
||||
public HugePageConfiguration(Set<StaticHugePageConfig> _staticHugePageConfigurations, long _staticDefaultHugePageSize, THPMode _thpMode, long _thpPageSize) {
|
||||
this._staticHugePageConfigurations = _staticHugePageConfigurations;
|
||||
this._staticDefaultHugePageSize = _staticDefaultHugePageSize;
|
||||
this._thpMode = _thpMode;
|
||||
this._thpPageSize = _thpPageSize;
|
||||
@ -65,7 +104,7 @@ class HugePageConfiguration {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Configuration{" +
|
||||
"_staticHugePageSizes=" + _staticHugePageSizes +
|
||||
"_staticHugePageConfigurations=" + _staticHugePageConfigurations +
|
||||
", _staticDefaultHugePageSize=" + _staticDefaultHugePageSize +
|
||||
", _thpMode=" + _thpMode +
|
||||
", _thpPageSize=" + _thpPageSize +
|
||||
@ -77,12 +116,8 @@ class HugePageConfiguration {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
HugePageConfiguration that = (HugePageConfiguration) o;
|
||||
return _staticDefaultHugePageSize == that._staticDefaultHugePageSize && _thpPageSize == that._thpPageSize && Objects.equals(_staticHugePageSizes, that._staticHugePageSizes) && _thpMode == that._thpMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(_staticHugePageSizes, _staticDefaultHugePageSize, _thpMode, _thpPageSize);
|
||||
return _staticDefaultHugePageSize == that._staticDefaultHugePageSize && _thpPageSize == that._thpPageSize &&
|
||||
Objects.equals(_staticHugePageConfigurations, that._staticHugePageConfigurations) && _thpMode == that._thpMode;
|
||||
}
|
||||
|
||||
private static long readDefaultHugePageSizeFromOS() {
|
||||
@ -102,25 +137,36 @@ class HugePageConfiguration {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static Set<Long> readSupportedHugePagesFromOS() {
|
||||
TreeSet<Long> pagesizes = new TreeSet<>();
|
||||
private static Set<StaticHugePageConfig> readSupportedHugePagesFromOS() throws IOException {
|
||||
TreeSet<StaticHugePageConfig> hugePageConfigs = new TreeSet<>();
|
||||
Pattern pat = Pattern.compile("hugepages-(\\d+)kB");
|
||||
File[] subdirs = new File("/sys/kernel/mm/hugepages").listFiles();
|
||||
if (subdirs != null) {
|
||||
for (File f : subdirs) {
|
||||
String name = f.getName();
|
||||
for (File subdir : subdirs) {
|
||||
String name = subdir.getName();
|
||||
Matcher mat = pat.matcher(name);
|
||||
if (mat.matches()) {
|
||||
long pagesize = Long.parseLong(mat.group(1)) * 1024;
|
||||
pagesizes.add(pagesize);
|
||||
StaticHugePageConfig config = new StaticHugePageConfig();
|
||||
config.pageSize = Long.parseLong(mat.group(1)) * 1024;
|
||||
try (FileReader fr = new FileReader(subdir.getAbsolutePath() + "/nr_hugepages");
|
||||
BufferedReader reader = new BufferedReader(fr)) {
|
||||
String s = reader.readLine();
|
||||
config.nr_hugepages = Long.parseLong(s);
|
||||
}
|
||||
try (FileReader fr = new FileReader(subdir.getAbsolutePath() + "/nr_overcommit_hugepages");
|
||||
BufferedReader reader = new BufferedReader(fr)) {
|
||||
String s = reader.readLine();
|
||||
config.nr_overcommit_hugepages = Long.parseLong(s);
|
||||
}
|
||||
hugePageConfigs.add(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
return pagesizes;
|
||||
return hugePageConfigs;
|
||||
}
|
||||
|
||||
private static THPMode readTHPModeFromOS() {
|
||||
THPMode mode = THPMode.unknown;
|
||||
THPMode mode = THPMode.never;
|
||||
String file = "/sys/kernel/mm/transparent_hugepage/enabled";
|
||||
try (FileReader fr = new FileReader(file);
|
||||
BufferedReader reader = new BufferedReader(fr)) {
|
||||
@ -136,7 +182,8 @@ class HugePageConfiguration {
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.out.println("Failed to read " + file);
|
||||
mode = THPMode.unknown;
|
||||
// Happens when the kernel is not built to support THPs.
|
||||
mode = THPMode.never;
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
@ -148,19 +195,19 @@ class HugePageConfiguration {
|
||||
BufferedReader reader = new BufferedReader(fr)) {
|
||||
String s = reader.readLine();
|
||||
pagesize = Long.parseLong(s);
|
||||
} catch (IOException | NumberFormatException e) { /* ignored */ }
|
||||
} catch (IOException | NumberFormatException e) { } // ignored
|
||||
return pagesize;
|
||||
}
|
||||
|
||||
// Fill object with info read from proc file system
|
||||
public static HugePageConfiguration readFromOS() {
|
||||
public static HugePageConfiguration readFromOS() throws IOException {
|
||||
return new HugePageConfiguration(readSupportedHugePagesFromOS(),
|
||||
readDefaultHugePageSizeFromOS(),
|
||||
readTHPModeFromOS(),
|
||||
readTHPPageSizeFromOS());
|
||||
}
|
||||
|
||||
private static long parseSIUnit(String num, String unit) {
|
||||
public static long parseSIUnit(String num, String unit) {
|
||||
long n = Long.parseLong(num);
|
||||
return switch (unit) {
|
||||
case "K" -> n * 1024;
|
||||
@ -180,7 +227,7 @@ class HugePageConfiguration {
|
||||
// [0.001s][info][pagesize] Transparent hugepage (THP) support:
|
||||
// [0.001s][info][pagesize] THP mode: madvise
|
||||
// [0.001s][info][pagesize] THP pagesize: 2M
|
||||
TreeSet<Long> hugepages = new TreeSet<>();
|
||||
TreeSet<StaticHugePageConfig> staticHugePageConfigs = new TreeSet<>();
|
||||
long defaultHugepageSize = 0;
|
||||
THPMode thpMode = THPMode.never;
|
||||
long thpPageSize = 0;
|
||||
@ -192,7 +239,9 @@ class HugePageConfiguration {
|
||||
for (String s : lines) {
|
||||
Matcher mat = patternHugepageSize.matcher(s);
|
||||
if (mat.matches()) {
|
||||
hugepages.add(parseSIUnit(mat.group(1), mat.group(2)));
|
||||
StaticHugePageConfig config = new StaticHugePageConfig();
|
||||
config.pageSize = parseSIUnit(mat.group(1), mat.group(2));
|
||||
staticHugePageConfigs.add(config);
|
||||
continue;
|
||||
}
|
||||
if (defaultHugepageSize == 0) {
|
||||
@ -215,7 +264,7 @@ class HugePageConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
return new HugePageConfiguration(hugepages, defaultHugepageSize, thpMode, thpPageSize);
|
||||
return new HugePageConfiguration(staticHugePageConfigs, defaultHugepageSize, thpMode, thpPageSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, Red Hat Inc.
|
||||
* 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 id=Default
|
||||
* @summary Test JVM large page setup (default options)
|
||||
* @library /test/lib
|
||||
* @requires os.family == "linux"
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
* @run driver TestHugePageDecisionsAtVMStartup
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=LP_enabled
|
||||
* @summary Test JVM large page setup (+LP)
|
||||
* @library /test/lib
|
||||
* @requires os.family == "linux"
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
* @run driver TestHugePageDecisionsAtVMStartup -XX:+UseLargePages
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=THP_enabled
|
||||
* @summary Test JVM large page setup (+THP)
|
||||
* @library /test/lib
|
||||
* @requires os.family == "linux"
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* java.management
|
||||
* @run driver TestHugePageDecisionsAtVMStartup -XX:+UseTransparentHugePages
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class TestHugePageDecisionsAtVMStartup {
|
||||
|
||||
// End user warnings, printing with Xlog:pagesize at warning level, should be unconditional
|
||||
static final String warningNoTHP = "[warning][pagesize] UseTransparentHugePages disabled, transparent huge pages are not supported by the operating system.";
|
||||
static final String warningNoLP = "[warning][pagesize] UseLargePages disabled, no large pages configured and available on the system.";
|
||||
|
||||
static final String buildSizeString(long l) {
|
||||
String units[] = { "K", "M", "G" };
|
||||
long factor = 1024 * 1024 * 1024;
|
||||
for (int i = 2; i >= 0; i--) {
|
||||
if (l >= factor) {
|
||||
return Long.toString(l / factor) + units[i];
|
||||
}
|
||||
factor /= 1024;
|
||||
}
|
||||
return Long.toString(l) + "B";
|
||||
}
|
||||
|
||||
static void testOutput(boolean useLP, boolean useTHP, OutputAnalyzer out, HugePageConfiguration configuration) {
|
||||
|
||||
// Note: If something goes wrong, the JVM warns but continues, so we should never see an exit value != 0
|
||||
out.shouldHaveExitValue(0);
|
||||
|
||||
// Static hugepages:
|
||||
// Let X = the default hugepage size of the system (the one in /proc/meminfo).
|
||||
// The JVM will cycle through page sizes, starting at X, down to the smallest hugepage size.
|
||||
//
|
||||
// Example 1: a system with 1GB and 2MB pages, the default hugepage size is 1GB (can only be done
|
||||
// via kernel parameter). the JVM should first attempt to use 1GB pages, failing that should try 2MB, failing
|
||||
// that give up and disable -UseLargePages.
|
||||
//
|
||||
// Example 1: same system, but the default hugepage size is 2MB. The JVM should not attempt to use 1GB pages.
|
||||
//
|
||||
// This picture gets more complex with -XX:LargePageSizeInBytes, which overrides the default
|
||||
// large page size; but we ignore this for now (feel free to extend the test to cover LBSiB too).
|
||||
|
||||
boolean haveUsableStaticHugePages = false;
|
||||
if (configuration.supportsStaticHugePages()) {
|
||||
long defaultLargePageSize = configuration.getStaticDefaultHugePageSize();
|
||||
Set<HugePageConfiguration.StaticHugePageConfig> configs = configuration.getStaticHugePageConfigurations();
|
||||
for (HugePageConfiguration.StaticHugePageConfig config: configs) {
|
||||
if (config.pageSize <= defaultLargePageSize) {
|
||||
if (config.nr_hugepages > 0 || config.nr_overcommit_hugepages > 0) {
|
||||
haveUsableStaticHugePages = true; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (useTHP && !useLP) {
|
||||
useLP = true; // its implicit
|
||||
}
|
||||
|
||||
if (!useLP) {
|
||||
out.shouldContain("[info][pagesize] Large page support disabled");
|
||||
} else if (useLP && !useTHP &&
|
||||
(!configuration.supportsStaticHugePages() || !haveUsableStaticHugePages)) {
|
||||
out.shouldContain(warningNoLP);
|
||||
} else if (useLP && useTHP && !configuration.supportsTHP()) {
|
||||
out.shouldContain(warningNoTHP);
|
||||
} else if (useLP && !useTHP &&
|
||||
configuration.supportsStaticHugePages() && haveUsableStaticHugePages) {
|
||||
out.shouldContain("[info][pagesize] Using the default large page size: " + buildSizeString(configuration.getStaticDefaultHugePageSize()));
|
||||
out.shouldContain("[info][pagesize] UseLargePages=1, UseTransparentHugePages=0");
|
||||
out.shouldContain("[info][pagesize] Large page support enabled");
|
||||
} else if (useLP && useTHP && configuration.supportsTHP()) {
|
||||
String thpPageSizeString = buildSizeString(configuration.getThpPageSize());
|
||||
// We expect to see exactly two "Usable page sizes" : the system page size and the THP page size. The system
|
||||
// page size differs, but its always in KB).
|
||||
out.shouldContain("[info][pagesize] UseLargePages=1, UseTransparentHugePages=1");
|
||||
out.shouldMatch(".*\\[info]\\[pagesize] Large page support enabled. Usable page sizes: \\d+[kK], " + thpPageSizeString + ". Default large page size: " + thpPageSizeString + ".*");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] extraOptions) throws Exception {
|
||||
List<String> allOptions = new ArrayList<String>();
|
||||
if (extraOptions != null) {
|
||||
allOptions.addAll(Arrays.asList(extraOptions));
|
||||
}
|
||||
allOptions.add("-Xmx128m");
|
||||
allOptions.add("-Xlog:pagesize");
|
||||
allOptions.add("-version");
|
||||
|
||||
boolean useLP = allOptions.contains("-XX:+UseLargePages");
|
||||
boolean useTHP = allOptions.contains("-XX:+UseTransparentHugePages");
|
||||
System.out.println("useLP: " + useLP + " useTHP: " + useTHP);
|
||||
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(allOptions.toArray(new String[0]));
|
||||
OutputAnalyzer output = new OutputAnalyzer(pb.start());
|
||||
output.reportDiagnosticSummary();
|
||||
HugePageConfiguration configuration = HugePageConfiguration.readFromOS();
|
||||
System.out.println("configuration read from OS:" + configuration);
|
||||
|
||||
testOutput(useLP, useTHP, output, configuration);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user