--- old/src/java.base/linux/classes/jdk/internal/platform/CgroupInfo.java 2020-01-09 20:39:10.581136575 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/CgroupInfo.java 2020-01-09 20:39:10.429136415 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Red Hat Inc. + * 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 --- old/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java 2020-01-09 20:39:11.257137284 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/CgroupMetrics.java 2020-01-09 20:39:11.119137139 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Red Hat Inc. + * 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 @@ -121,7 +121,7 @@ } @Override - public boolean isCpuSetMemoryPressureEnabled() { + public Boolean isCpuSetMemoryPressureEnabled() { return subsystem.isCpuSetMemoryPressureEnabled(); } @@ -206,7 +206,7 @@ } @Override - public boolean isMemoryOOMKillEnabled() { + public Boolean isMemoryOOMKillEnabled() { return subsystem.isMemoryOOMKillEnabled(); } --- old/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystem.java 2020-01-09 20:39:11.949138009 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystem.java 2020-01-09 20:39:11.809137862 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Red Hat Inc. + * 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 --- old/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemController.java 2020-01-09 20:39:12.647138741 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemController.java 2020-01-09 20:39:12.504138591 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Red Hat Inc. + * 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 @@ -41,9 +41,6 @@ */ public interface CgroupSubsystemController { - public static final long RETVAL_UNLIMITED = -1; - public static final long RETVAL_NOT_SUPPORTED = -2; - public static final long RETVAL_ERROR = -3; public static final String EMPTY_STR = ""; public String path(); @@ -75,7 +72,7 @@ String param, String match, Function conversion) { - long retval = CgroupSubsystemController.RETVAL_UNLIMITED; + long retval = Metrics.LONG_RETVAL_UNLIMITED; try { Path filePath = Paths.get(controller.path(), param); List lines = CgroupUtil.readAllLinesPrivileged(filePath); @@ -149,9 +146,7 @@ * @return int[] containing a sorted list of processors or memory nodes */ public static int[] stringRangeToIntArray(String range) { - int[] ints = new int[0]; - - if (range == null || EMPTY_STR.equals(range)) return ints; + if (range == null || EMPTY_STR.equals(range)) return null; ArrayList results = new ArrayList<>(); String strs[] = range.split(","); @@ -177,7 +172,7 @@ results.sort(null); // convert ArrayList to primitive int array - ints = new int[results.size()]; + int[] ints = new int[results.size()]; int i = 0; for (Integer n : results) { ints[i++] = n; --- old/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java 2020-01-09 20:39:13.332139460 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java 2020-01-09 20:39:13.193139314 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Red Hat Inc. + * 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 @@ -66,10 +66,14 @@ return null; } - // For cgroups v1 all controllers need to have non-null hierarchy id + // For cgroups v1 all controllers need to have non-zero hierarchy id boolean isCgroupsV2 = true; boolean anyControllersEnabled = false; + boolean anyCgroupsV2Controller = false; + boolean anyCgroupsV1Controller = false; for (CgroupInfo info: infos.values()) { + anyCgroupsV1Controller = anyCgroupsV1Controller || info.getHierarchyId() != 0; + anyCgroupsV2Controller = anyCgroupsV2Controller || info.getHierarchyId() == 0; isCgroupsV2 = isCgroupsV2 && info.getHierarchyId() == 0; anyControllersEnabled = anyControllersEnabled || info.isEnabled(); } @@ -78,6 +82,13 @@ if (!anyControllersEnabled) { return null; } + // Warn about mixed cgroups v1 and cgroups v2 controllers. The code is + // 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."); + return null; + } if (isCgroupsV2) { // read mountinfo so as to determine root mount path --- old/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1MemorySubSystemController.java 2020-01-09 20:39:14.024140185 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1MemorySubSystemController.java 2020-01-09 20:39:13.885140039 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Red Hat Inc. + * 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 --- old/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java 2020-01-09 20:39:14.707140901 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1Subsystem.java 2020-01-09 20:39:14.571140759 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -33,6 +33,7 @@ import jdk.internal.platform.CgroupSubsystem; import jdk.internal.platform.CgroupSubsystemController; import jdk.internal.platform.CgroupUtil; +import jdk.internal.platform.Metrics; public class CgroupV1Subsystem implements CgroupSubsystem { private CgroupV1MemorySubSystemController memory; @@ -283,7 +284,7 @@ public long[] getPerCpuUsage() { String usagelist = CgroupSubsystemController.getStringValue(cpuacct, "cpuacct.usage_percpu"); if (usagelist == null) { - return new long[0]; + return null; } String list[] = usagelist.split(" "); @@ -319,7 +320,7 @@ public long getCpuShares() { long retval = getLongValue(cpu, "cpu.shares"); if (retval == 0 || retval == 1024) - return -1; + return Metrics.LONG_RETVAL_UNLIMITED; else return retval; } @@ -365,7 +366,7 @@ return CgroupSubsystemController.getDoubleValue(cpuset, "cpuset.memory_pressure"); } - public boolean isCpuSetMemoryPressureEnabled() { + public Boolean isCpuSetMemoryPressureEnabled() { long val = getLongValue(cpuset, "cpuset.memory_pressure_enabled"); return (val == 1); } @@ -473,7 +474,7 @@ return getLongValue(memory, "memory.memsw.usage_in_bytes"); } - public boolean isMemoryOOMKillEnabled() { + public Boolean isMemoryOOMKillEnabled() { long val = CgroupSubsystemController.getLongEntry(memory, "memory.oom_control", "oom_kill_disable"); return (val == 0); } --- old/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1SubsystemController.java 2020-01-09 20:39:15.385141613 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1SubsystemController.java 2020-01-09 20:39:15.248141469 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -26,6 +26,7 @@ package jdk.internal.platform.cgroupv1; import jdk.internal.platform.CgroupSubsystemController; +import jdk.internal.platform.Metrics; public class CgroupV1SubsystemController implements CgroupSubsystemController { @@ -76,6 +77,6 @@ } public static long longValOrUnlimited(long value) { - return value > UNLIMITED_MIN ? CgroupSubsystemController.RETVAL_UNLIMITED : value; + return value > UNLIMITED_MIN ? Metrics.LONG_RETVAL_UNLIMITED : value; } } --- old/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java 2020-01-09 20:39:16.066142327 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java 2020-01-09 20:39:15.926142180 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Red Hat Inc. + * 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 @@ -25,10 +25,16 @@ package jdk.internal.platform.cgroupv2; +import java.io.IOException; +import java.nio.file.Paths; import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; 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 { @@ -38,8 +44,6 @@ private static final String MAX_VAL = "max"; private static final Object EMPTY_STR = ""; - private volatile long memoryMaxUsage = Long.MIN_VALUE; - public CgroupV2Subsystem(CgroupSubsystemController unified) { this.unified = unified; } @@ -63,8 +67,7 @@ @Override public long[] getPerCpuUsage() { - // Not supported - return new long[0]; + return null; // Not supported. } @Override @@ -93,12 +96,12 @@ String cpuMaxRaw = CgroupSubsystemController.getStringValue(unified, "cpu.max"); if (cpuMaxRaw == null) { // likely file not found - return CgroupSubsystemController.RETVAL_UNLIMITED; + return Metrics.LONG_RETVAL_UNLIMITED; } // $MAX $PERIOD String[] tokens = cpuMaxRaw.split("\\s+"); if (tokens.length != 2) { - return CgroupSubsystemController.RETVAL_ERROR; + return Metrics.LONG_RETVAL_UNLIMITED; } String quota = tokens[tokenIdx]; return limitFromString(quota); @@ -106,7 +109,7 @@ private long limitFromString(String strVal) { if (MAX_VAL.equals(strVal)) { - return CgroupSubsystemController.RETVAL_UNLIMITED; + return Metrics.LONG_RETVAL_UNLIMITED; } return Long.parseLong(strVal); } @@ -115,7 +118,7 @@ public long getCpuShares() { long sharesRaw = getLongVal("cpu.weight"); if (sharesRaw == 100 || sharesRaw == 0) { - return CgroupSubsystemController.RETVAL_UNLIMITED; + return Metrics.LONG_RETVAL_UNLIMITED; } int shares = (int)sharesRaw; // CPU shares (OCI) value needs to get translated into @@ -165,7 +168,7 @@ public int[] getCpuSetCpus() { String cpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus"); if (cpuSetVal == null || EMPTY_STR.equals(cpuSetVal)) { - return new int[0]; // not available + return null; // not available } return CgroupSubsystemController.stringRangeToIntArray(cpuSetVal); } @@ -174,7 +177,7 @@ public int[] getEffectiveCpuSetCpus() { String effCpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus.effective"); if (effCpuSetVal == null || EMPTY_STR.equals(effCpuSetVal)) { - return new int[0]; // not available + return null; // not available } return CgroupSubsystemController.stringRangeToIntArray(effCpuSetVal); } @@ -191,12 +194,12 @@ @Override public double getCpuSetMemoryPressure() { - return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; + return Metrics.DOUBLE_RETVAL_NOT_SUPPORTED; } @Override - public boolean isCpuSetMemoryPressureEnabled() { - return false; // not supported + public Boolean isCpuSetMemoryPressureEnabled() { + return Metrics.BOOL_RETVAL_NOT_SUPPORTED; } @Override @@ -212,62 +215,57 @@ @Override public long getMemoryMaxUsage() { - return memoryMaxUsage; + return Metrics.LONG_RETVAL_NOT_SUPPORTED; } @Override public long getMemoryUsage() { - long memUsage = getLongVal("memory.current"); - // synthesize memory max usage by piggy-backing on current usage - if (memoryMaxUsage < memUsage) { - memoryMaxUsage = memUsage; - } - return memUsage; + return getLongVal("memory.current"); } @Override public long getKernelMemoryFailCount() { - return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; + return Metrics.LONG_RETVAL_NOT_SUPPORTED; } @Override public long getKernelMemoryLimit() { - return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; + return Metrics.LONG_RETVAL_NOT_SUPPORTED; } @Override public long getKernelMemoryMaxUsage() { - return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; + return Metrics.LONG_RETVAL_NOT_SUPPORTED; } @Override public long getKernelMemoryUsage() { - return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; + return Metrics.LONG_RETVAL_NOT_SUPPORTED; } @Override public long getTcpMemoryFailCount() { - return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; + return Metrics.LONG_RETVAL_NOT_SUPPORTED; } @Override public long getTcpMemoryLimit() { - return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; + return Metrics.LONG_RETVAL_NOT_SUPPORTED; } @Override public long getTcpMemoryMaxUsage() { - return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; + return Metrics.LONG_RETVAL_NOT_SUPPORTED; } @Override public long getTcpMemoryUsage() { - return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; + return CgroupSubsystemController.getLongEntry(unified, "memory.stat", "sock"); } @Override public long getMemoryAndSwapFailCount() { - return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; + return Metrics.LONG_RETVAL_NOT_SUPPORTED; } @Override @@ -278,7 +276,7 @@ @Override public long getMemoryAndSwapMaxUsage() { - return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; + return Metrics.LONG_RETVAL_NOT_SUPPORTED; } @Override @@ -287,8 +285,8 @@ } @Override - public boolean isMemoryOOMKillEnabled() { - return false; // Not supported. + public Boolean isMemoryOOMKillEnabled() { + return Metrics.BOOL_RETVAL_NOT_SUPPORTED; } @Override @@ -299,11 +297,71 @@ @Override public long getBlkIOServiceCount() { - return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; + return sumTokensIOStat(CgroupV2Subsystem::lineToRandWIOs); } + @Override public long getBlkIOServiced() { - return CgroupSubsystemController.RETVAL_NOT_SUPPORTED; + return sumTokensIOStat(CgroupV2Subsystem::lineToRBytesAndWBytesIO); + } + + private long sumTokensIOStat(Function mapFunc) { + try { + return CgroupUtil.readFilePrivileged(Paths.get(unified.path(), "io.stat")) + .map(mapFunc) + .collect(Collectors.summingLong(e -> e)); + } catch (IOException e) { + return Metrics.LONG_RETVAL_NOT_SUPPORTED; + } + } + + private static String[] getRWIOMatchTokenNames() { + return new String[] { "rios", "wios" }; + } + + private static String[] getRWBytesIOMatchTokenNames() { + return new String[] { "rbytes", "wbytes" }; + } + + public static Long lineToRandWIOs(String line) { + String[] matchNames = getRWIOMatchTokenNames(); + return ioStatLineToLong(line, matchNames); + } + + public static Long lineToRBytesAndWBytesIO(String line) { + String[] matchNames = getRWBytesIOMatchTokenNames(); + return ioStatLineToLong(line, matchNames); + } + + private static Long ioStatLineToLong(String line, String[] matchNames) { + if (line == null || EMPTY_STR.equals(line)) { + return Long.valueOf(0); + } + String[] tokens = line.split("\\s+"); + long retval = 0; + for (String t: tokens) { + String[] valKeys = t.split("="); + if (valKeys.length != 2) { + // ignore device ids $MAJ:$MIN + continue; + } + for (String match: matchNames) { + if (match.equals(valKeys[0])) { + retval += longOrZero(valKeys[1]); + } + } + } + return Long.valueOf(retval); + } + + private static long longOrZero(String val) { + long lVal = 0; + try { + lVal = Long.parseLong(val); + } catch (NumberFormatException e) { + // keep at 0 + } + return lVal; } } --- old/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2SubsystemController.java 2020-01-09 20:39:16.749143043 +0100 +++ new/src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2SubsystemController.java 2020-01-09 20:39:16.613142900 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Red Hat Inc. + * 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 @@ -28,6 +28,7 @@ import java.nio.file.Paths; import jdk.internal.platform.CgroupSubsystemController; +import jdk.internal.platform.Metrics; public class CgroupV2SubsystemController implements CgroupSubsystemController { @@ -43,6 +44,6 @@ } public static long convertStringToLong(String strval) { - return CgroupSubsystemController.convertStringToLong(strval, CgroupSubsystemController.RETVAL_UNLIMITED); + return CgroupSubsystemController.convertStringToLong(strval, Metrics.LONG_RETVAL_UNLIMITED); } } --- old/src/java.base/share/classes/jdk/internal/platform/Metrics.java 2020-01-09 20:39:17.433143760 +0100 +++ new/src/java.base/share/classes/jdk/internal/platform/Metrics.java 2020-01-09 20:39:17.294143614 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -50,6 +50,23 @@ 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. @@ -72,7 +89,7 @@ * * @implNote * Metrics are currently only supported Linux. - * The provider for Linux is cgroupsv1. + * The provider for Linux is cgroups (version 1 or 2). * * @return The name of the provider. * @@ -88,7 +105,7 @@ * Returns the aggregate time, in nanoseconds, consumed by all * tasks in the Isolation Group. * - * @return Time in nanoseconds or 0L if metric is not available. + * @return Time in nanoseconds or -2 if metric is not available. * */ public long getCpuUsage(); @@ -104,7 +121,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, a zero length array will be + * this metric is not available, null will be * returned. * */ @@ -114,7 +131,7 @@ * Returns the aggregate user time, in nanoseconds, consumed by all * tasks in the Isolation Group. * - * @return User time in nanoseconds or 0L if metric is not available. + * @return User time in nanoseconds or -2 if metric is not available. * */ public long getCpuUserUsage(); @@ -123,7 +140,7 @@ * Returns the aggregate system time, in nanoseconds, consumed by * all tasks in the Isolation Group. * - * @return System time in nanoseconds or 0L if metric is not available. + * @return System time in nanoseconds or -2 if metric is not available. * */ public long getCpuSystemUsage(); @@ -136,7 +153,7 @@ * Returns the length of the scheduling period, in * microseconds, for processes within the Isolation Group. * - * @return time in microseconds or 0L if metric is not available. + * @return time in microseconds or -2 if metric is not available. * */ public long getCpuPeriod(); @@ -146,7 +163,7 @@ * during each scheduling period for all tasks in the Isolation * Group. * - * @return time in microseconds or -1 if the quota is unlimited. + * @return time in microseconds or -2 if not supported. * */ public long getCpuQuota(); @@ -165,7 +182,7 @@ * each process. To request 2 CPUS worth of execution time, CPU shares * would be set to 2048. * - * @return shares value or -1 if no share set. + * @return shares value or -2 if no share set. * */ public long getCpuShares(); @@ -175,7 +192,7 @@ * a CPU quota has been setup for the Isolation Group; otherwise * returns 0. * - * @return count of elapsed periods or 0 if the quota is unlimited. + * @return count of elapsed periods or -2 if not supported. * */ public long getCpuNumPeriods(); @@ -185,7 +202,7 @@ * 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 0 if the quota is unlimited. + * @return count of throttled periods or -2 if not supported. * */ public long getCpuNumThrottled(); @@ -195,7 +212,7 @@ * 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 0 if the quota is unlimited. + * @return Throttled time in nanoseconds or -2 if not supported. * */ public long getCpuThrottledTime(); @@ -227,7 +244,7 @@ * may be offline. To get the current online CPUs, use * {@link getEffectiveCpuSetCpus()}. * - * @return An array of available CPUs or a zero length array + * @return An array of available CPUs or null * if the metric is not available. * */ @@ -239,8 +256,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 a zero length - * array if the metric is not available. + * @return An array of available and online CPUs or null + * if the metric is not available. * */ public int[] getEffectiveCpuSetCpus(); @@ -253,7 +270,7 @@ * may be offline. To get the current online memory nodes, use * {@link getEffectiveCpuSetMems()}. * - * @return An array of available memory nodes or a zero length array + * @return An array of available memory nodes or null * if the metric is not available. * */ @@ -265,8 +282,8 @@ * array is equal to the total number of nodes and the elements in * the array are the physical node numbers. * - * @return An array of available and online nodes or a zero length - * array if the metric is not available. + * @return An array of available and online nodes or null + * if the metric is not available. * */ public int[] getEffectiveCpuSetMems(); @@ -278,7 +295,7 @@ * readily available. Use {@link #isCpuSetMemoryPressureEnabled()} to * determine if this support is enabled. * - * @return Memory pressure or 0 if not enabled or metric is not + * @return Memory pressure or 0 if not enabled or -2 if metric is not * available. * */ @@ -287,10 +304,11 @@ /** * Returns the state of the memory pressure detection support. * - * @return true if the support is available and enabled, otherwise false. + * @return true if support is available and enabled. null if metric is + * not available. false otherwise. * */ - public boolean isCpuSetMemoryPressureEnabled(); + public Boolean isCpuSetMemoryPressureEnabled(); /***************************************************************** * Memory Subsystem @@ -300,7 +318,7 @@ * 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 0 if none or metric + * @return The number of exceeded requests or -2 if metric * is not available. * */ @@ -310,8 +328,8 @@ * Returns the maximum amount of physical memory, in bytes, that * can be allocated in the Isolation Group. * - * @return The maximum amount of memory in bytes or -1 if either - * there is no limit set or this metric is not available. + * @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 getMemoryLimit(); @@ -320,7 +338,7 @@ * 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 0 if this + * @return The largest amount of memory in bytes or -2 if this * metric is not available. * */ @@ -330,7 +348,7 @@ * 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 0 if this + * @return The amount of memory in bytes allocated or -2 if this * metric is not available. * */ @@ -340,7 +358,7 @@ * 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 0 if none or metric + * @return The number of exceeded requests or -2 if metric * is not available. * */ @@ -350,8 +368,8 @@ * 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 either - * there is no limit set or this metric is not available. + * @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(); @@ -360,7 +378,7 @@ * 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 0 if this + * @return The largest amount of memory in bytes or -2 if this * metric is not available. * */ @@ -370,7 +388,7 @@ * 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 0 if this + * @return The amount of memory in bytes allocated or -2 if this * metric is not available. * */ @@ -380,7 +398,7 @@ * 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 0 if none or metric + * @return The number of exceeded requests or -2 if the metric * is not available. * */ @@ -390,8 +408,8 @@ * 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 either - * there is no limit set or this metric is not available. + * @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(); @@ -400,7 +418,7 @@ * 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 0 if this + * @return The largest amount of memory in bytes or -2 if this * metric is not available. * */ @@ -410,7 +428,7 @@ * 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 0 if this + * @return The amount of memory in bytes allocated or -2 if this * metric is not available. * */ @@ -420,7 +438,7 @@ * 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 0 if none or metric + * @return The number of exceeded requests or -2 if the metric * is not available. * */ @@ -430,8 +448,8 @@ * 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 either - * there is no limit set or this metric is not available. + * @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 getMemoryAndSwapLimit(); @@ -440,7 +458,7 @@ * 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 0 if this + * @return The largest amount of memory in bytes or -2 if this * metric is not available. * */ @@ -450,7 +468,7 @@ * 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 0 if this + * @return The amount of memory in bytes allocated or -2 if this * metric is not available. * */ @@ -462,11 +480,11 @@ * * @return Returns true if operating system will terminate processes * in the Isolation Group that exceed the amount of available - * memory, otherwise false. Flase will be returned if this + * memory, otherwise false. null will be returned if this * capability is not available on the current operating system. * */ - public boolean isMemoryOOMKillEnabled(); + public Boolean isMemoryOOMKillEnabled(); /** * Returns the hint to the operating system that allows groups @@ -476,7 +494,7 @@ * * @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, 0 will be + * conditions. If this metric is not available, -2 will be * returned. * */ @@ -490,7 +508,7 @@ * 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 0 if this metric is not available. + * @return The count of requests or -2 if this metric is not available. * */ public long getBlkIOServiceCount(); @@ -499,7 +517,7 @@ * 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 0 if this metric is not available. + * @return The number of bytes transferred or -2 if this metric is not available. * */ public long getBlkIOServiced(); --- old/src/java.base/share/classes/sun/launcher/LauncherHelper.java 2020-01-09 20:39:18.146144508 +0100 +++ new/src/java.base/share/classes/sun/launcher/LauncherHelper.java 2020-01-09 20:39:18.010144365 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 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 @@ -45,10 +45,10 @@ import java.io.UnsupportedEncodingException; import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; -import java.lang.module.ModuleDescriptor.Requires; import java.lang.module.ModuleDescriptor.Exports; import java.lang.module.ModuleDescriptor.Opens; import java.lang.module.ModuleDescriptor.Provides; +import java.lang.module.ModuleDescriptor.Requires; import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; import java.lang.module.ResolvedModule; @@ -62,8 +62,8 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; -import java.text.Normalizer; import java.text.MessageFormat; +import java.text.Normalizer; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -325,89 +325,126 @@ ostream.println(INDENT + "Provider: " + c.getProvider()); ostream.println(INDENT + "Effective CPU Count: " + c.getEffectiveCpuCount()); - ostream.println(INDENT + "CPU Period: " + c.getCpuPeriod() + - (c.getCpuPeriod() == -1 ? "" : "us")); - ostream.println(INDENT + "CPU Quota: " + c.getCpuQuota() + - (c.getCpuQuota() == -1 ? "" : "us")); - ostream.println(INDENT + "CPU Shares: " + c.getCpuShares()); + ostream.println(formatCpuVal(c.getCpuPeriod(), INDENT + "CPU Period: ")); + ostream.println(formatCpuVal(c.getCpuQuota(), INDENT + "CPU Quota: ")); + ostream.println(formatCpuVal(c.getCpuShares(), INDENT + "CPU Shares: ")); int cpus[] = c.getCpuSetCpus(); - ostream.println(INDENT + "List of Processors, " - + cpus.length + " total: "); - - ostream.print(INDENT); - for (int i = 0; i < cpus.length; i++) { - ostream.print(cpus[i] + " "); - } - if (cpus.length > 0) { - ostream.println(""); + if (cpus != null) { + ostream.println(INDENT + "List of Processors, " + + cpus.length + " total: "); + + ostream.print(INDENT); + for (int i = 0; i < cpus.length; i++) { + ostream.print(cpus[i] + " "); + } + if (cpus.length > 0) { + ostream.println(""); + } + } else { + ostream.println(INDENT + "List of Processors: N/A"); } cpus = c.getEffectiveCpuSetCpus(); - ostream.println(INDENT + "List of Effective Processors, " - + cpus.length + " total: "); - - ostream.print(INDENT); - for (int i = 0; i < cpus.length; i++) { - ostream.print(cpus[i] + " "); - } - if (cpus.length > 0) { - ostream.println(""); + if (cpus != null) { + ostream.println(INDENT + "List of Effective Processors, " + + cpus.length + " total: "); + + ostream.print(INDENT); + for (int i = 0; i < cpus.length; i++) { + ostream.print(cpus[i] + " "); + } + if (cpus.length > 0) { + ostream.println(""); + } + } else { + ostream.println(INDENT + "List of Effective Processors: N/A"); } int mems[] = c.getCpuSetMems(); - ostream.println(INDENT + "List of Memory Nodes, " - + mems.length + " total: "); - - ostream.print(INDENT); - for (int i = 0; i < mems.length; i++) { - ostream.print(mems[i] + " "); - } - if (mems.length > 0) { - ostream.println(""); + if (mems != null) { + ostream.println(INDENT + "List of Memory Nodes, " + + mems.length + " total: "); + + ostream.print(INDENT); + for (int i = 0; i < mems.length; i++) { + ostream.print(mems[i] + " "); + } + if (mems.length > 0) { + ostream.println(""); + } + } else { + ostream.println(INDENT + "List of Memory Nodes: N/A"); } mems = c.getEffectiveCpuSetMems(); - ostream.println(INDENT + "List of Available Memory Nodes, " - + mems.length + " total: "); - - ostream.print(INDENT); - for (int i = 0; i < mems.length; i++) { - ostream.print(mems[i] + " "); - } - if (mems.length > 0) { - ostream.println(""); + if (mems != null) { + ostream.println(INDENT + "List of Available Memory Nodes, " + + mems.length + " total: "); + + ostream.print(INDENT); + for (int i = 0; i < mems.length; i++) { + ostream.print(mems[i] + " "); + } + if (mems.length > 0) { + ostream.println(""); + } + } else { + ostream.println(INDENT + "List of Available Memory Nodes: N/A"); } - ostream.println(INDENT + "CPUSet Memory Pressure Enabled: " - + c.isCpuSetMemoryPressureEnabled()); + ostream.println(formatBoolean(c.isCpuSetMemoryPressureEnabled(), + INDENT + "CPUSet Memory Pressure Enabled: ")); long limit = c.getMemoryLimit(); - ostream.println(INDENT + "Memory Limit: " + - ((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited")); + ostream.println(formatLimitString(limit, INDENT + "Memory Limit: ")); limit = c.getMemorySoftLimit(); - ostream.println(INDENT + "Memory Soft Limit: " + - ((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited")); + ostream.println(formatLimitString(limit, INDENT + "Memory Soft Limit: ")); limit = c.getMemoryAndSwapLimit(); - ostream.println(INDENT + "Memory & Swap Limit: " + - ((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited")); + ostream.println(formatLimitString(limit, INDENT + "Memory & Swap Limit: ")); limit = c.getKernelMemoryLimit(); - ostream.println(INDENT + "Kernel Memory Limit: " + - ((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited")); + ostream.println(formatLimitString(limit, INDENT + "Kernel Memory Limit: ")); limit = c.getTcpMemoryLimit(); - ostream.println(INDENT + "TCP Memory Limit: " + - ((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited")); + ostream.println(formatLimitString(limit, INDENT + "TCP Memory Limit: ")); - ostream.println(INDENT + "Out Of Memory Killer Enabled: " - + c.isMemoryOOMKillEnabled()); + 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) { + return prefix + "N/A"; + } else { + return prefix + "Unlimited"; + } + } + + private static String formatCpuVal(long cpuVal, String prefix) { + if (cpuVal >= 0) { + return prefix + cpuVal + "us"; + } else if (cpuVal == Metrics.LONG_RETVAL_NOT_SUPPORTED) { + return prefix + "N/A"; + } else { + return prefix + cpuVal; + } + } + + private static String formatBoolean(Boolean value, String prefix) { + if (value == Metrics.BOOL_RETVAL_NOT_SUPPORTED) { + return prefix + "N/A"; + } else { + return prefix + value; + } + } + private enum SizePrefix { KILO(1024, "K"), --- old/src/jdk.management/unix/classes/com/sun/management/internal/OperatingSystemImpl.java 2020-01-09 20:39:18.853145249 +0100 +++ new/src/jdk.management/unix/classes/com/sun/management/internal/OperatingSystemImpl.java 2020-01-09 20:39:18.719145109 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -170,7 +170,7 @@ } private boolean isCpuSetSameAsHostCpuSet() { - if (containerMetrics != null) { + if (containerMetrics != null && containerMetrics.getCpuSetCpus() != null) { return containerMetrics.getCpuSetCpus().length == getHostConfiguredCpuCount0(); } return false; --- old/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java 2020-01-09 20:39:19.533145962 +0100 +++ new/test/jdk/jdk/internal/platform/docker/MetricsMemoryTester.java 2020-01-09 20:39:19.394145817 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -65,9 +65,11 @@ // Allocate 512M of data byte[][] bytes = new byte[64][]; + boolean atLeastOneAllocationWorked = false; for (int i = 0; i < 64; i++) { try { bytes[i] = new byte[8 * 1024 * 1024]; + atLeastOneAllocationWorked = true; // Break out as soon as we see an increase in failcount // to avoid getting killed by the OOM killer. if (Metrics.systemMetrics().getMemoryFailCount() > count) { @@ -77,6 +79,12 @@ break; } } + if (!atLeastOneAllocationWorked) { + System.out.println("Allocation failed immediately. Ignoring test!"); + return; + } + // Be sure bytes allocations don't get optimized out + System.out.println("DEBUG: Bytes allocation length 1: " + bytes[0].length); if (Metrics.systemMetrics().getMemoryFailCount() <= count) { throw new RuntimeException("Memory fail count : new : [" + Metrics.systemMetrics().getMemoryFailCount() + "]" --- old/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java 2020-01-09 20:39:20.215146677 +0100 +++ new/test/jdk/jdk/internal/platform/docker/TestDockerCpuMetrics.java 2020-01-09 20:39:20.077146533 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -78,10 +78,10 @@ int[] cpuSetMems = Metrics.systemMetrics().getCpuSetMems(); String memNodes = null; - if (cpuSetMems.length > 1) { + if (cpuSetMems != null && cpuSetMems.length > 1) { int endNode = (cpuSetMems[cpuSetMems.length - 1] - cpuSetMems[0]) / 2 + cpuSetMems[0]; memNodes = cpuSetMems[0] + "-" + endNode; - } else if (cpuSetMems.length == 1) { + } else if (cpuSetMems != null && cpuSetMems.length == 1) { memNodes = cpuSetMems[0] + ""; } --- old/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java 2020-01-09 20:39:20.892147387 +0100 +++ new/test/jdk/jdk/internal/platform/docker/TestDockerMemoryMetrics.java 2020-01-09 20:39:20.761147250 +0100 @@ -21,6 +21,7 @@ * questions. */ +import jdk.internal.platform.Metrics; import jdk.test.lib.Utils; import jdk.test.lib.containers.docker.Common; import jdk.test.lib.containers.docker.DockerRunOptions; @@ -58,10 +59,21 @@ testMemoryAndSwapLimit("200m", "1g"); testMemoryAndSwapLimit("100m", "200m"); - testKernelMemoryLimit("100m"); - testKernelMemoryLimit("1g"); - - testOomKillFlag("100m", false); + Metrics m = Metrics.systemMetrics(); + // kernel memory, '--kernel-memory' switch, and OOM killer, + // '--oom-kill-disable' switch, tests not supported by cgroupv2 + // runtimes + if (m != null) { + if ("cgroupv1".equals(m.getProvider())) { + testKernelMemoryLimit("100m"); + testKernelMemoryLimit("1g"); + + testOomKillFlag("100m", false); + } else { + System.out.println("kernel memory tests and OOM Kill flag tests not " + + "possible with cgroupv2."); + } + } testOomKillFlag("100m", true); testMemoryFailCount("64m"); @@ -133,11 +145,6 @@ "skipping the test case"); return; } - if (oa.getStderr().contains("cannot set kernel memory with cgroupv2")) { - System.out.println("Kernel memory settings not possible with cgroupv2, " + - "skipping the test case"); - return; - } oa.shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); } @@ -155,12 +162,6 @@ .addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED") .addClassOptions("memory", value, oomKillFlag + ""); OutputAnalyzer oa = DockerTestUtils.dockerRunJava(opts); - if (oa.getStderr().contains("cannot disable OOM killer with cgroupv2")) { - System.out.println("Disabling OOM killer not possible with cgroupv2, " + - "skipping the test case"); - return; - } - oa.shouldHaveExitValue(0).shouldContain("TEST PASSED!!!"); } --- old/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV1.java 2020-01-09 20:39:21.578148107 +0100 +++ new/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV1.java 2020-01-09 20:39:21.439147961 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -126,6 +126,9 @@ startUserVal = metrics.getCpuUserUsage(); startUsage = metrics.getCpuUsage(); startPerCpu = metrics.getPerCpuUsage(); + if (startPerCpu == null) { + startPerCpu = new long[0]; + } try { Stream lines = Files.lines(Paths.get("/proc/self/mountinfo")); @@ -149,13 +152,13 @@ return new Scanner(new File(fname)).useDelimiter("\\Z").next(); } catch (FileNotFoundException e) { System.err.println("Unable to open : " + fname); - return ""; + return null; } } private static long getLongValueFromFile(Controller subSystem, String fileName) { String data = getFileContents(subSystem, fileName); - return data.isEmpty() ? 0L : convertStringToLong(data); + return (data == null || data.isEmpty()) ? 0L : convertStringToLong(data); } private static long convertStringToLong(String strval) { @@ -332,11 +335,17 @@ warn(Controller.CPUACCT, "cpuacct.usage", oldVal, newVal); } - Long[] newVals = Stream.of(getFileContents(Controller.CPUACCT, "cpuacct.usage_percpu") + String newValsStr = getFileContents(Controller.CPUACCT, "cpuacct.usage_percpu"); + Long[] newVals = new Long[0]; + if (newValsStr != null) { + newVals = Stream.of(newValsStr .split("\\s+")) .map(Long::parseLong) .toArray(Long[]::new); - Long[] oldVals = LongStream.of(metrics.getPerCpuUsage()).boxed().toArray(Long[]::new); + } + long[] oldValsPrim = metrics.getPerCpuUsage(); + Long[] oldVals = LongStream.of(oldValsPrim == null ? new long[0] : oldValsPrim) + .boxed().toArray(Long[]::new); for (int i = 0; i < oldVals.length; i++) { if (!CgroupMetricsTester.compareWithErrorMargin(oldVals[i], newVals[i])) { warn(Controller.CPUACCT, "cpuacct.usage_percpu", oldVals[i], newVals[i]); @@ -488,6 +497,9 @@ long newUserVal = metrics.getCpuUserUsage(); long newUsage = metrics.getCpuUsage(); long[] newPerCpu = metrics.getPerCpuUsage(); + if (newPerCpu == null) { + newPerCpu = new long[0]; + } // system/user CPU usage counters may be slowly increasing. // allow for equal values for a pass --- old/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV2.java 2020-01-09 20:39:22.272148835 +0100 +++ new/test/lib/jdk/test/lib/containers/cgroup/MetricsTesterCgroupV2.java 2020-01-09 20:39:22.131148687 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Red Hat Inc. + * 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 @@ -149,10 +149,6 @@ CgroupMetricsTester.fail(UnifiedController.NAME, metric, oldVal, newVal); } - private void fail(String metric, String message) { - throw new RuntimeException(metric + ": " + message); - } - private void warn(String metric, long oldVal, long newVal) { CgroupMetricsTester.warn(UnifiedController.NAME, metric, oldVal, newVal); } @@ -169,9 +165,7 @@ } private void verifyPerCpuNotSupported(long[] perCpuUsage) { - if (!Arrays.equals(perCpuUsage, new long[0])) { - throw new RuntimeException("perCpuUsage expected to be not supported"); - }; + Asserts.assertNull(perCpuUsage, "perCpuUsage expected to be not supported"); } private long getCpuShares(String file) { @@ -212,6 +206,9 @@ private long getCpuValueFromFile(String file, int index) { String maxPeriod = getStringVal(file); + if (maxPeriod == null) { + return UNLIMITED; + } String[] tokens = maxPeriod.split("\\s+"); String val = tokens[index]; if (MAX.equals(val)) { @@ -228,7 +225,6 @@ public void testMemorySubsystem() { Metrics metrics = Metrics.systemMetrics(); - long memoryMaxUsageMin; // User Memory long oldVal = metrics.getMemoryFailCount(); long newVal = getLongValueEntryFromFile("memory.events", "max"); @@ -242,20 +238,12 @@ fail("memory.max", oldVal, newVal); } - memoryMaxUsageMin = oldVal = metrics.getMemoryUsage(); + oldVal = metrics.getMemoryUsage(); newVal = getLongValueFromFile("memory.current"); if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) { fail("memory.current", oldVal, newVal); } - - // Memory max has no knowledge of out of band read to "memory.current" - // above. Fix that by calling it again. - metrics.getMemoryUsage(); - long memoryMax = metrics.getMemoryMaxUsage(); - if (memoryMax < memoryMaxUsageMin || memoryMax < newVal) { - fail("memoryMaxUsage", "old usages [ " + memoryMaxUsageMin + ", " + - newVal + " ] >= " + memoryMax); - } + verifyNotSupported(metrics.getMemoryMaxUsage()); // Kernel memory verifyNotSupported(metrics.getKernelMemoryFailCount()); @@ -267,7 +255,12 @@ verifyNotSupported(metrics.getTcpMemoryFailCount()); verifyNotSupported(metrics.getTcpMemoryLimit()); verifyNotSupported(metrics.getTcpMemoryMaxUsage()); - verifyNotSupported(metrics.getTcpMemoryUsage()); + + oldVal = metrics.getTcpMemoryUsage(); + newVal = getLongValueEntryFromFile("memory.stat", "sock"); + if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) { + fail("memory.stat[sock]", oldVal, newVal); + } // Memory and Swap verifyNotSupported(metrics.getMemoryAndSwapFailCount()); @@ -291,7 +284,7 @@ fail("memory.high", oldVal, newVal); } - Asserts.assertFalse(metrics.isMemoryOOMKillEnabled(), "Not supported"); + Asserts.assertNull(metrics.isMemoryOOMKillEnabled(), "Not supported"); } @Override @@ -362,7 +355,8 @@ @Override public void testCpuSets() { Metrics metrics = Metrics.systemMetrics(); - Integer[] oldVal = Arrays.stream(metrics.getCpuSetCpus()).boxed().toArray(Integer[]::new); + int[] cpus = mapNullToEmpty(metrics.getCpuSetCpus()); + Integer[] oldVal = Arrays.stream(cpus).boxed().toArray(Integer[]::new); Arrays.sort(oldVal); String cpusstr = getStringVal("cpuset.cpus"); @@ -374,7 +368,8 @@ Arrays.toString(newVal)); } - oldVal = Arrays.stream(metrics.getEffectiveCpuSetCpus()).boxed().toArray(Integer[]::new); + cpus = mapNullToEmpty(metrics.getEffectiveCpuSetCpus()); + oldVal = Arrays.stream(cpus).boxed().toArray(Integer[]::new); Arrays.sort(oldVal); cpusstr = getStringVal("cpuset.cpus.effective"); newVal = CgroupMetricsTester.convertCpuSetsToArray(cpusstr); @@ -384,7 +379,8 @@ Arrays.toString(newVal)); } - oldVal = Arrays.stream(metrics.getCpuSetMems()).boxed().toArray(Integer[]::new); + cpus = mapNullToEmpty(metrics.getCpuSetMems()); + oldVal = Arrays.stream(cpus).boxed().toArray(Integer[]::new); Arrays.sort(oldVal); cpusstr = getStringVal("cpuset.mems"); newVal = CgroupMetricsTester.convertCpuSetsToArray(cpusstr); @@ -394,7 +390,8 @@ Arrays.toString(newVal)); } - oldVal = Arrays.stream(metrics.getEffectiveCpuSetMems()).boxed().toArray(Integer[]::new); + cpus = mapNullToEmpty(metrics.getEffectiveCpuSetMems()); + oldVal = Arrays.stream(cpus).boxed().toArray(Integer[]::new); Arrays.sort(oldVal); cpusstr = getStringVal("cpuset.mems.effective"); newVal = CgroupMetricsTester.convertCpuSetsToArray(cpusstr); @@ -405,7 +402,16 @@ } verifyNotSupported(metrics.getCpuSetMemoryPressure()); - Asserts.assertFalse(metrics.isCpuSetMemoryPressureEnabled(), "Should be not supported"); + Asserts.assertNull(metrics.isCpuSetMemoryPressureEnabled(), "Should be not supported"); + } + + private int[] mapNullToEmpty(int[] cpus) { + if (cpus == null) { + // Not available. For sake of testing continue with an + // empty array. + cpus = new int[0]; + } + return cpus; } @Override @@ -467,7 +473,45 @@ @Override public void testMisc() { - // nothing for cgroups v2 + testIOStat(); } + private void testIOStat() { + Metrics metrics = Metrics.systemMetrics(); + long oldVal = metrics.getBlkIOServiceCount(); + long newVal = getIoStatAccumulate(new String[] { "rios", "wios" }); + if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) { + fail("io.stat->rios/wios: ", oldVal, newVal); + } + + oldVal = metrics.getBlkIOServiced(); + newVal = getIoStatAccumulate(new String[] { "rbytes", "wbytes" }); + if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) { + fail("io.stat->rbytes/wbytes: ", oldVal, newVal); + } + } + + private long getIoStatAccumulate(String[] matchNames) { + try { + return Files.lines(Paths.get(UNIFIED.getPath(), "io.stat")) + .map(line -> { + long accumulator = 0; + String[] tokens = line.split("\\s+"); + for (String t: tokens) { + String[] keyVal = t.split("="); + if (keyVal.length != 2) { + continue; + } + for (String match: matchNames) { + if (match.equals(keyVal[0])) { + accumulator += Long.parseLong(keyVal[1]); + } + } + } + return accumulator; + }).collect(Collectors.summingLong(e -> e)); + } catch (IOException e) { + return Metrics.LONG_RETVAL_NOT_SUPPORTED; + } + } }