--- old/src/java.base/linux/classes/jdk/internal/platform/CgroupInfo.java 2020-02-11 18:27:56.522474434 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/CgroupInfo.java 2020-02-11 18:27:56.384473976 +0100 @@ -37,7 +37,6 @@ private final String name; private final int hierarchyId; private final boolean enabled; - private String cgroupPath; private CgroupInfo(String name, int hierarchyId, boolean enabled) { this.name = name; @@ -57,14 +56,6 @@ return enabled; } - String getCgroupPath() { - return cgroupPath; - } - - void setCgroupPath(String cgroupPath) { - this.cgroupPath = cgroupPath; - } - static CgroupInfo fromCgroupsLine(String line) { String[] tokens = line.split("\\s+"); if (tokens.length != 4) { --- old/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java 2020-02-11 18:27:57.215476729 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java 2020-02-11 18:27:57.082476289 +0100 @@ -29,9 +29,9 @@ public class CgroupMetrics implements Metrics { - private final CgroupSubsystem subsystem; + protected final CgroupSubsystem subsystem; - private CgroupMetrics(CgroupSubsystem subsystem) { + CgroupMetrics(CgroupSubsystem subsystem) { this.subsystem = Objects.requireNonNull(subsystem); } @@ -115,17 +115,6 @@ return subsystem.getEffectiveCpuSetMems(); } - @Override - public double getCpuSetMemoryPressure() { - return subsystem.getCpuSetMemoryPressure(); - } - - @Override - public Boolean isCpuSetMemoryPressureEnabled() { - return subsystem.isCpuSetMemoryPressureEnabled(); - } - - @Override public long getMemoryFailCount() { return subsystem.getMemoryFailCount(); } @@ -136,81 +125,26 @@ } @Override - public long getMemoryMaxUsage() { - return subsystem.getMemoryMaxUsage(); - } - - @Override public long getMemoryUsage() { return subsystem.getMemoryUsage(); } @Override - public long getKernelMemoryFailCount() { - return subsystem.getKernelMemoryFailCount(); - } - - @Override - public long getKernelMemoryLimit() { - return subsystem.getKernelMemoryLimit(); - } - - @Override - public long getKernelMemoryMaxUsage() { - return subsystem.getKernelMemoryMaxUsage(); - } - - @Override - public long getKernelMemoryUsage() { - return subsystem.getKernelMemoryUsage(); - } - - @Override - public long getTcpMemoryFailCount() { - return subsystem.getTcpMemoryFailCount(); - } - - @Override - public long getTcpMemoryLimit() { - return subsystem.getTcpMemoryLimit(); - } - - @Override - public long getTcpMemoryMaxUsage() { - return subsystem.getTcpMemoryMaxUsage(); - } - - @Override public long getTcpMemoryUsage() { return subsystem.getTcpMemoryUsage(); } @Override - public long getMemoryAndSwapFailCount() { - return subsystem.getMemoryAndSwapFailCount(); - } - - @Override public long getMemoryAndSwapLimit() { return subsystem.getMemoryAndSwapLimit(); } @Override - public long getMemoryAndSwapMaxUsage() { - return subsystem.getMemoryAndSwapMaxUsage(); - } - - @Override public long getMemoryAndSwapUsage() { return subsystem.getMemoryAndSwapUsage(); } @Override - public Boolean isMemoryOOMKillEnabled() { - return subsystem.isMemoryOOMKillEnabled(); - } - - @Override public long getMemorySoftLimit() { return subsystem.getMemorySoftLimit(); } @@ -226,8 +160,7 @@ } public static Metrics getInstance() { - CgroupSubsystem subsystem = CgroupSubsystemFactory.create(); - return new CgroupMetrics(subsystem); + return CgroupSubsystemFactory.create(); } } \ No newline at end of file --- old/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystem.java 2020-02-11 18:27:57.911479035 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystem.java 2020-02-11 18:27:57.773478577 +0100 @@ -30,4 +30,11 @@ * */ public interface CgroupSubsystem extends Metrics { + + /** + * Returned for metrics of type long if the underlying implementation + * has determined that no limit is being imposed. + */ + public static final long LONG_RETVAL_UNLIMITED = -1; + } --- old/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemController.java 2020-02-11 18:27:58.599481314 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemController.java 2020-02-11 18:27:58.457480843 +0100 @@ -48,13 +48,14 @@ /** * getStringValue * - * Return the first line of the file "parm" argument from the controller. + * Return the first line of the file "param" argument from the controller. * * TODO: Consider using weak references for caching BufferedReader object. * * @param controller - * @param parm - * @return Returns the contents of the file specified by param. + * @param param + * @return Returns the contents of the file specified by param or null if + * an error occurs. */ public static String getStringValue(CgroupSubsystemController controller, String param) { if (controller == null) return null; @@ -68,11 +69,27 @@ } + /** + * Get an entry from file "param" within the "controller" directory path + * which matches string "match". Applies "conversion" to the matching line. + * + * @param controller + * @param param + * @param match + * @param conversion + * @param defaultRetval + * @return The long value as derived by applying "conversion" to the matching + * line or "defaultRetval" if there was an error or no match found. + */ public static long getLongValueMatchingLine(CgroupSubsystemController controller, String param, String match, - Function conversion) { - long retval = Metrics.LONG_RETVAL_UNLIMITED; + Function conversion, + long defaultRetval) { + long retval = defaultRetval; + if (controller == null) { + return retval; + } try { Path filePath = Paths.get(controller.path(), param); List lines = CgroupUtil.readAllLinesPrivileged(filePath); @@ -88,17 +105,38 @@ return retval; } + /** + * Get a long value from directory "controller" and file "param", by + * applying "conversion" to the string value within the file. + * + * @param controller + * @param param + * @param conversion + * @param defaultRetval + * @return The converted long value or "defaultRetval" if there was an + * error. + */ public static long getLongValue(CgroupSubsystemController controller, - String parm, - Function conversion) { - String strval = getStringValue(controller, parm); + String param, + Function conversion, + long defaultRetval) { + String strval = getStringValue(controller, param); + if (strval == null) return defaultRetval; return conversion.apply(strval); } - public static double getDoubleValue(CgroupSubsystemController controller, String parm) { - String strval = getStringValue(controller, parm); + /** + * Get a double value from file "param" within "controller". + * + * @param controller + * @param param + * @param defaultRetval + * @return The double value or "defaultRetval" if there was an error. + */ + public static double getDoubleValue(CgroupSubsystemController controller, String param, double defaultRetval) { + String strval = getStringValue(controller, param); - if (strval == null) return 0L; + if (strval == null) return defaultRetval; double retval = Double.parseDouble(strval); @@ -106,22 +144,23 @@ } /** - * getSubSystemlongEntry + * getLongEntry * * Return the long value from the line containing the string "entryname" - * within file "parm" in the "subsystem". + * within file "param" in the "controller". * * TODO: Consider using weak references for caching BufferedReader object. * * @param controller - * @param parm + * @param param * @param entryname - * @return long value + * @return long value or "defaultRetval" if there was an error or no match + * was found. */ - public static long getLongEntry(CgroupSubsystemController controller, String parm, String entryname) { - if (controller == null) return 0L; + public static long getLongEntry(CgroupSubsystemController controller, String param, String entryname, long defaultRetval) { + if (controller == null) return defaultRetval; - try (Stream lines = CgroupUtil.readFilePrivileged(Paths.get(controller.path(), parm))) { + try (Stream lines = CgroupUtil.readFilePrivileged(Paths.get(controller.path(), param))) { Optional result = lines.map(line -> line.split(" ")) .filter(line -> (line.length == 2 && @@ -129,21 +168,23 @@ .map(line -> line[1]) .findFirst(); - return result.isPresent() ? Long.parseLong(result.get()) : 0L; + return result.isPresent() ? Long.parseLong(result.get()) : defaultRetval; } catch (IOException e) { - return 0L; + return defaultRetval; } } /** - * StringRangeToIntArray + * stringRangeToIntArray * * Convert a string in the form of 1,3-4,6 to an array of * integers containing all the numbers in the range. * * @param range - * @return int[] containing a sorted list of processors or memory nodes + * @return int[] containing a sorted list of numbers as represented by + * the string range. Returns null if there was an error or the input + * was an empty string. */ public static int[] stringRangeToIntArray(String range) { if (range == null || EMPTY_STR.equals(range)) return null; @@ -181,15 +222,26 @@ return ints; } - public static long convertStringToLong(String strval, long overflowRetval) { - long retval = 0; - if (strval == null) return 0L; + /** + * Convert a number from its string representation to a long. + * + * @param strval + * @param overflowRetval + * @param defaultRetval + * @return The converted long value. "overflowRetval" is returned if the + * string representation exceeds the range of type long. + * "defaultRetval" is returned if another type of error occurred + * during conversion. + */ + public static long convertStringToLong(String strval, long overflowRetval, long defaultRetval) { + long retval = defaultRetval; + if (strval == null) return retval; try { retval = Long.parseLong(strval); } catch (NumberFormatException e) { - // For some properties (e.g. memory.limit_in_bytes) we may overflow - // the range of signed long. In this case, return unlimited + // For some properties (e.g. memory.limit_in_bytes, cgroups v1) we may overflow + // the range of signed long. In this case, return overflowRetval BigInteger b = new BigInteger(strval); if (b.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) { return overflowRetval; --- old/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java 2020-02-11 18:27:59.288483596 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java 2020-02-11 18:27:59.152483145 +0100 @@ -26,6 +26,8 @@ package jdk.internal.platform; import java.io.IOException; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; import java.nio.file.Paths; import java.util.HashMap; import java.util.List; @@ -45,7 +47,7 @@ private static final String BLKIO_CTRL = "blkio"; private static final String MEMORY_CTRL = "memory"; - static CgroupSubsystem create() { + static CgroupMetrics create() { Map infos = new HashMap<>(); try { List lines = CgroupUtil.readAllLinesPrivileged(Paths.get("/proc/cgroups")); @@ -86,7 +88,8 @@ // not ready to deal with that on a per-controller basis. Return no metrics // in that case if (anyCgroupsV1Controller && anyCgroupsV2Controller) { - System.err.println("Warning: Mixed cgroupv1 and cgroupv2 not supported. Metrics disabled."); + Logger logger = System.getLogger("jdk.internal.platform"); + logger.log(Level.WARNING, "Mixed cgroupv1 and cgroupv2 not supported. Metrics disabled."); return null; } @@ -111,7 +114,7 @@ if (tokens.length != 3) { return null; // something is not right. } - if (!Integer.valueOf(0).toString().equals(tokens[0])) { + if (!"0".equals(tokens[0])) { // hierarchy must be zero for cgroups v2 return null; } @@ -124,9 +127,9 @@ CgroupSubsystemController unified = new CgroupV2SubsystemController( mountPath, cgroupPath); - return new CgroupV2Subsystem(unified); + return new CgroupMetrics(new CgroupV2Subsystem(unified)); } else { - return CgroupV1Subsystem.getInstance(); + return new CgroupV1Metrics(CgroupV1Subsystem.getInstance()); } } } --- old/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java 2020-02-11 18:27:59.978485881 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java 2020-02-11 18:27:59.840485424 +0100 @@ -33,9 +33,9 @@ import jdk.internal.platform.CgroupSubsystem; import jdk.internal.platform.CgroupSubsystemController; import jdk.internal.platform.CgroupUtil; -import jdk.internal.platform.Metrics; +import jdk.internal.platform.MetricsCgroupV1; -public class CgroupV1Subsystem implements CgroupSubsystem { +public class CgroupV1Subsystem implements CgroupSubsystem, MetricsCgroupV1 { private CgroupV1MemorySubSystemController memory; private CgroupV1SubsystemController cpu; private CgroupV1SubsystemController cpuacct; @@ -265,7 +265,8 @@ String parm) { return CgroupSubsystemController.getLongValue(controller, parm, - CgroupV1SubsystemController::convertStringToLong); + CgroupV1SubsystemController::convertStringToLong, + CgroupSubsystem.LONG_RETVAL_UNLIMITED); } public String getProvider() { @@ -296,11 +297,11 @@ } public long getCpuUserUsage() { - return CgroupSubsystemController.getLongEntry(cpuacct, "cpuacct.stat", "user"); + return CgroupV1SubsystemController.getLongEntry(cpuacct, "cpuacct.stat", "user"); } public long getCpuSystemUsage() { - return CgroupSubsystemController.getLongEntry(cpuacct, "cpuacct.stat", "system"); + return CgroupV1SubsystemController.getLongEntry(cpuacct, "cpuacct.stat", "system"); } @@ -320,21 +321,21 @@ public long getCpuShares() { long retval = getLongValue(cpu, "cpu.shares"); if (retval == 0 || retval == 1024) - return Metrics.LONG_RETVAL_UNLIMITED; + return CgroupSubsystem.LONG_RETVAL_UNLIMITED; else return retval; } public long getCpuNumPeriods() { - return CgroupSubsystemController.getLongEntry(cpu, "cpu.stat", "nr_periods"); + return CgroupV1SubsystemController.getLongEntry(cpu, "cpu.stat", "nr_periods"); } public long getCpuNumThrottled() { - return CgroupSubsystemController.getLongEntry(cpu, "cpu.stat", "nr_throttled"); + return CgroupV1SubsystemController.getLongEntry(cpu, "cpu.stat", "nr_throttled"); } public long getCpuThrottledTime() { - return CgroupSubsystemController.getLongEntry(cpu, "cpu.stat", "throttled_time"); + return CgroupV1SubsystemController.getLongEntry(cpu, "cpu.stat", "throttled_time"); } public long getEffectiveCpuCount() { @@ -363,7 +364,7 @@ } public double getCpuSetMemoryPressure() { - return CgroupSubsystemController.getDoubleValue(cpuset, "cpuset.memory_pressure"); + return CgroupV1SubsystemController.getDoubleValue(cpuset, "cpuset.memory_pressure"); } public Boolean isCpuSetMemoryPressureEnabled() { @@ -388,24 +389,14 @@ // memory.limit_in_bytes returned unlimited, attempt // hierarchical memory limit String match = "hierarchical_memory_limit"; - retval = CgroupSubsystemController.getLongValueMatchingLine(memory, + retval = CgroupV1SubsystemController.getLongValueMatchingLine(memory, "memory.stat", - match, - CgroupV1Subsystem::convertHierachicalLimitLine); + match); } } return CgroupV1SubsystemController.longValOrUnlimited(retval); } - public static long convertHierachicalLimitLine(String line) { - String[] tokens = line.split("\\s"); - if (tokens.length == 2) { - String strVal = tokens[1]; - return CgroupV1SubsystemController.convertStringToLong(strVal); - } - return CgroupV1SubsystemController.UNLIMITED_MIN + 1; // unlimited - } - public long getMemoryMaxUsage() { return getLongValue(memory, "memory.max_usage_in_bytes"); } @@ -457,10 +448,9 @@ // memory.memsw.limit_in_bytes returned unlimited, attempt // hierarchical memory limit String match = "hierarchical_memsw_limit"; - retval = CgroupSubsystemController.getLongValueMatchingLine(memory, + retval = CgroupV1SubsystemController.getLongValueMatchingLine(memory, "memory.stat", - match, - CgroupV1Subsystem::convertHierachicalLimitLine); + match); } } return CgroupV1SubsystemController.longValOrUnlimited(retval); @@ -475,7 +465,7 @@ } public Boolean isMemoryOOMKillEnabled() { - long val = CgroupSubsystemController.getLongEntry(memory, "memory.oom_control", "oom_kill_disable"); + long val = CgroupV1SubsystemController.getLongEntry(memory, "memory.oom_control", "oom_kill_disable"); return (val == 0); } @@ -490,11 +480,11 @@ public long getBlkIOServiceCount() { - return CgroupSubsystemController.getLongEntry(blkio, "blkio.throttle.io_service_bytes", "Total"); + return CgroupV1SubsystemController.getLongEntry(blkio, "blkio.throttle.io_service_bytes", "Total"); } public long getBlkIOServiced() { - return CgroupSubsystemController.getLongEntry(blkio, "blkio.throttle.io_serviced", "Total"); + return CgroupV1SubsystemController.getLongEntry(blkio, "blkio.throttle.io_serviced", "Total"); } } --- old/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1SubsystemController.java 2020-02-11 18:28:00.678488200 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1SubsystemController.java 2020-02-11 18:28:00.541487746 +0100 @@ -25,11 +25,12 @@ package jdk.internal.platform.cgroupv1; +import jdk.internal.platform.CgroupSubsystem; import jdk.internal.platform.CgroupSubsystemController; -import jdk.internal.platform.Metrics; public class CgroupV1SubsystemController implements CgroupSubsystemController { + private static final double DOUBLE_RETVAL_UNLIMITED = CgroupSubsystem.LONG_RETVAL_UNLIMITED; // Values returned larger than this number are unlimited. static long UNLIMITED_MIN = 0x7FFFFFFFFF000000L; String root; @@ -72,11 +73,46 @@ return path; } + public static long getLongEntry(CgroupSubsystemController controller, String param, String entryname) { + return CgroupSubsystemController.getLongEntry(controller, + param, + entryname, + CgroupSubsystem.LONG_RETVAL_UNLIMITED /* retval on error */); + } + + public static double getDoubleValue(CgroupSubsystemController controller, String parm) { + return CgroupSubsystemController.getDoubleValue(controller, + parm, + DOUBLE_RETVAL_UNLIMITED /* retval on error */); + } + public static long convertStringToLong(String strval) { - return CgroupSubsystemController.convertStringToLong(strval, Long.MAX_VALUE); + return CgroupSubsystemController.convertStringToLong(strval, + Long.MAX_VALUE /* overflow value */, + CgroupSubsystem.LONG_RETVAL_UNLIMITED /* retval on error */); } public static long longValOrUnlimited(long value) { - return value > UNLIMITED_MIN ? Metrics.LONG_RETVAL_UNLIMITED : value; + return value > UNLIMITED_MIN ? CgroupSubsystem.LONG_RETVAL_UNLIMITED : value; + } + + public static long getLongValueMatchingLine(CgroupSubsystemController controller, + String param, + String match) { + return CgroupSubsystemController.getLongValueMatchingLine(controller, + param, + match, + CgroupV1SubsystemController::convertHierachicalLimitLine, + CgroupSubsystem.LONG_RETVAL_UNLIMITED); } + + public static long convertHierachicalLimitLine(String line) { + String[] tokens = line.split("\\s"); + if (tokens.length == 2) { + String strVal = tokens[1]; + return CgroupV1SubsystemController.convertStringToLong(strVal); + } + return CgroupV1SubsystemController.UNLIMITED_MIN + 1; // unlimited + } + } --- old/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java 2020-02-11 18:28:01.387490549 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java 2020-02-11 18:28:01.229490025 +0100 @@ -34,10 +34,11 @@ import jdk.internal.platform.CgroupSubsystem; import jdk.internal.platform.CgroupSubsystemController; import jdk.internal.platform.CgroupUtil; -import jdk.internal.platform.Metrics; public class CgroupV2Subsystem implements CgroupSubsystem { + private static final long[] LONG_ARRAY_NOT_SUPPORTED = null; + private static final int[] INT_ARRAY_UNAVAILABLE = null; private final CgroupSubsystemController unified; private static final String PROVIDER_NAME = "cgroupv2"; private static final int PER_CPU_SHARES = 1024; @@ -51,7 +52,8 @@ private long getLongVal(String file) { return CgroupSubsystemController.getLongValue(unified, file, - CgroupV2SubsystemController::convertStringToLong); + CgroupV2SubsystemController::convertStringToLong, + CgroupSubsystem.LONG_RETVAL_UNLIMITED); } @Override @@ -61,24 +63,33 @@ @Override public long getCpuUsage() { - long micros = CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "usage_usec"); + long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "usage_usec"); + if (micros < 0) { + return micros; + } return TimeUnit.MICROSECONDS.toNanos(micros); } @Override public long[] getPerCpuUsage() { - return null; // Not supported. + return LONG_ARRAY_NOT_SUPPORTED; } @Override public long getCpuUserUsage() { - long micros = CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "user_usec"); + long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "user_usec"); + if (micros < 0) { + return micros; + } return TimeUnit.MICROSECONDS.toNanos(micros); } @Override public long getCpuSystemUsage() { - long micros = CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "system_usec"); + long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "system_usec"); + if (micros < 0) { + return micros; + } return TimeUnit.MICROSECONDS.toNanos(micros); } @@ -96,20 +107,20 @@ String cpuMaxRaw = CgroupSubsystemController.getStringValue(unified, "cpu.max"); if (cpuMaxRaw == null) { // likely file not found - return Metrics.LONG_RETVAL_UNLIMITED; + return CgroupSubsystem.LONG_RETVAL_UNLIMITED; } // $MAX $PERIOD String[] tokens = cpuMaxRaw.split("\\s+"); if (tokens.length != 2) { - return Metrics.LONG_RETVAL_UNLIMITED; + return CgroupSubsystem.LONG_RETVAL_UNLIMITED; } String quota = tokens[tokenIdx]; return limitFromString(quota); } private long limitFromString(String strVal) { - if (MAX_VAL.equals(strVal)) { - return Metrics.LONG_RETVAL_UNLIMITED; + if (strVal == null || MAX_VAL.equals(strVal)) { + return CgroupSubsystem.LONG_RETVAL_UNLIMITED; } return Long.parseLong(strVal); } @@ -117,8 +128,8 @@ @Override public long getCpuShares() { long sharesRaw = getLongVal("cpu.weight"); - if (sharesRaw == 100 || sharesRaw == 0) { - return Metrics.LONG_RETVAL_UNLIMITED; + if (sharesRaw == 100 || sharesRaw <= 0) { + return CgroupSubsystem.LONG_RETVAL_UNLIMITED; } int shares = (int)sharesRaw; // CPU shares (OCI) value needs to get translated into @@ -145,17 +156,20 @@ @Override public long getCpuNumPeriods() { - return CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "nr_periods"); + return CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "nr_periods"); } @Override public long getCpuNumThrottled() { - return CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "nr_throttled"); + return CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "nr_throttled"); } @Override public long getCpuThrottledTime() { - long micros = CgroupSubsystemController.getLongEntry(unified, "cpu.stat", "throttled_usec"); + long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "throttled_usec"); + if (micros < 0) { + return micros; + } return TimeUnit.MICROSECONDS.toNanos(micros); } @@ -167,44 +181,37 @@ @Override public int[] getCpuSetCpus() { String cpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus"); - if (cpuSetVal == null || EMPTY_STR.equals(cpuSetVal)) { - return null; // not available - } - return CgroupSubsystemController.stringRangeToIntArray(cpuSetVal); + return getCpuSet(cpuSetVal); } @Override public int[] getEffectiveCpuSetCpus() { String effCpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus.effective"); - if (effCpuSetVal == null || EMPTY_STR.equals(effCpuSetVal)) { - return null; // not available - } - return CgroupSubsystemController.stringRangeToIntArray(effCpuSetVal); + return getCpuSet(effCpuSetVal); } @Override public int[] getCpuSetMems() { - return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(unified, "cpuset.mems")); + String cpuSetMems = CgroupSubsystemController.getStringValue(unified, "cpuset.mems"); + return getCpuSet(cpuSetMems); } @Override public int[] getEffectiveCpuSetMems() { - return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(unified, "cpuset.mems.effective")); - } - - @Override - public double getCpuSetMemoryPressure() { - return Metrics.DOUBLE_RETVAL_NOT_SUPPORTED; + String effCpuSetMems = CgroupSubsystemController.getStringValue(unified, "cpuset.mems.effective"); + return getCpuSet(effCpuSetMems); } - @Override - public Boolean isCpuSetMemoryPressureEnabled() { - return Metrics.BOOL_RETVAL_NOT_SUPPORTED; + private int[] getCpuSet(String cpuSetVal) { + if (cpuSetVal == null || EMPTY_STR.equals(cpuSetVal)) { + return INT_ARRAY_UNAVAILABLE; + } + return CgroupSubsystemController.stringRangeToIntArray(cpuSetVal); } @Override public long getMemoryFailCount() { - return CgroupSubsystemController.getLongEntry(unified, "memory.events", "max"); + return CgroupV2SubsystemController.getLongEntry(unified, "memory.events", "max"); } @Override @@ -214,58 +221,13 @@ } @Override - public long getMemoryMaxUsage() { - return Metrics.LONG_RETVAL_NOT_SUPPORTED; - } - - @Override public long getMemoryUsage() { return getLongVal("memory.current"); } @Override - public long getKernelMemoryFailCount() { - return Metrics.LONG_RETVAL_NOT_SUPPORTED; - } - - @Override - public long getKernelMemoryLimit() { - return Metrics.LONG_RETVAL_NOT_SUPPORTED; - } - - @Override - public long getKernelMemoryMaxUsage() { - return Metrics.LONG_RETVAL_NOT_SUPPORTED; - } - - @Override - public long getKernelMemoryUsage() { - return Metrics.LONG_RETVAL_NOT_SUPPORTED; - } - - @Override - public long getTcpMemoryFailCount() { - return Metrics.LONG_RETVAL_NOT_SUPPORTED; - } - - @Override - public long getTcpMemoryLimit() { - return Metrics.LONG_RETVAL_NOT_SUPPORTED; - } - - @Override - public long getTcpMemoryMaxUsage() { - return Metrics.LONG_RETVAL_NOT_SUPPORTED; - } - - @Override public long getTcpMemoryUsage() { - return CgroupSubsystemController.getLongEntry(unified, "memory.stat", "sock"); - } - - @Override - public long getMemoryAndSwapFailCount() { - return Metrics.LONG_RETVAL_NOT_SUPPORTED; + return CgroupV2SubsystemController.getLongEntry(unified, "memory.stat", "sock"); } @Override @@ -275,21 +237,11 @@ } @Override - public long getMemoryAndSwapMaxUsage() { - return Metrics.LONG_RETVAL_NOT_SUPPORTED; - } - - @Override public long getMemoryAndSwapUsage() { return getLongVal("memory.swap.current"); } @Override - public Boolean isMemoryOOMKillEnabled() { - return Metrics.BOOL_RETVAL_NOT_SUPPORTED; - } - - @Override public long getMemorySoftLimit() { String softLimitStr = CgroupSubsystemController.getStringValue(unified, "memory.high"); return limitFromString(softLimitStr); @@ -312,7 +264,7 @@ .map(mapFunc) .collect(Collectors.summingLong(e -> e)); } catch (IOException e) { - return Metrics.LONG_RETVAL_NOT_SUPPORTED; + return CgroupSubsystem.LONG_RETVAL_UNLIMITED; } } --- old/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2SubsystemController.java 2020-02-11 18:28:02.091492880 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2SubsystemController.java 2020-02-11 18:28:01.953492423 +0100 @@ -27,8 +27,8 @@ import java.nio.file.Paths; +import jdk.internal.platform.CgroupSubsystem; import jdk.internal.platform.CgroupSubsystemController; -import jdk.internal.platform.Metrics; public class CgroupV2SubsystemController implements CgroupSubsystemController { @@ -44,6 +44,15 @@ } public static long convertStringToLong(String strval) { - return CgroupSubsystemController.convertStringToLong(strval, Metrics.LONG_RETVAL_UNLIMITED); + return CgroupSubsystemController.convertStringToLong(strval, + CgroupSubsystem.LONG_RETVAL_UNLIMITED /* overflow retval */, + CgroupSubsystem.LONG_RETVAL_UNLIMITED /* default retval on error */); + } + + public static long getLongEntry(CgroupSubsystemController controller, String param, String entryname) { + return CgroupSubsystemController.getLongEntry(controller, + param, + entryname, + CgroupSubsystem.LONG_RETVAL_UNLIMITED /* retval on error */); } } --- old/src/java.base/share/classes/jdk/internal/platform/Metrics.java 2020-02-11 18:28:02.791495199 +0100 +++ new/src/java.base/share/classes/jdk/internal/platform/Metrics.java 2020-02-11 18:28:02.654494745 +0100 @@ -50,23 +50,6 @@ public interface Metrics { /** - * Returned for metrics of type long if the underlying implementation - * has determined that no limit is being imposed. - */ - public static final long LONG_RETVAL_UNLIMITED = -1; - /** - * Returned for metrics of type long if the underlying implementation - * doesn't support that metric. - */ - public static final long LONG_RETVAL_NOT_SUPPORTED = -2; - public static final double DOUBLE_RETVAL_NOT_SUPPORTED = LONG_RETVAL_NOT_SUPPORTED; - /** - * Returned for metrics of type Boolean if the underlying implementation - * doesn't support that metric. - */ - public static final Boolean BOOL_RETVAL_NOT_SUPPORTED = null; - - /** * Returns an instance of the Metrics class. * * @return Metrics object or null if not supported on this platform. @@ -105,7 +88,8 @@ * Returns the aggregate time, in nanoseconds, consumed by all * tasks in the Isolation Group. * - * @return Time in nanoseconds or -2 if metric is not available. + * @return Time in nanoseconds, -1 if unknown or + * -2 if the metric is not supported. * */ public long getCpuUsage(); @@ -121,7 +105,7 @@ * * @return long array of time values. The size of the array is equal * to the total number of physical processors in the system. If - * this metric is not available, null will be + * this metric is not supported or not available, null will be * returned. * */ @@ -131,7 +115,8 @@ * Returns the aggregate user time, in nanoseconds, consumed by all * tasks in the Isolation Group. * - * @return User time in nanoseconds or -2 if metric is not available. + * @return User time in nanoseconds, -1 if the metric is not available or + * -2 if the metric is not supported. * */ public long getCpuUserUsage(); @@ -140,7 +125,8 @@ * Returns the aggregate system time, in nanoseconds, consumed by * all tasks in the Isolation Group. * - * @return System time in nanoseconds or -2 if metric is not available. + * @return System time in nanoseconds, -1 if the metric is not available or + * -2 if the metric is not supported. * */ public long getCpuSystemUsage(); @@ -153,7 +139,8 @@ * Returns the length of the scheduling period, in * microseconds, for processes within the Isolation Group. * - * @return time in microseconds or -2 if metric is not available. + * @return time in microseconds, -1 if the metric is not available or + * -2 if the metric is not supported. * */ public long getCpuPeriod(); @@ -163,7 +150,8 @@ * during each scheduling period for all tasks in the Isolation * Group. * - * @return time in microseconds or -2 if not supported. + * @return time in microseconds, -1 if the quota is unlimited or + * -2 if not supported. * */ public long getCpuQuota(); @@ -182,17 +170,18 @@ * each process. To request 2 CPUS worth of execution time, CPU shares * would be set to 2048. * - * @return shares value or -2 if no share set. + * @return shares value, -1 if the metric is not available or + * -2 if cpu shares are not supported. * */ public long getCpuShares(); /** * Returns the number of time-slice periods that have elapsed if - * a CPU quota has been setup for the Isolation Group; otherwise - * returns 0. + * a CPU quota has been setup for the Isolation Group * - * @return count of elapsed periods or -2 if not supported. + * @return count of elapsed periods, -1 if the metric is not available + * or -2 if the metric is not supported. * */ public long getCpuNumPeriods(); @@ -202,7 +191,8 @@ * been throttled or limited due to the group exceeding its quota * if a CPU quota has been setup for the Isolation Group. * - * @return count of throttled periods or -2 if not supported. + * @return count of throttled periods, -1 if the metric is not available or + * -2 if it is not supported. * */ public long getCpuNumThrottled(); @@ -212,7 +202,8 @@ * group has been throttled or limited due to the group exceeding * its quota if a CPU quota has been setup for the Isolation Group. * - * @return Throttled time in nanoseconds or -2 if not supported. + * @return Throttled time in nanoseconds, -1 if the metric is not available + * or -2 if it is not supported. * */ public long getCpuThrottledTime(); @@ -244,8 +235,8 @@ * may be offline. To get the current online CPUs, use * {@link getEffectiveCpuSetCpus()}. * - * @return An array of available CPUs or null - * if the metric is not available. + * @return An array of available CPUs. Returns null if the metric is not + * available or the metric is not supported. * */ public int[] getCpuSetCpus(); @@ -256,8 +247,8 @@ * array is equal to the total number of CPUs and the elements in * the array are the physical CPU numbers. * - * @return An array of available and online CPUs or null - * if the metric is not available. + * @return An array of available and online CPUs. Returns null + * if the metric is not available or the metric is not supported. * */ public int[] getEffectiveCpuSetCpus(); @@ -271,7 +262,7 @@ * {@link getEffectiveCpuSetMems()}. * * @return An array of available memory nodes or null - * if the metric is not available. + * if the metric is not available or is not supported. * */ public int[] getCpuSetMems(); @@ -283,33 +274,11 @@ * the array are the physical node numbers. * * @return An array of available and online nodes or null - * if the metric is not available. + * if the metric is not available or is not supported. * */ public int[] getEffectiveCpuSetMems(); - /** - * Returns the (attempts per second * 1000), if enabled, that the - * operating system tries to satisfy a memory request for any - * process in the current Isolation Group when no free memory is - * readily available. Use {@link #isCpuSetMemoryPressureEnabled()} to - * determine if this support is enabled. - * - * @return Memory pressure or 0 if not enabled or -2 if metric is not - * available. - * - */ - public double getCpuSetMemoryPressure(); - - /** - * Returns the state of the memory pressure detection support. - * - * @return true if support is available and enabled. null if metric is - * not available. false otherwise. - * - */ - public Boolean isCpuSetMemoryPressureEnabled(); - /***************************************************************** * Memory Subsystem ****************************************************************/ @@ -318,8 +287,9 @@ * Returns the number of times that user memory requests in the * Isolation Group have exceeded the memory limit. * - * @return The number of exceeded requests or -2 if metric - * is not available. + * @return The number of exceeded requests or -1 if the metric + * is not available. Returns -2 if the metric is not + * supported. * */ public long getMemoryFailCount(); @@ -329,164 +299,54 @@ * can be allocated in the Isolation Group. * * @return The maximum amount of memory in bytes or -1 if - * there is no limit or -2 if this metric is not available. + * there is no limit or -2 if this metric is not supported. * */ public long getMemoryLimit(); /** - * Returns the largest amount of physical memory, in bytes, that - * have been allocated in the Isolation Group. - * - * @return The largest amount of memory in bytes or -2 if this - * metric is not available. - * - */ - public long getMemoryMaxUsage(); - - /** * Returns the amount of physical memory, in bytes, that is currently * allocated in the current Isolation Group. * - * @return The amount of memory in bytes allocated or -2 if this - * metric is not available. + * @return The amount of memory in bytes allocated or -1 if + * the metric is not available or -2 if the metric is not + * supported. * */ public long getMemoryUsage(); /** - * Returns the number of times that kernel memory requests in the - * Isolation Group have exceeded the kernel memory limit. - * - * @return The number of exceeded requests or -2 if metric - * is not available. - * - */ - public long getKernelMemoryFailCount(); - - /** - * Returns the maximum amount of kernel physical memory, in bytes, that - * can be allocated in the Isolation Group. - * - * @return The maximum amount of memory in bytes or -1 if - * there is no limit set or -2 if this metric is not available. - * - */ - public long getKernelMemoryLimit(); - - /** - * Returns the largest amount of kernel physical memory, in bytes, that - * have been allocated in the Isolation Group. - * - * @return The largest amount of memory in bytes or -2 if this - * metric is not available. - * - */ - public long getKernelMemoryMaxUsage(); - - /** - * Returns the amount of kernel physical memory, in bytes, that - * is currently allocated in the current Isolation Group. - * - * @return The amount of memory in bytes allocated or -2 if this - * metric is not available. - * - */ - public long getKernelMemoryUsage(); - - /** - * Returns the number of times that networking memory requests in the - * Isolation Group have exceeded the kernel memory limit. - * - * @return The number of exceeded requests or -2 if the metric - * is not available. - * - */ - public long getTcpMemoryFailCount(); - - /** - * Returns the maximum amount of networking physical memory, in bytes, - * that can be allocated in the Isolation Group. - * - * @return The maximum amount of memory in bytes or -1 if - * there is no limit or -2 if this metric is not available. - * - */ - public long getTcpMemoryLimit(); - - /** - * Returns the largest amount of networking physical memory, in bytes, - * that have been allocated in the Isolation Group. - * - * @return The largest amount of memory in bytes or -2 if this - * metric is not available. - * - */ - public long getTcpMemoryMaxUsage(); - - /** * Returns the amount of networking physical memory, in bytes, that * is currently allocated in the current Isolation Group. * - * @return The amount of memory in bytes allocated or -2 if this - * metric is not available. + * @return The amount of memory in bytes allocated or -1 if the metric + * is not available. Returns -2 if this metric is not supported. * */ public long getTcpMemoryUsage(); /** - * Returns the number of times that user memory requests in the - * Isolation Group have exceeded the memory + swap limit. - * - * @return The number of exceeded requests or -2 if the metric - * is not available. - * - */ - public long getMemoryAndSwapFailCount(); - - /** * Returns the maximum amount of physical memory and swap space, * in bytes, that can be allocated in the Isolation Group. * * @return The maximum amount of memory in bytes or -1 if - * there is no limit set or -2 if this metric is not available. + * there is no limit set or -2 if this metric is not supported. * */ public long getMemoryAndSwapLimit(); /** - * Returns the largest amount of physical memory and swap space, - * in bytes, that have been allocated in the Isolation Group. - * - * @return The largest amount of memory in bytes or -2 if this - * metric is not available. - * - */ - public long getMemoryAndSwapMaxUsage(); - - /** * Returns the amount of physical memory and swap space, in bytes, * that is currently allocated in the current Isolation Group. * - * @return The amount of memory in bytes allocated or -2 if this - * metric is not available. + * @return The amount of memory in bytes allocated or -1 if + * the metric is not available. Returns -2 if this metric is not + * supported. * */ public long getMemoryAndSwapUsage(); /** - * Returns the state of the Operating System Out of Memory termination - * policy. - * - * @return Returns true if operating system will terminate processes - * in the Isolation Group that exceed the amount of available - * memory, otherwise false. null will be returned if this - * capability is not available on the current operating system. - * - */ - public Boolean isMemoryOOMKillEnabled(); - - /** * Returns the hint to the operating system that allows groups * to specify the minimum amount of physical memory that they need to * achieve reasonable performance in low memory systems. This allows @@ -494,8 +354,8 @@ * * @return The minimum amount of physical memory, in bytes, that the * operating system will try to maintain under low memory - * conditions. If this metric is not available, -2 will be - * returned. + * conditions. If this metric is not available, -1 will be + * returned. Returns -2 if the metric is not supported. * */ public long getMemorySoftLimit(); @@ -508,7 +368,8 @@ * Returns the number of block I/O requests to the disk that have been * issued by the Isolation Group. * - * @return The count of requests or -2 if this metric is not available. + * @return The count of requests or -1 if the metric is not available. + * Returns -2 if this metric is not supported. * */ public long getBlkIOServiceCount(); @@ -517,7 +378,8 @@ * Returns the number of block I/O bytes that have been transferred * to/from the disk by the Isolation Group. * - * @return The number of bytes transferred or -2 if this metric is not available. + * @return The number of bytes transferred or -1 if the metric is not + * available. Returns -2 if this metric is not supported. * */ public long getBlkIOServiced(); --- old/src/java.base/share/classes/sun/launcher/LauncherHelper.java 2020-02-11 18:28:03.492497521 +0100 +++ new/src/java.base/share/classes/sun/launcher/LauncherHelper.java 2020-02-11 18:28:03.350497051 +0100 @@ -119,6 +119,7 @@ private static final String defaultBundleName = "sun.launcher.resources.launcher"; + private static final long LONG_RETVAL_NOT_SUPPORTED = -2; private static class ResourceBundleHolder { private static final ResourceBundle RB = ResourceBundle.getBundle(defaultBundleName); @@ -393,9 +394,6 @@ ostream.println(INDENT + "List of Available Memory Nodes: N/A"); } - ostream.println(formatBoolean(c.isCpuSetMemoryPressureEnabled(), - INDENT + "CPUSet Memory Pressure Enabled: ")); - long limit = c.getMemoryLimit(); ostream.println(formatLimitString(limit, INDENT + "Memory Limit: ")); @@ -405,22 +403,13 @@ limit = c.getMemoryAndSwapLimit(); ostream.println(formatLimitString(limit, INDENT + "Memory & Swap Limit: ")); - limit = c.getKernelMemoryLimit(); - ostream.println(formatLimitString(limit, INDENT + "Kernel Memory Limit: ")); - - limit = c.getTcpMemoryLimit(); - ostream.println(formatLimitString(limit, INDENT + "TCP Memory Limit: ")); - - ostream.println(formatBoolean(c.isMemoryOOMKillEnabled(), - INDENT + "Out Of Memory Killer Enabled: ")); - ostream.println(""); } private static String formatLimitString(long limit, String prefix) { if (limit >= 0) { return prefix + SizePrefix.scaleValue(limit); - } else if (limit == Metrics.LONG_RETVAL_NOT_SUPPORTED) { + } else if (limit == LONG_RETVAL_NOT_SUPPORTED) { return prefix + "N/A"; } else { return prefix + "Unlimited"; @@ -430,7 +419,7 @@ private static String formatCpuVal(long cpuVal, String prefix) { if (cpuVal >= 0) { return prefix + cpuVal + "us"; - } else if (cpuVal == Metrics.LONG_RETVAL_NOT_SUPPORTED) { + } else if (cpuVal == LONG_RETVAL_NOT_SUPPORTED) { return prefix + "N/A"; } else { return prefix + cpuVal; @@ -438,10 +427,10 @@ } private static String formatBoolean(Boolean value, String prefix) { - if (value == Metrics.BOOL_RETVAL_NOT_SUPPORTED) { - return prefix + "N/A"; - } else { + if (value != null) { return prefix + value; + } else { + return prefix + "N/A"; } } --- old/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java 2020-02-11 18:28:04.194499847 +0100 +++ new/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java 2020-02-11 18:28:04.059499399 +0100 @@ -23,6 +23,7 @@ import java.util.Arrays; import jdk.internal.platform.Metrics; +import jdk.internal.platform.MetricsCgroupV1; public class MetricsMemoryTester { public static void main(String[] args) { @@ -107,14 +108,20 @@ } private static void testKernelMemoryLimit(String value) { - long limit = getMemoryValue(value); - long kmemlimit = Metrics.systemMetrics().getKernelMemoryLimit(); - if (kmemlimit != 0 && limit != kmemlimit) { - throw new RuntimeException("Kernel Memory limit not equal, expected : [" - + limit + "]" + ", got : [" - + kmemlimit + "]"); - } - System.out.println("TEST PASSED!!!"); + Metrics m = Metrics.systemMetrics(); + if (m instanceof MetricsCgroupV1) { + MetricsCgroupV1 mCgroupV1 = (MetricsCgroupV1)m; + System.out.println("TEST PASSED!!!"); + long limit = getMemoryValue(value); + long kmemlimit = mCgroupV1.getKernelMemoryLimit(); + if (kmemlimit != 0 && limit != kmemlimit) { + throw new RuntimeException("Kernel Memory limit not equal, expected : [" + + limit + "]" + ", got : [" + + kmemlimit + "]"); + } + } else { + throw new RuntimeException("oomKillFlag test not supported for cgroups v2"); + } } private static void testMemoryAndSwapLimit(String memory, String memAndSwap) { @@ -146,9 +153,17 @@ } private static void testOomKillFlag(boolean oomKillFlag) { - if (!(oomKillFlag ^ Metrics.systemMetrics().isMemoryOOMKillEnabled())) { - throw new RuntimeException("oomKillFlag error"); + Metrics m = Metrics.systemMetrics(); + if (m instanceof MetricsCgroupV1) { + MetricsCgroupV1 mCgroupV1 = (MetricsCgroupV1)m; + Boolean expected = Boolean.valueOf(oomKillFlag); + Boolean actual = mCgroupV1.isMemoryOOMKillEnabled(); + if (!(expected.equals(actual))) { + throw new RuntimeException("oomKillFlag error"); + } + System.out.println("TEST PASSED!!!"); + } else { + throw new RuntimeException("oomKillFlag test not supported for cgroups v2"); } - System.out.println("TEST PASSED!!!"); } } --- old/test/lib/jdk/test/lib/containers/cgroup/CPUSetsReader.java 2020-02-11 18:28:04.890502152 +0100 +++ new/test/lib/jdk/test/lib/containers/cgroup/CPUSetsReader.java 2020-02-11 18:28:04.753501698 +0100 @@ -32,6 +32,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; + import jdk.test.lib.Asserts; @@ -54,8 +55,7 @@ public static int getNumCpus() { String path = "/proc/cpuinfo"; - try { - Stream stream = Files.lines(Paths.get(path)); + try(Stream stream = Files.lines(Paths.get(path))) { return (int) stream.filter(line -> line.startsWith("processor")).count(); } catch (IOException e) { return 0; --- old/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV1.java 2020-02-11 18:28:05.596504491 +0100 +++ new/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV1.java 2020-02-11 18:28:05.454504020 +0100 @@ -39,6 +39,7 @@ import java.util.stream.Stream; import jdk.internal.platform.Metrics; +import jdk.internal.platform.MetricsCgroupV1; public class MetricsTesterCgroupV1 implements CgroupMetricsTester { @@ -203,7 +204,7 @@ } public void testMemorySubsystem() { - Metrics metrics = Metrics.systemMetrics(); + MetricsCgroupV1 metrics = (MetricsCgroupV1)Metrics.systemMetrics(); // User Memory long oldVal = metrics.getMemoryFailCount(); @@ -327,7 +328,7 @@ } public void testCpuAccounting() { - Metrics metrics = Metrics.systemMetrics(); + MetricsCgroupV1 metrics = (MetricsCgroupV1)Metrics.systemMetrics(); long oldVal = metrics.getCpuUsage(); long newVal = getLongValueFromFile(Controller.CPUACCT, "cpuacct.usage"); @@ -366,7 +367,7 @@ } public void testCpuSchedulingMetrics() { - Metrics metrics = Metrics.systemMetrics(); + MetricsCgroupV1 metrics = (MetricsCgroupV1)Metrics.systemMetrics(); long oldVal = metrics.getCpuPeriod(); long newVal = getLongValueFromFile(Controller.CPUACCT, "cpu.cfs_period_us"); if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) { @@ -406,7 +407,7 @@ } public void testCpuSets() { - Metrics metrics = Metrics.systemMetrics(); + MetricsCgroupV1 metrics = (MetricsCgroupV1)Metrics.systemMetrics(); Integer[] oldVal = Arrays.stream(metrics.getCpuSetCpus()).boxed().toArray(Integer[]::new); Arrays.sort(oldVal); @@ -474,7 +475,7 @@ } private void testBlkIO() { - Metrics metrics = Metrics.systemMetrics(); + MetricsCgroupV1 metrics = (MetricsCgroupV1)Metrics.systemMetrics(); long oldVal = metrics.getBlkIOServiceCount(); long newVal = getLongValueFromFile(Controller.BLKIO, "blkio.throttle.io_service_bytes", "Total"); @@ -491,7 +492,7 @@ } public void testCpuConsumption() throws IOException, InterruptedException { - Metrics metrics = Metrics.systemMetrics(); + MetricsCgroupV1 metrics = (MetricsCgroupV1)Metrics.systemMetrics(); // make system call long newSysVal = metrics.getCpuSystemUsage(); long newUserVal = metrics.getCpuUserUsage(); @@ -530,7 +531,7 @@ } public void testMemoryUsage() throws Exception { - Metrics metrics = Metrics.systemMetrics(); + MetricsCgroupV1 metrics = (MetricsCgroupV1)Metrics.systemMetrics(); long memoryMaxUsage = metrics.getMemoryMaxUsage(); long memoryUsage = metrics.getMemoryUsage(); long newMemoryMaxUsage = 0, newMemoryUsage = 0; --- old/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV2.java 2020-02-11 18:28:06.307506846 +0100 +++ new/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV2.java 2020-02-11 18:28:06.156506346 +0100 @@ -34,13 +34,12 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import jdk.internal.platform.CgroupSubsystem; import jdk.internal.platform.Metrics; -import jdk.test.lib.Asserts; public class MetricsTesterCgroupV2 implements CgroupMetricsTester { private static final long UNLIMITED = -1; - private static final long NOT_SUPPORTED = -2; private static final UnifiedController UNIFIED = new UnifiedController(); private static final String MAX = "max"; private static final int PER_CPU_SHARES = 1024; @@ -153,21 +152,6 @@ CgroupMetricsTester.warn(UnifiedController.NAME, metric, oldVal, newVal); } - private void verifyNotSupported(long metricVal) { - Asserts.assertEquals(metricVal, NOT_SUPPORTED, "Expected metric to be not supported!"); - } - - private void verifyNotSupported(double metricVal) { - if (!CgroupMetricsTester.compareWithErrorMargin(NOT_SUPPORTED, metricVal)) { - throw new RuntimeException("Metric not supported, got: " + metricVal + - " expected: " + NOT_SUPPORTED); - } - } - - private void verifyPerCpuNotSupported(long[] perCpuUsage) { - Asserts.assertNull(perCpuUsage, "perCpuUsage expected to be not supported"); - } - private long getCpuShares(String file) { long rawVal = getLongValueFromFile(file); if (rawVal == 0 || rawVal == 100) { @@ -243,18 +227,6 @@ if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) { fail("memory.current", oldVal, newVal); } - verifyNotSupported(metrics.getMemoryMaxUsage()); - - // Kernel memory - verifyNotSupported(metrics.getKernelMemoryFailCount()); - verifyNotSupported(metrics.getKernelMemoryLimit()); - verifyNotSupported(metrics.getKernelMemoryMaxUsage()); - verifyNotSupported(metrics.getKernelMemoryUsage()); - - //TCP Memory - verifyNotSupported(metrics.getTcpMemoryFailCount()); - verifyNotSupported(metrics.getTcpMemoryLimit()); - verifyNotSupported(metrics.getTcpMemoryMaxUsage()); oldVal = metrics.getTcpMemoryUsage(); newVal = getLongValueEntryFromFile("memory.stat", "sock"); @@ -262,10 +234,6 @@ fail("memory.stat[sock]", oldVal, newVal); } - // Memory and Swap - verifyNotSupported(metrics.getMemoryAndSwapFailCount()); - verifyNotSupported(metrics.getMemoryAndSwapMaxUsage()); - oldVal = metrics.getMemoryAndSwapLimit(); newVal = getLongLimitValueFromFile("memory.swap.max"); if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) { @@ -284,7 +252,6 @@ fail("memory.high", oldVal, newVal); } - Asserts.assertNull(metrics.isMemoryOOMKillEnabled(), "Not supported"); } @Override @@ -297,8 +264,6 @@ warn("cpu.stat[usage_usec]", oldVal, newVal); } - verifyPerCpuNotSupported(metrics.getPerCpuUsage()); - oldVal = metrics.getCpuUserUsage(); newVal = TimeUnit.MICROSECONDS.toNanos(getLongValueEntryFromFile("cpu.stat", "user_usec")); if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) { @@ -400,9 +365,6 @@ fail("cpuset.mems.effective", Arrays.toString(oldVal), Arrays.toString(newVal)); } - - verifyNotSupported(metrics.getCpuSetMemoryPressure()); - Asserts.assertNull(metrics.isCpuSetMemoryPressureEnabled(), "Should be not supported"); } private int[] mapNullToEmpty(int[] cpus) { @@ -422,8 +384,6 @@ long newUserVal = metrics.getCpuUserUsage(); long newUsage = metrics.getCpuUsage(); - verifyPerCpuNotSupported(metrics.getPerCpuUsage()); - // system/user CPU usage counters may be slowly increasing. // allow for equal values for a pass if (newSysVal < startSysVal) { @@ -444,9 +404,8 @@ @Override public void testMemoryUsage() { Metrics metrics = Metrics.systemMetrics(); - long memoryMaxUsage = metrics.getMemoryMaxUsage(); long memoryUsage = metrics.getMemoryUsage(); - long newMemoryMaxUsage = 0, newMemoryUsage = 0; + long newMemoryUsage = 0; // allocate memory in a loop and check more than once for new values // otherwise we might occasionally see the effect of decreasing new memory @@ -459,12 +418,6 @@ break; } } - newMemoryMaxUsage = metrics.getMemoryMaxUsage(); - - if (newMemoryMaxUsage < memoryMaxUsage) { - fail("getMemoryMaxUsage", memoryMaxUsage, - newMemoryMaxUsage); - } if (newMemoryUsage < memoryUsage) { fail("getMemoryUsage", memoryUsage, newMemoryUsage); @@ -511,7 +464,7 @@ return accumulator; }).collect(Collectors.summingLong(e -> e)); } catch (IOException e) { - return Metrics.LONG_RETVAL_NOT_SUPPORTED; + return CgroupSubsystem.LONG_RETVAL_UNLIMITED; } } } --- /dev/null 2020-02-11 09:31:42.601809740 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/CgroupV1Metrics.java 2020-02-11 18:28:06.865508694 +0100 @@ -0,0 +1,78 @@ +package jdk.internal.platform; + +/** + * Cgroup v1 Metrics extensions + * + */ +public class CgroupV1Metrics extends CgroupMetrics implements MetricsCgroupV1 { + + CgroupV1Metrics(MetricsCgroupV1 subsystem) { + super((CgroupSubsystem)subsystem); + } + + @Override + public long getMemoryMaxUsage() { + return ((MetricsCgroupV1)subsystem).getMemoryMaxUsage(); + } + + @Override + public long getKernelMemoryFailCount() { + return ((MetricsCgroupV1)subsystem).getKernelMemoryFailCount(); + } + + @Override + public long getKernelMemoryLimit() { + return ((MetricsCgroupV1)subsystem).getKernelMemoryLimit(); + } + + @Override + public long getKernelMemoryMaxUsage() { + return ((MetricsCgroupV1)subsystem).getKernelMemoryMaxUsage(); + } + + @Override + public long getKernelMemoryUsage() { + return ((MetricsCgroupV1)subsystem).getKernelMemoryUsage(); + } + + @Override + public long getTcpMemoryFailCount() { + return ((MetricsCgroupV1)subsystem).getTcpMemoryFailCount(); + } + + @Override + public long getTcpMemoryLimit() { + return ((MetricsCgroupV1)subsystem).getTcpMemoryLimit(); + } + + @Override + public long getTcpMemoryMaxUsage() { + return ((MetricsCgroupV1)subsystem).getTcpMemoryMaxUsage(); + } + + @Override + public long getMemoryAndSwapFailCount() { + return ((MetricsCgroupV1)subsystem).getMemoryAndSwapFailCount(); + } + + @Override + public long getMemoryAndSwapMaxUsage() { + return ((MetricsCgroupV1)subsystem).getMemoryAndSwapMaxUsage(); + } + + @Override + public Boolean isMemoryOOMKillEnabled() { + return ((MetricsCgroupV1)subsystem).isMemoryOOMKillEnabled(); + } + + @Override + public double getCpuSetMemoryPressure() { + return ((MetricsCgroupV1)subsystem).getCpuSetMemoryPressure(); + } + + @Override + public Boolean isCpuSetMemoryPressureEnabled() { + return ((MetricsCgroupV1)subsystem).isCpuSetMemoryPressureEnabled(); + } + +} --- /dev/null 2020-02-11 09:31:42.601809740 +0100 +++ new/src/java.base/share/classes/jdk/internal/platform/MetricsCgroupV1.java 2020-02-11 18:28:07.661511331 +0100 @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2018, 2020, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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 jdk.internal.platform; + +/** + * + * Cgroup v1 extensions to the Metrics interface. + * + */ +public interface MetricsCgroupV1 extends Metrics { + + /** + * Returns the largest amount of physical memory, in bytes, that + * have been allocated in the Isolation Group. + * + * @return The largest amount of memory in bytes or -1 if this + * metric is not available. Returns -2 if this metric is not + * supported. + * + */ + public long getMemoryMaxUsage(); + + /** + * Returns the number of times that kernel memory requests in the + * Isolation Group have exceeded the kernel memory limit. + * + * @return The number of exceeded requests or -1 if metric + * is not available. + * + */ + public long getKernelMemoryFailCount(); + + /** + * Returns the maximum amount of kernel physical memory, in bytes, that + * can be allocated in the Isolation Group. + * + * @return The maximum amount of memory in bytes or -1 if + * there is no limit set. + * + */ + public long getKernelMemoryLimit(); + + /** + * Returns the largest amount of kernel physical memory, in bytes, that + * have been allocated in the Isolation Group. + * + * @return The largest amount of memory in bytes or -1 if this + * metric is not available. + * + */ + public long getKernelMemoryMaxUsage(); + + /** + * Returns the amount of kernel physical memory, in bytes, that + * is currently allocated in the current Isolation Group. + * + * @return The amount of memory in bytes allocated or -1 if this + * metric is not available. + * + */ + public long getKernelMemoryUsage(); + + /** + * Returns the number of times that networking memory requests in the + * Isolation Group have exceeded the kernel memory limit. + * + * @return The number of exceeded requests or -1 if the metric + * is not available. + * + */ + public long getTcpMemoryFailCount(); + + /** + * Returns the maximum amount of networking physical memory, in bytes, + * that can be allocated in the Isolation Group. + * + * @return The maximum amount of memory in bytes or -1 if + * there is no limit. + * + */ + public long getTcpMemoryLimit(); + + /** + * Returns the largest amount of networking physical memory, in bytes, + * that have been allocated in the Isolation Group. + * + * @return The largest amount of memory in bytes or -1 if this + * metric is not available. + * + */ + public long getTcpMemoryMaxUsage(); + + /** + * Returns the number of times that user memory requests in the + * Isolation Group have exceeded the memory + swap limit. + * + * @return The number of exceeded requests or -1 if the metric + * is not available. + * + */ + public long getMemoryAndSwapFailCount(); + + /** + * Returns the largest amount of physical memory and swap space, + * in bytes, that have been allocated in the Isolation Group. + * + * @return The largest amount of memory in bytes or -1 if this + * metric is not available. + * + */ + public long getMemoryAndSwapMaxUsage(); + + /** + * Returns the state of the Operating System Out of Memory termination + * policy. + * + * @return Returns true if operating system will terminate processes + * in the Isolation Group that exceed the amount of available + * memory, otherwise false. null will be returned if this + * capability is not available on the current operating system. + * + */ + public Boolean isMemoryOOMKillEnabled(); + + /** + * Returns the (attempts per second * 1000), if enabled, that the + * operating system tries to satisfy a memory request for any + * process in the current Isolation Group when no free memory is + * readily available. Use {@link #isCpuSetMemoryPressureEnabled()} to + * determine if this support is enabled. + * + * @return Memory pressure or 0 if not enabled or -1 if metric is not + * available. + * + */ + public double getCpuSetMemoryPressure(); + + /** + * Returns the state of the memory pressure detection support. + * + * @return true if support is available and enabled. false otherwise. + * + */ + public Boolean isCpuSetMemoryPressureEnabled(); +} --- /dev/null 2020-02-11 09:31:42.601809740 +0100 +++ new/test/jdk/jdk/internal/platform/cgroup/TestCgroupSubsystemController.java 2020-02-11 18:28:08.386513732 +0100 @@ -0,0 +1,217 @@ +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import jdk.internal.platform.CgroupSubsystemController; +import jdk.test.lib.Utils; +import jdk.test.lib.util.FileUtils; + +/* + * Copyright (c) 2020, 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 + * @requires os.family == "linux" + * @modules java.base/jdk.internal.platform + * @library /test/lib + * @run junit/othervm TestCgroupSubsystemController + */ + +/** + * + * Basic unit test for CgroupSubsystemController + * + */ +public class TestCgroupSubsystemController { + + private static final double DELTA = 0.01; + private Path existingDirectory; + private Path existingFile; + private String existingFileName = "test-controller-file"; + private String existingFileContents = "foobar"; + private String doubleValueContents = "1.5"; + private String longValueContents = "3000000000"; + private String longValueMatchingLineContents = "testme\n" + + "itemfoo 25"; + private String longEntryContents = "s 1\n" + + "t 2"; + private String longEntryName = "longEntry"; + private String longEntryMatchingLineName = "longMatchingLine"; + private String doubleValueName = "doubleValue"; + private String longValueName = "longValue"; + private CgroupSubsystemController mockController; + + @Before + public void setup() { + try { + existingDirectory = Utils.createTempDirectory(TestCgroupSubsystemController.class.getSimpleName()); + existingFile = Paths.get(existingDirectory.toString(), existingFileName); + Files.writeString(existingFile, existingFileContents, StandardCharsets.UTF_8); + Path longFile = Paths.get(existingDirectory.toString(), longValueName); + Files.writeString(longFile, longValueContents); + Path doubleFile = Paths.get(existingDirectory.toString(), doubleValueName); + Files.writeString(doubleFile, doubleValueContents); + Path longEntryFile = Paths.get(existingDirectory.toString(), longEntryName); + Files.writeString(longEntryFile, longEntryContents); + Path longMatchingLine = Paths.get(existingDirectory.toString(), longEntryMatchingLineName); + Files.writeString(longMatchingLine, longValueMatchingLineContents); + mockController = new MockCgroupSubsystemController(existingDirectory.toString()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @After + public void teardown() { + try { + FileUtils.deleteFileTreeWithRetry(existingDirectory); + } catch (IOException e) { + System.err.println("Teardown failed. " + e.getMessage()); + } + } + + @Test + public void getStringValueNullController() { + String val = CgroupSubsystemController.getStringValue(null, "ignore"); + assertNull(val); + } + + @Test + public void getStringValueIOException() throws IOException { + String val = CgroupSubsystemController.getStringValue(mockController, "don-t-exist.txt"); + assertNull(val); + } + + @Test + public void getStringValueSuccess() { + String actual = CgroupSubsystemController.getStringValue(mockController, existingFileName); + assertEquals(existingFileContents, actual); + } + + @Test + public void convertStringToLong() { + String strVal = "1230"; + long longVal = Long.parseLong(strVal); + long actual = CgroupSubsystemController.convertStringToLong(strVal, -1L, 0); + assertEquals(longVal, actual); + + String overflowVal = "9223372036854775808"; // Long.MAX_VALUE + 1 + long overflowDefault = -1; + actual = CgroupSubsystemController.convertStringToLong(overflowVal, overflowDefault, 0); + assertEquals(overflowDefault, actual); + overflowDefault = Long.MAX_VALUE; + actual = CgroupSubsystemController.convertStringToLong(overflowVal, overflowDefault, 0); + assertEquals(overflowDefault, actual); + } + + @Test + public void convertStringRangeToIntArray() { + assertNull(CgroupSubsystemController.stringRangeToIntArray(null)); + assertNull(CgroupSubsystemController.stringRangeToIntArray("")); + String strRange = "2,4,6"; + int[] actual = CgroupSubsystemController.stringRangeToIntArray(strRange); + int[] expected = new int[] { 2, 4, 6 }; + assertTrue(Arrays.equals(expected, actual)); + strRange = "6,1-3"; + actual = CgroupSubsystemController.stringRangeToIntArray(strRange); + expected = new int[] { 1, 2, 3, 6 }; + assertTrue(Arrays.equals(expected, actual)); + } + + @Test + public void getDoubleValue() { + double defaultValue = -3; + double actual = CgroupSubsystemController.getDoubleValue(null, null, defaultValue); + assertEquals(defaultValue, actual, DELTA); + double expected = Double.parseDouble(doubleValueContents); + actual = CgroupSubsystemController.getDoubleValue(mockController, doubleValueName, defaultValue); + assertEquals(expected, actual, DELTA); + actual = CgroupSubsystemController.getDoubleValue(mockController, "don't-exist", defaultValue); + assertEquals(defaultValue, actual, DELTA); + } + + @Test + public void getLongValue() { + long defaultValue = -4; + long actual = CgroupSubsystemController.getLongValue(null, null, Long::parseLong, defaultValue); + assertEquals(defaultValue, actual); + actual = CgroupSubsystemController.getLongValue(mockController, "dont-exist", Long::parseLong, defaultValue); + assertEquals(defaultValue, actual); + long expected = Long.parseLong(longValueContents); + actual = CgroupSubsystemController.getLongValue(mockController, longValueName, Long::parseLong, defaultValue); + assertEquals(expected, actual); + } + + @Test + public void getLongEntry() { + long defaultValue = -5; + long actual = CgroupSubsystemController.getLongEntry(null, null, "no-matter", defaultValue); + assertEquals(defaultValue, actual); + actual = CgroupSubsystemController.getLongEntry(mockController, "dont-exist", "foo-bar", defaultValue); + assertEquals(defaultValue, actual); + actual = CgroupSubsystemController.getLongEntry(mockController, longEntryName, "t", defaultValue); + assertEquals(2, actual); + } + + @Test + public void getLongMatchingLine() { + long defaultValue = -6; + long actual = CgroupSubsystemController.getLongValueMatchingLine(null, null, "no-matter", Long::parseLong, defaultValue); + assertEquals(defaultValue, actual); + actual = CgroupSubsystemController.getLongValueMatchingLine(mockController, "dont-exist", "no-matter", Long::parseLong, defaultValue); + assertEquals(defaultValue, actual); + actual = CgroupSubsystemController.getLongValueMatchingLine(mockController, longEntryMatchingLineName, "item", TestCgroupSubsystemController::convertLong, defaultValue); + assertEquals(25, actual); + } + + public static long convertLong(String line) { + return Long.parseLong(line.split("\\s+")[1]); + } + + static class MockCgroupSubsystemController implements CgroupSubsystemController { + + private final String path; + + public MockCgroupSubsystemController(String path) { + this.path = path; + } + + @Override + public String path() { + return path; + } + + } + +}