mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8231111: Cgroups v2: Rework Metrics in java.base so as to recognize unified hierarchy
Reviewed-by: bobv, mchung
This commit is contained in:
parent
8827df9b2d
commit
4def210a22
28 changed files with 3283 additions and 1278 deletions
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* 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. 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data structure to hold info from /proc/self/cgroup
|
||||||
|
*
|
||||||
|
* man 7 cgroups
|
||||||
|
*
|
||||||
|
* @see CgroupSubsystemFactory
|
||||||
|
*/
|
||||||
|
class CgroupInfo {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final int hierarchyId;
|
||||||
|
private final boolean enabled;
|
||||||
|
|
||||||
|
private CgroupInfo(String name, int hierarchyId, boolean enabled) {
|
||||||
|
this.name = name;
|
||||||
|
this.hierarchyId = hierarchyId;
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getHierarchyId() {
|
||||||
|
return hierarchyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CgroupInfo fromCgroupsLine(String line) {
|
||||||
|
String[] tokens = line.split("\\s+");
|
||||||
|
if (tokens.length != 4) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// discard 3'rd field, num_cgroups
|
||||||
|
return new CgroupInfo(tokens[0] /* name */,
|
||||||
|
Integer.parseInt(tokens[1]) /* hierarchyId */,
|
||||||
|
(Integer.parseInt(tokens[3]) == 1) /* enabled */);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
/*
|
||||||
|
* 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. 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;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class CgroupMetrics implements Metrics {
|
||||||
|
|
||||||
|
private final CgroupSubsystem subsystem;
|
||||||
|
|
||||||
|
CgroupMetrics(CgroupSubsystem subsystem) {
|
||||||
|
this.subsystem = Objects.requireNonNull(subsystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProvider() {
|
||||||
|
return subsystem.getProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuUsage() {
|
||||||
|
return subsystem.getCpuUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long[] getPerCpuUsage() {
|
||||||
|
return subsystem.getPerCpuUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuUserUsage() {
|
||||||
|
return subsystem.getCpuUserUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuSystemUsage() {
|
||||||
|
return subsystem.getCpuSystemUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuPeriod() {
|
||||||
|
return subsystem.getCpuPeriod();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuQuota() {
|
||||||
|
return subsystem.getCpuQuota();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuShares() {
|
||||||
|
return subsystem.getCpuShares();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuNumPeriods() {
|
||||||
|
return subsystem.getCpuNumPeriods();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuNumThrottled() {
|
||||||
|
return subsystem.getCpuNumThrottled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuThrottledTime() {
|
||||||
|
return subsystem.getCpuThrottledTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEffectiveCpuCount() {
|
||||||
|
return subsystem.getEffectiveCpuCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getCpuSetCpus() {
|
||||||
|
return subsystem.getCpuSetCpus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getEffectiveCpuSetCpus() {
|
||||||
|
return subsystem.getEffectiveCpuSetCpus();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getCpuSetMems() {
|
||||||
|
return subsystem.getCpuSetMems();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getEffectiveCpuSetMems() {
|
||||||
|
return subsystem.getEffectiveCpuSetMems();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMemoryFailCount() {
|
||||||
|
return subsystem.getMemoryFailCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMemoryLimit() {
|
||||||
|
return subsystem.getMemoryLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMemoryUsage() {
|
||||||
|
return subsystem.getMemoryUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTcpMemoryUsage() {
|
||||||
|
return subsystem.getTcpMemoryUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMemoryAndSwapLimit() {
|
||||||
|
return subsystem.getMemoryAndSwapLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMemoryAndSwapUsage() {
|
||||||
|
return subsystem.getMemoryAndSwapUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMemorySoftLimit() {
|
||||||
|
return subsystem.getMemorySoftLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getBlkIOServiceCount() {
|
||||||
|
return subsystem.getBlkIOServiceCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getBlkIOServiced() {
|
||||||
|
return subsystem.getBlkIOServiced();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Metrics getInstance() {
|
||||||
|
return CgroupSubsystemFactory.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* 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. 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marker interface for cgroup-based metrics
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,253 @@
|
||||||
|
/*
|
||||||
|
* 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. 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;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cgroup version agnostic controller logic
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface CgroupSubsystemController {
|
||||||
|
|
||||||
|
public static final String EMPTY_STR = "";
|
||||||
|
|
||||||
|
public String path();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getStringValue
|
||||||
|
*
|
||||||
|
* Return the first line of the file "param" argument from the controller.
|
||||||
|
*
|
||||||
|
* TODO: Consider using weak references for caching BufferedReader object.
|
||||||
|
*
|
||||||
|
* @param controller
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return CgroupUtil.readStringValue(controller, param);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<String, Long> conversion,
|
||||||
|
long defaultRetval) {
|
||||||
|
long retval = defaultRetval;
|
||||||
|
if (controller == null) {
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Path filePath = Paths.get(controller.path(), param);
|
||||||
|
List<String> lines = CgroupUtil.readAllLinesPrivileged(filePath);
|
||||||
|
for (String line : lines) {
|
||||||
|
if (line.startsWith(match)) {
|
||||||
|
retval = conversion.apply(line);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Ignore. Default is unlimited.
|
||||||
|
}
|
||||||
|
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 param,
|
||||||
|
Function<String, Long> conversion,
|
||||||
|
long defaultRetval) {
|
||||||
|
String strval = getStringValue(controller, param);
|
||||||
|
if (strval == null) return defaultRetval;
|
||||||
|
return conversion.apply(strval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 defaultRetval;
|
||||||
|
|
||||||
|
double retval = Double.parseDouble(strval);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getLongEntry
|
||||||
|
*
|
||||||
|
* Return the long value from the line containing the string "entryname"
|
||||||
|
* within file "param" in the "controller".
|
||||||
|
*
|
||||||
|
* TODO: Consider using weak references for caching BufferedReader object.
|
||||||
|
*
|
||||||
|
* @param controller
|
||||||
|
* @param param
|
||||||
|
* @param entryname
|
||||||
|
* @return long value or "defaultRetval" if there was an error or no match
|
||||||
|
* was found.
|
||||||
|
*/
|
||||||
|
public static long getLongEntry(CgroupSubsystemController controller, String param, String entryname, long defaultRetval) {
|
||||||
|
if (controller == null) return defaultRetval;
|
||||||
|
|
||||||
|
try (Stream<String> lines = CgroupUtil.readFilePrivileged(Paths.get(controller.path(), param))) {
|
||||||
|
|
||||||
|
Optional<String> result = lines.map(line -> line.split(" "))
|
||||||
|
.filter(line -> (line.length == 2 &&
|
||||||
|
line[0].equals(entryname)))
|
||||||
|
.map(line -> line[1])
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
|
return result.isPresent() ? Long.parseLong(result.get()) : defaultRetval;
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
return defaultRetval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 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;
|
||||||
|
|
||||||
|
ArrayList<Integer> results = new ArrayList<>();
|
||||||
|
String strs[] = range.split(",");
|
||||||
|
for (String str : strs) {
|
||||||
|
if (str.contains("-")) {
|
||||||
|
String lohi[] = str.split("-");
|
||||||
|
// validate format
|
||||||
|
if (lohi.length != 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int lo = Integer.parseInt(lohi[0]);
|
||||||
|
int hi = Integer.parseInt(lohi[1]);
|
||||||
|
for (int i = lo; i <= hi; i++) {
|
||||||
|
results.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
results.add(Integer.parseInt(str));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort results
|
||||||
|
results.sort(null);
|
||||||
|
|
||||||
|
// convert ArrayList to primitive int array
|
||||||
|
int[] ints = new int[results.size()];
|
||||||
|
int i = 0;
|
||||||
|
for (Integer n : results) {
|
||||||
|
ints[i++] = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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, 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* 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. 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import jdk.internal.platform.cgroupv1.CgroupV1Subsystem;
|
||||||
|
import jdk.internal.platform.cgroupv2.CgroupV2Subsystem;
|
||||||
|
|
||||||
|
class CgroupSubsystemFactory {
|
||||||
|
|
||||||
|
private static final String CPU_CTRL = "cpu";
|
||||||
|
private static final String CPUACCT_CTRL = "cpuacct";
|
||||||
|
private static final String CPUSET_CTRL = "cpuset";
|
||||||
|
private static final String BLKIO_CTRL = "blkio";
|
||||||
|
private static final String MEMORY_CTRL = "memory";
|
||||||
|
|
||||||
|
static CgroupMetrics create() {
|
||||||
|
Map<String, CgroupInfo> infos = new HashMap<>();
|
||||||
|
try {
|
||||||
|
List<String> lines = CgroupUtil.readAllLinesPrivileged(Paths.get("/proc/cgroups"));
|
||||||
|
for (String line : lines) {
|
||||||
|
if (line.startsWith("#")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
CgroupInfo info = CgroupInfo.fromCgroupsLine(line);
|
||||||
|
switch (info.getName()) {
|
||||||
|
case CPU_CTRL: infos.put(CPU_CTRL, info); break;
|
||||||
|
case CPUACCT_CTRL: infos.put(CPUACCT_CTRL, info); break;
|
||||||
|
case CPUSET_CTRL: infos.put(CPUSET_CTRL, info); break;
|
||||||
|
case MEMORY_CTRL: infos.put(MEMORY_CTRL, info); break;
|
||||||
|
case BLKIO_CTRL: infos.put(BLKIO_CTRL, info); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no controller is enabled, return no metrics.
|
||||||
|
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) {
|
||||||
|
Logger logger = System.getLogger("jdk.internal.platform");
|
||||||
|
logger.log(Level.DEBUG, "Mixed cgroupv1 and cgroupv2 not supported. Metrics disabled.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCgroupsV2) {
|
||||||
|
CgroupSubsystem subsystem = CgroupV2Subsystem.getInstance();
|
||||||
|
return subsystem != null ? new CgroupMetrics(subsystem) : null;
|
||||||
|
} else {
|
||||||
|
CgroupV1Subsystem subsystem = CgroupV1Subsystem.getInstance();
|
||||||
|
return subsystem != null ? new CgroupV1MetricsImpl(subsystem) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* 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. 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;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedActionException;
|
||||||
|
import java.security.PrivilegedExceptionAction;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public final class CgroupUtil {
|
||||||
|
|
||||||
|
public static Stream<String> readFilePrivileged(Path path) throws IOException {
|
||||||
|
try {
|
||||||
|
PrivilegedExceptionAction<Stream<String>> pea = () -> Files.lines(path);
|
||||||
|
return AccessController.doPrivileged(pea);
|
||||||
|
} catch (PrivilegedActionException e) {
|
||||||
|
unwrapIOExceptionAndRethrow(e);
|
||||||
|
throw new InternalError(e.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unwrapIOExceptionAndRethrow(PrivilegedActionException pae) throws IOException {
|
||||||
|
Throwable x = pae.getCause();
|
||||||
|
if (x instanceof IOException)
|
||||||
|
throw (IOException) x;
|
||||||
|
if (x instanceof RuntimeException)
|
||||||
|
throw (RuntimeException) x;
|
||||||
|
if (x instanceof Error)
|
||||||
|
throw (Error) x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String readStringValue(CgroupSubsystemController controller, String param) throws IOException {
|
||||||
|
PrivilegedExceptionAction<BufferedReader> pea = () ->
|
||||||
|
Files.newBufferedReader(Paths.get(controller.path(), param));
|
||||||
|
try (BufferedReader bufferedReader =
|
||||||
|
AccessController.doPrivileged(pea)) {
|
||||||
|
String line = bufferedReader.readLine();
|
||||||
|
return line;
|
||||||
|
} catch (PrivilegedActionException e) {
|
||||||
|
unwrapIOExceptionAndRethrow(e);
|
||||||
|
throw new InternalError(e.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> readAllLinesPrivileged(Path path) throws IOException {
|
||||||
|
try {
|
||||||
|
PrivilegedExceptionAction<List<String>> pea = () -> Files.readAllLines(path);
|
||||||
|
return AccessController.doPrivileged(pea);
|
||||||
|
} catch (PrivilegedActionException e) {
|
||||||
|
unwrapIOExceptionAndRethrow(e);
|
||||||
|
throw new InternalError(e.getCause());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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. Linux, only.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface CgroupV1Metrics 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();
|
||||||
|
}
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* 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. 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 Metrics extensions
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class CgroupV1MetricsImpl extends CgroupMetrics implements CgroupV1Metrics {
|
||||||
|
|
||||||
|
private final CgroupV1Metrics metrics;
|
||||||
|
|
||||||
|
CgroupV1MetricsImpl(CgroupV1Metrics metrics) {
|
||||||
|
super((CgroupSubsystem)metrics);
|
||||||
|
this.metrics = metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMemoryMaxUsage() {
|
||||||
|
return metrics.getMemoryMaxUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getKernelMemoryFailCount() {
|
||||||
|
return metrics.getKernelMemoryFailCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getKernelMemoryLimit() {
|
||||||
|
return metrics.getKernelMemoryLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getKernelMemoryMaxUsage() {
|
||||||
|
return metrics.getKernelMemoryMaxUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getKernelMemoryUsage() {
|
||||||
|
return metrics.getKernelMemoryUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTcpMemoryFailCount() {
|
||||||
|
return metrics.getTcpMemoryFailCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTcpMemoryLimit() {
|
||||||
|
return metrics.getTcpMemoryLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTcpMemoryMaxUsage() {
|
||||||
|
return metrics.getTcpMemoryMaxUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMemoryAndSwapFailCount() {
|
||||||
|
return metrics.getMemoryAndSwapFailCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMemoryAndSwapMaxUsage() {
|
||||||
|
return metrics.getMemoryAndSwapMaxUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean isMemoryOOMKillEnabled() {
|
||||||
|
return metrics.isMemoryOOMKillEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getCpuSetMemoryPressure() {
|
||||||
|
return metrics.getCpuSetMemoryPressure();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean isCpuSetMemoryPressureEnabled() {
|
||||||
|
return metrics.isCpuSetMemoryPressureEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* 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. 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.cgroupv1;
|
||||||
|
|
||||||
|
public class CgroupV1MemorySubSystemController extends CgroupV1SubsystemController {
|
||||||
|
|
||||||
|
private boolean hierarchical;
|
||||||
|
|
||||||
|
public CgroupV1MemorySubSystemController(String root, String mountPoint) {
|
||||||
|
super(root, mountPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isHierarchical() {
|
||||||
|
return hierarchical;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHierarchical(boolean hierarchical) {
|
||||||
|
this.hierarchical = hierarchical;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -26,41 +26,37 @@
|
||||||
package jdk.internal.platform.cgroupv1;
|
package jdk.internal.platform.cgroupv1;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.PrivilegedActionException;
|
|
||||||
import java.security.PrivilegedExceptionAction;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import jdk.internal.platform.cgroupv1.SubSystem.MemorySubSystem;
|
import jdk.internal.platform.CgroupSubsystem;
|
||||||
|
import jdk.internal.platform.CgroupSubsystemController;
|
||||||
|
import jdk.internal.platform.CgroupUtil;
|
||||||
|
import jdk.internal.platform.CgroupV1Metrics;
|
||||||
|
|
||||||
public class Metrics implements jdk.internal.platform.Metrics {
|
public class CgroupV1Subsystem implements CgroupSubsystem, CgroupV1Metrics {
|
||||||
private MemorySubSystem memory;
|
private CgroupV1MemorySubSystemController memory;
|
||||||
private SubSystem cpu;
|
private CgroupV1SubsystemController cpu;
|
||||||
private SubSystem cpuacct;
|
private CgroupV1SubsystemController cpuacct;
|
||||||
private SubSystem cpuset;
|
private CgroupV1SubsystemController cpuset;
|
||||||
private SubSystem blkio;
|
private CgroupV1SubsystemController blkio;
|
||||||
private boolean activeSubSystems;
|
private boolean activeSubSystems;
|
||||||
|
|
||||||
// Values returned larger than this number are unlimited.
|
private static final CgroupV1Subsystem INSTANCE = initSubSystem();
|
||||||
static long unlimited_minimum = 0x7FFFFFFFFF000000L;
|
|
||||||
|
|
||||||
private static final Metrics INSTANCE = initContainerSubSystems();
|
|
||||||
|
|
||||||
private static final String PROVIDER_NAME = "cgroupv1";
|
private static final String PROVIDER_NAME = "cgroupv1";
|
||||||
|
|
||||||
private Metrics() {
|
private CgroupV1Subsystem() {
|
||||||
activeSubSystems = false;
|
activeSubSystems = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Metrics getInstance() {
|
public static CgroupV1Subsystem getInstance() {
|
||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Metrics initContainerSubSystems() {
|
private static CgroupV1Subsystem initSubSystem() {
|
||||||
Metrics metrics = new Metrics();
|
CgroupV1Subsystem subsystem = new CgroupV1Subsystem();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the cgroup mount points for subsystems
|
* Find the cgroup mount points for subsystems
|
||||||
|
@ -73,11 +69,11 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
||||||
* 34 28 0:29 / /sys/fs/cgroup/MemorySubSystem rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,MemorySubSystem
|
* 34 28 0:29 / /sys/fs/cgroup/MemorySubSystem rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,MemorySubSystem
|
||||||
*/
|
*/
|
||||||
try (Stream<String> lines =
|
try (Stream<String> lines =
|
||||||
readFilePrivileged(Paths.get("/proc/self/mountinfo"))) {
|
CgroupUtil.readFilePrivileged(Paths.get("/proc/self/mountinfo"))) {
|
||||||
|
|
||||||
lines.filter(line -> line.contains(" - cgroup "))
|
lines.filter(line -> line.contains(" - cgroup "))
|
||||||
.map(line -> line.split(" "))
|
.map(line -> line.split(" "))
|
||||||
.forEach(entry -> createSubSystem(metrics, entry));
|
.forEach(entry -> createSubSystemController(subsystem, entry));
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -107,47 +103,28 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
try (Stream<String> lines =
|
try (Stream<String> lines =
|
||||||
readFilePrivileged(Paths.get("/proc/self/cgroup"))) {
|
CgroupUtil.readFilePrivileged(Paths.get("/proc/self/cgroup"))) {
|
||||||
|
|
||||||
lines.map(line -> line.split(":"))
|
lines.map(line -> line.split(":"))
|
||||||
.filter(line -> (line.length >= 3))
|
.filter(line -> (line.length >= 3))
|
||||||
.forEach(line -> setSubSystemPath(metrics, line));
|
.forEach(line -> setSubSystemControllerPath(subsystem, line));
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return Metrics object if we found any subsystems.
|
// Return Metrics object if we found any subsystems.
|
||||||
if (metrics.activeSubSystems()) {
|
if (subsystem.activeSubSystems()) {
|
||||||
return metrics;
|
return subsystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Stream<String> readFilePrivileged(Path path) throws IOException {
|
|
||||||
try {
|
|
||||||
PrivilegedExceptionAction<Stream<String>> pea = () -> Files.lines(path);
|
|
||||||
return AccessController.doPrivileged(pea);
|
|
||||||
} catch (PrivilegedActionException e) {
|
|
||||||
unwrapIOExceptionAndRethrow(e);
|
|
||||||
throw new InternalError(e.getCause());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void unwrapIOExceptionAndRethrow(PrivilegedActionException pae) throws IOException {
|
|
||||||
Throwable x = pae.getCause();
|
|
||||||
if (x instanceof IOException)
|
|
||||||
throw (IOException) x;
|
|
||||||
if (x instanceof RuntimeException)
|
|
||||||
throw (RuntimeException) x;
|
|
||||||
if (x instanceof Error)
|
|
||||||
throw (Error) x;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* createSubSystem objects and initialize mount points
|
* createSubSystem objects and initialize mount points
|
||||||
*/
|
*/
|
||||||
private static void createSubSystem(Metrics metric, String[] mountentry) {
|
private static void createSubSystemController(CgroupV1Subsystem subsystem, String[] mountentry) {
|
||||||
if (mountentry.length < 5) return;
|
if (mountentry.length < 5) return;
|
||||||
|
|
||||||
Path p = Paths.get(mountentry[4]);
|
Path p = Paths.get(mountentry[4]);
|
||||||
|
@ -156,19 +133,19 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
||||||
for (String subsystemName: subsystemNames) {
|
for (String subsystemName: subsystemNames) {
|
||||||
switch (subsystemName) {
|
switch (subsystemName) {
|
||||||
case "memory":
|
case "memory":
|
||||||
metric.setMemorySubSystem(new MemorySubSystem(mountentry[3], mountentry[4]));
|
subsystem.setMemorySubSystem(new CgroupV1MemorySubSystemController(mountentry[3], mountentry[4]));
|
||||||
break;
|
break;
|
||||||
case "cpuset":
|
case "cpuset":
|
||||||
metric.setCpuSetSubSystem(new SubSystem(mountentry[3], mountentry[4]));
|
subsystem.setCpuSetController(new CgroupV1SubsystemController(mountentry[3], mountentry[4]));
|
||||||
break;
|
break;
|
||||||
case "cpuacct":
|
case "cpuacct":
|
||||||
metric.setCpuAcctSubSystem(new SubSystem(mountentry[3], mountentry[4]));
|
subsystem.setCpuAcctController(new CgroupV1SubsystemController(mountentry[3], mountentry[4]));
|
||||||
break;
|
break;
|
||||||
case "cpu":
|
case "cpu":
|
||||||
metric.setCpuSubSystem(new SubSystem(mountentry[3], mountentry[4]));
|
subsystem.setCpuController(new CgroupV1SubsystemController(mountentry[3], mountentry[4]));
|
||||||
break;
|
break;
|
||||||
case "blkio":
|
case "blkio":
|
||||||
metric.setBlkIOSubSystem(new SubSystem(mountentry[3], mountentry[4]));
|
subsystem.setBlkIOController(new CgroupV1SubsystemController(mountentry[3], mountentry[4]));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Ignore subsystems that we don't support
|
// Ignore subsystems that we don't support
|
||||||
|
@ -180,35 +157,35 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
||||||
/**
|
/**
|
||||||
* setSubSystemPath based on the contents of /proc/self/cgroup
|
* setSubSystemPath based on the contents of /proc/self/cgroup
|
||||||
*/
|
*/
|
||||||
private static void setSubSystemPath(Metrics metric, String[] entry) {
|
private static void setSubSystemControllerPath(CgroupV1Subsystem subsystem, String[] entry) {
|
||||||
String controller;
|
String controllerName;
|
||||||
String base;
|
String base;
|
||||||
SubSystem subsystem = null;
|
CgroupV1SubsystemController controller = null;
|
||||||
SubSystem subsystem2 = null;
|
CgroupV1SubsystemController controller2 = null;
|
||||||
|
|
||||||
controller = entry[1];
|
controllerName = entry[1];
|
||||||
base = entry[2];
|
base = entry[2];
|
||||||
if (controller != null && base != null) {
|
if (controllerName != null && base != null) {
|
||||||
switch (controller) {
|
switch (controllerName) {
|
||||||
case "memory":
|
case "memory":
|
||||||
subsystem = metric.MemorySubSystem();
|
controller = subsystem.memoryController();
|
||||||
break;
|
break;
|
||||||
case "cpuset":
|
case "cpuset":
|
||||||
subsystem = metric.CpuSetSubSystem();
|
controller = subsystem.cpuSetController();
|
||||||
break;
|
break;
|
||||||
case "cpu,cpuacct":
|
case "cpu,cpuacct":
|
||||||
case "cpuacct,cpu":
|
case "cpuacct,cpu":
|
||||||
subsystem = metric.CpuSubSystem();
|
controller = subsystem.cpuController();
|
||||||
subsystem2 = metric.CpuAcctSubSystem();
|
controller2 = subsystem.cpuAcctController();
|
||||||
break;
|
break;
|
||||||
case "cpuacct":
|
case "cpuacct":
|
||||||
subsystem = metric.CpuAcctSubSystem();
|
controller = subsystem.cpuAcctController();
|
||||||
break;
|
break;
|
||||||
case "cpu":
|
case "cpu":
|
||||||
subsystem = metric.CpuSubSystem();
|
controller = subsystem.cpuController();
|
||||||
break;
|
break;
|
||||||
case "blkio":
|
case "blkio":
|
||||||
subsystem = metric.BlkIOSubSystem();
|
controller = subsystem.blkIOController();
|
||||||
break;
|
break;
|
||||||
// Ignore subsystems that we don't support
|
// Ignore subsystems that we don't support
|
||||||
default:
|
default:
|
||||||
|
@ -216,23 +193,23 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subsystem != null) {
|
if (controller != null) {
|
||||||
subsystem.setPath(base);
|
controller.setPath(base);
|
||||||
if (subsystem instanceof MemorySubSystem) {
|
if (controller instanceof CgroupV1MemorySubSystemController) {
|
||||||
MemorySubSystem memorySubSystem = (MemorySubSystem)subsystem;
|
CgroupV1MemorySubSystemController memorySubSystem = (CgroupV1MemorySubSystemController)controller;
|
||||||
boolean isHierarchial = getHierarchical(memorySubSystem);
|
boolean isHierarchial = getHierarchical(memorySubSystem);
|
||||||
memorySubSystem.setHierarchical(isHierarchial);
|
memorySubSystem.setHierarchical(isHierarchial);
|
||||||
}
|
}
|
||||||
metric.setActiveSubSystems();
|
subsystem.setActiveSubSystems();
|
||||||
}
|
}
|
||||||
if (subsystem2 != null) {
|
if (controller2 != null) {
|
||||||
subsystem2.setPath(base);
|
controller2.setPath(base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static boolean getHierarchical(MemorySubSystem subsystem) {
|
private static boolean getHierarchical(CgroupV1MemorySubSystemController controller) {
|
||||||
long hierarchical = SubSystem.getLongValue(subsystem, "memory.use_hierarchy");
|
long hierarchical = getLongValue(controller, "memory.use_hierarchy");
|
||||||
return hierarchical > 0;
|
return hierarchical > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,46 +221,54 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
||||||
return activeSubSystems;
|
return activeSubSystems;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setMemorySubSystem(MemorySubSystem memory) {
|
private void setMemorySubSystem(CgroupV1MemorySubSystemController memory) {
|
||||||
this.memory = memory;
|
this.memory = memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCpuSubSystem(SubSystem cpu) {
|
private void setCpuController(CgroupV1SubsystemController cpu) {
|
||||||
this.cpu = cpu;
|
this.cpu = cpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCpuAcctSubSystem(SubSystem cpuacct) {
|
private void setCpuAcctController(CgroupV1SubsystemController cpuacct) {
|
||||||
this.cpuacct = cpuacct;
|
this.cpuacct = cpuacct;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCpuSetSubSystem(SubSystem cpuset) {
|
private void setCpuSetController(CgroupV1SubsystemController cpuset) {
|
||||||
this.cpuset = cpuset;
|
this.cpuset = cpuset;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setBlkIOSubSystem(SubSystem blkio) {
|
private void setBlkIOController(CgroupV1SubsystemController blkio) {
|
||||||
this.blkio = blkio;
|
this.blkio = blkio;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SubSystem MemorySubSystem() {
|
private CgroupV1SubsystemController memoryController() {
|
||||||
return memory;
|
return memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SubSystem CpuSubSystem() {
|
private CgroupV1SubsystemController cpuController() {
|
||||||
return cpu;
|
return cpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SubSystem CpuAcctSubSystem() {
|
private CgroupV1SubsystemController cpuAcctController() {
|
||||||
return cpuacct;
|
return cpuacct;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SubSystem CpuSetSubSystem() {
|
private CgroupV1SubsystemController cpuSetController() {
|
||||||
return cpuset;
|
return cpuset;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SubSystem BlkIOSubSystem() {
|
private CgroupV1SubsystemController blkIOController() {
|
||||||
return blkio;
|
return blkio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static long getLongValue(CgroupSubsystemController controller,
|
||||||
|
String parm) {
|
||||||
|
return CgroupSubsystemController.getLongValue(controller,
|
||||||
|
parm,
|
||||||
|
CgroupV1SubsystemController::convertStringToLong,
|
||||||
|
CgroupSubsystem.LONG_RETVAL_UNLIMITED);
|
||||||
|
}
|
||||||
|
|
||||||
public String getProvider() {
|
public String getProvider() {
|
||||||
return PROVIDER_NAME;
|
return PROVIDER_NAME;
|
||||||
}
|
}
|
||||||
|
@ -294,13 +279,13 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
||||||
|
|
||||||
|
|
||||||
public long getCpuUsage() {
|
public long getCpuUsage() {
|
||||||
return SubSystem.getLongValue(cpuacct, "cpuacct.usage");
|
return getLongValue(cpuacct, "cpuacct.usage");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long[] getPerCpuUsage() {
|
public long[] getPerCpuUsage() {
|
||||||
String usagelist = SubSystem.getStringValue(cpuacct, "cpuacct.usage_percpu");
|
String usagelist = CgroupSubsystemController.getStringValue(cpuacct, "cpuacct.usage_percpu");
|
||||||
if (usagelist == null) {
|
if (usagelist == null) {
|
||||||
return new long[0];
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String list[] = usagelist.split(" ");
|
String list[] = usagelist.split(" ");
|
||||||
|
@ -312,11 +297,11 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getCpuUserUsage() {
|
public long getCpuUserUsage() {
|
||||||
return SubSystem.getLongEntry(cpuacct, "cpuacct.stat", "user");
|
return CgroupV1SubsystemController.getLongEntry(cpuacct, "cpuacct.stat", "user");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getCpuSystemUsage() {
|
public long getCpuSystemUsage() {
|
||||||
return SubSystem.getLongEntry(cpuacct, "cpuacct.stat", "system");
|
return CgroupV1SubsystemController.getLongEntry(cpuacct, "cpuacct.stat", "system");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -326,31 +311,31 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
||||||
|
|
||||||
|
|
||||||
public long getCpuPeriod() {
|
public long getCpuPeriod() {
|
||||||
return SubSystem.getLongValue(cpu, "cpu.cfs_period_us");
|
return getLongValue(cpu, "cpu.cfs_period_us");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getCpuQuota() {
|
public long getCpuQuota() {
|
||||||
return SubSystem.getLongValue(cpu, "cpu.cfs_quota_us");
|
return getLongValue(cpu, "cpu.cfs_quota_us");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getCpuShares() {
|
public long getCpuShares() {
|
||||||
long retval = SubSystem.getLongValue(cpu, "cpu.shares");
|
long retval = getLongValue(cpu, "cpu.shares");
|
||||||
if (retval == 0 || retval == 1024)
|
if (retval == 0 || retval == 1024)
|
||||||
return -1;
|
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
|
||||||
else
|
else
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getCpuNumPeriods() {
|
public long getCpuNumPeriods() {
|
||||||
return SubSystem.getLongEntry(cpu, "cpu.stat", "nr_periods");
|
return CgroupV1SubsystemController.getLongEntry(cpu, "cpu.stat", "nr_periods");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getCpuNumThrottled() {
|
public long getCpuNumThrottled() {
|
||||||
return SubSystem.getLongEntry(cpu, "cpu.stat", "nr_throttled");
|
return CgroupV1SubsystemController.getLongEntry(cpu, "cpu.stat", "nr_throttled");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getCpuThrottledTime() {
|
public long getCpuThrottledTime() {
|
||||||
return SubSystem.getLongEntry(cpu, "cpu.stat", "throttled_time");
|
return CgroupV1SubsystemController.getLongEntry(cpu, "cpu.stat", "throttled_time");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getEffectiveCpuCount() {
|
public long getEffectiveCpuCount() {
|
||||||
|
@ -363,27 +348,27 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
||||||
****************************************************************/
|
****************************************************************/
|
||||||
|
|
||||||
public int[] getCpuSetCpus() {
|
public int[] getCpuSetCpus() {
|
||||||
return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.cpus"));
|
return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.cpus"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] getEffectiveCpuSetCpus() {
|
public int[] getEffectiveCpuSetCpus() {
|
||||||
return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.effective_cpus"));
|
return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.effective_cpus"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] getCpuSetMems() {
|
public int[] getCpuSetMems() {
|
||||||
return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.mems"));
|
return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.mems"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] getEffectiveCpuSetMems() {
|
public int[] getEffectiveCpuSetMems() {
|
||||||
return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.effective_mems"));
|
return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.effective_mems"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getCpuSetMemoryPressure() {
|
public double getCpuSetMemoryPressure() {
|
||||||
return SubSystem.getDoubleValue(cpuset, "cpuset.memory_pressure");
|
return CgroupV1SubsystemController.getDoubleValue(cpuset, "cpuset.memory_pressure");
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCpuSetMemoryPressureEnabled() {
|
public Boolean isCpuSetMemoryPressureEnabled() {
|
||||||
long val = SubSystem.getLongValue(cpuset, "cpuset.memory_pressure_enabled");
|
long val = getLongValue(cpuset, "cpuset.memory_pressure_enabled");
|
||||||
return (val == 1);
|
return (val == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,112 +379,98 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
||||||
|
|
||||||
|
|
||||||
public long getMemoryFailCount() {
|
public long getMemoryFailCount() {
|
||||||
return SubSystem.getLongValue(memory, "memory.failcnt");
|
return getLongValue(memory, "memory.failcnt");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMemoryLimit() {
|
public long getMemoryLimit() {
|
||||||
long retval = SubSystem.getLongValue(memory, "memory.limit_in_bytes");
|
long retval = getLongValue(memory, "memory.limit_in_bytes");
|
||||||
if (retval > unlimited_minimum) {
|
if (retval > CgroupV1SubsystemController.UNLIMITED_MIN) {
|
||||||
if (memory.isHierarchical()) {
|
if (memory.isHierarchical()) {
|
||||||
// memory.limit_in_bytes returned unlimited, attempt
|
// memory.limit_in_bytes returned unlimited, attempt
|
||||||
// hierarchical memory limit
|
// hierarchical memory limit
|
||||||
String match = "hierarchical_memory_limit";
|
String match = "hierarchical_memory_limit";
|
||||||
retval = SubSystem.getLongValueMatchingLine(memory,
|
retval = CgroupV1SubsystemController.getLongValueMatchingLine(memory,
|
||||||
"memory.stat",
|
"memory.stat",
|
||||||
match,
|
match);
|
||||||
Metrics::convertHierachicalLimitLine);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return retval > unlimited_minimum ? -1L : retval;
|
return CgroupV1SubsystemController.longValOrUnlimited(retval);
|
||||||
}
|
|
||||||
|
|
||||||
public static long convertHierachicalLimitLine(String line) {
|
|
||||||
String[] tokens = line.split("\\s");
|
|
||||||
if (tokens.length == 2) {
|
|
||||||
String strVal = tokens[1];
|
|
||||||
return SubSystem.convertStringToLong(strVal);
|
|
||||||
}
|
|
||||||
return unlimited_minimum + 1; // unlimited
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMemoryMaxUsage() {
|
public long getMemoryMaxUsage() {
|
||||||
return SubSystem.getLongValue(memory, "memory.max_usage_in_bytes");
|
return getLongValue(memory, "memory.max_usage_in_bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMemoryUsage() {
|
public long getMemoryUsage() {
|
||||||
return SubSystem.getLongValue(memory, "memory.usage_in_bytes");
|
return getLongValue(memory, "memory.usage_in_bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getKernelMemoryFailCount() {
|
public long getKernelMemoryFailCount() {
|
||||||
return SubSystem.getLongValue(memory, "memory.kmem.failcnt");
|
return getLongValue(memory, "memory.kmem.failcnt");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getKernelMemoryLimit() {
|
public long getKernelMemoryLimit() {
|
||||||
long retval = SubSystem.getLongValue(memory, "memory.kmem.limit_in_bytes");
|
return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.kmem.limit_in_bytes"));
|
||||||
return retval > unlimited_minimum ? -1L : retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getKernelMemoryMaxUsage() {
|
public long getKernelMemoryMaxUsage() {
|
||||||
return SubSystem.getLongValue(memory, "memory.kmem.max_usage_in_bytes");
|
return getLongValue(memory, "memory.kmem.max_usage_in_bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getKernelMemoryUsage() {
|
public long getKernelMemoryUsage() {
|
||||||
return SubSystem.getLongValue(memory, "memory.kmem.usage_in_bytes");
|
return getLongValue(memory, "memory.kmem.usage_in_bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTcpMemoryFailCount() {
|
public long getTcpMemoryFailCount() {
|
||||||
return SubSystem.getLongValue(memory, "memory.kmem.tcp.failcnt");
|
return getLongValue(memory, "memory.kmem.tcp.failcnt");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTcpMemoryLimit() {
|
public long getTcpMemoryLimit() {
|
||||||
long retval = SubSystem.getLongValue(memory, "memory.kmem.tcp.limit_in_bytes");
|
return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.kmem.tcp.limit_in_bytes"));
|
||||||
return retval > unlimited_minimum ? -1L : retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTcpMemoryMaxUsage() {
|
public long getTcpMemoryMaxUsage() {
|
||||||
return SubSystem.getLongValue(memory, "memory.kmem.tcp.max_usage_in_bytes");
|
return getLongValue(memory, "memory.kmem.tcp.max_usage_in_bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getTcpMemoryUsage() {
|
public long getTcpMemoryUsage() {
|
||||||
return SubSystem.getLongValue(memory, "memory.kmem.tcp.usage_in_bytes");
|
return getLongValue(memory, "memory.kmem.tcp.usage_in_bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMemoryAndSwapFailCount() {
|
public long getMemoryAndSwapFailCount() {
|
||||||
return SubSystem.getLongValue(memory, "memory.memsw.failcnt");
|
return getLongValue(memory, "memory.memsw.failcnt");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMemoryAndSwapLimit() {
|
public long getMemoryAndSwapLimit() {
|
||||||
long retval = SubSystem.getLongValue(memory, "memory.memsw.limit_in_bytes");
|
long retval = getLongValue(memory, "memory.memsw.limit_in_bytes");
|
||||||
if (retval > unlimited_minimum) {
|
if (retval > CgroupV1SubsystemController.UNLIMITED_MIN) {
|
||||||
if (memory.isHierarchical()) {
|
if (memory.isHierarchical()) {
|
||||||
// memory.memsw.limit_in_bytes returned unlimited, attempt
|
// memory.memsw.limit_in_bytes returned unlimited, attempt
|
||||||
// hierarchical memory limit
|
// hierarchical memory limit
|
||||||
String match = "hierarchical_memsw_limit";
|
String match = "hierarchical_memsw_limit";
|
||||||
retval = SubSystem.getLongValueMatchingLine(memory,
|
retval = CgroupV1SubsystemController.getLongValueMatchingLine(memory,
|
||||||
"memory.stat",
|
"memory.stat",
|
||||||
match,
|
match);
|
||||||
Metrics::convertHierachicalLimitLine);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return retval > unlimited_minimum ? -1L : retval;
|
return CgroupV1SubsystemController.longValOrUnlimited(retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMemoryAndSwapMaxUsage() {
|
public long getMemoryAndSwapMaxUsage() {
|
||||||
return SubSystem.getLongValue(memory, "memory.memsw.max_usage_in_bytes");
|
return getLongValue(memory, "memory.memsw.max_usage_in_bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMemoryAndSwapUsage() {
|
public long getMemoryAndSwapUsage() {
|
||||||
return SubSystem.getLongValue(memory, "memory.memsw.usage_in_bytes");
|
return getLongValue(memory, "memory.memsw.usage_in_bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMemoryOOMKillEnabled() {
|
public Boolean isMemoryOOMKillEnabled() {
|
||||||
long val = SubSystem.getLongEntry(memory, "memory.oom_control", "oom_kill_disable");
|
long val = CgroupV1SubsystemController.getLongEntry(memory, "memory.oom_control", "oom_kill_disable");
|
||||||
return (val == 0);
|
return (val == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMemorySoftLimit() {
|
public long getMemorySoftLimit() {
|
||||||
long retval = SubSystem.getLongValue(memory, "memory.soft_limit_in_bytes");
|
return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.soft_limit_in_bytes"));
|
||||||
return retval > unlimited_minimum ? -1L : retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -509,11 +480,11 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
||||||
|
|
||||||
|
|
||||||
public long getBlkIOServiceCount() {
|
public long getBlkIOServiceCount() {
|
||||||
return SubSystem.getLongEntry(blkio, "blkio.throttle.io_service_bytes", "Total");
|
return CgroupV1SubsystemController.getLongEntry(blkio, "blkio.throttle.io_service_bytes", "Total");
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getBlkIOServiced() {
|
public long getBlkIOServiced() {
|
||||||
return SubSystem.getLongEntry(blkio, "blkio.throttle.io_serviced", "Total");
|
return CgroupV1SubsystemController.getLongEntry(blkio, "blkio.throttle.io_serviced", "Total");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* 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.cgroupv1;
|
||||||
|
|
||||||
|
import jdk.internal.platform.CgroupSubsystem;
|
||||||
|
import jdk.internal.platform.CgroupSubsystemController;
|
||||||
|
|
||||||
|
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;
|
||||||
|
String mountPoint;
|
||||||
|
String path;
|
||||||
|
|
||||||
|
public CgroupV1SubsystemController(String root, String mountPoint) {
|
||||||
|
this.root = root;
|
||||||
|
this.mountPoint = mountPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPath(String cgroupPath) {
|
||||||
|
if (root != null && cgroupPath != null) {
|
||||||
|
if (root.equals("/")) {
|
||||||
|
if (!cgroupPath.equals("/")) {
|
||||||
|
path = mountPoint + cgroupPath;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
path = mountPoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (root.equals(cgroupPath)) {
|
||||||
|
path = mountPoint;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (cgroupPath.startsWith(root)) {
|
||||||
|
if (cgroupPath.length() > root.length()) {
|
||||||
|
String cgroupSubstr = cgroupPath.substring(root.length());
|
||||||
|
path = mountPoint + cgroupSubstr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String path() {
|
||||||
|
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 /* overflow value */,
|
||||||
|
CgroupSubsystem.LONG_RETVAL_UNLIMITED /* retval on error */);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long longValOrUnlimited(long 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,283 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018, 2019, 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.cgroupv1;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.PrivilegedActionException;
|
|
||||||
import java.security.PrivilegedExceptionAction;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public class SubSystem {
|
|
||||||
String root;
|
|
||||||
String mountPoint;
|
|
||||||
String path;
|
|
||||||
|
|
||||||
public SubSystem(String root, String mountPoint) {
|
|
||||||
this.root = root;
|
|
||||||
this.mountPoint = mountPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPath(String cgroupPath) {
|
|
||||||
if (root != null && cgroupPath != null) {
|
|
||||||
if (root.equals("/")) {
|
|
||||||
if (!cgroupPath.equals("/")) {
|
|
||||||
path = mountPoint + cgroupPath;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
path = mountPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (root.equals(cgroupPath)) {
|
|
||||||
path = mountPoint;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (cgroupPath.startsWith(root)) {
|
|
||||||
if (cgroupPath.length() > root.length()) {
|
|
||||||
String cgroupSubstr = cgroupPath.substring(root.length());
|
|
||||||
path = mountPoint + cgroupSubstr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String path() {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getSubSystemStringValue
|
|
||||||
*
|
|
||||||
* Return the first line of the file "parm" argument from the subsystem.
|
|
||||||
*
|
|
||||||
* TODO: Consider using weak references for caching BufferedReader object.
|
|
||||||
*
|
|
||||||
* @param subsystem
|
|
||||||
* @param parm
|
|
||||||
* @return Returns the contents of the file specified by param.
|
|
||||||
*/
|
|
||||||
public static String getStringValue(SubSystem subsystem, String parm) {
|
|
||||||
if (subsystem == null) return null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
return subsystem.readStringValue(parm);
|
|
||||||
} catch (IOException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String readStringValue(String param) throws IOException {
|
|
||||||
PrivilegedExceptionAction<BufferedReader> pea = () ->
|
|
||||||
Files.newBufferedReader(Paths.get(path(), param));
|
|
||||||
try (BufferedReader bufferedReader =
|
|
||||||
AccessController.doPrivileged(pea)) {
|
|
||||||
String line = bufferedReader.readLine();
|
|
||||||
return line;
|
|
||||||
} catch (PrivilegedActionException e) {
|
|
||||||
Metrics.unwrapIOExceptionAndRethrow(e);
|
|
||||||
throw new InternalError(e.getCause());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getLongValueMatchingLine(SubSystem subsystem,
|
|
||||||
String param,
|
|
||||||
String match,
|
|
||||||
Function<String, Long> conversion) {
|
|
||||||
long retval = Metrics.unlimited_minimum + 1; // default unlimited
|
|
||||||
try {
|
|
||||||
List<String> lines = subsystem.readMatchingLines(param);
|
|
||||||
for (String line : lines) {
|
|
||||||
if (line.startsWith(match)) {
|
|
||||||
retval = conversion.apply(line);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Ignore. Default is unlimited.
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<String> readMatchingLines(String param) throws IOException {
|
|
||||||
try {
|
|
||||||
PrivilegedExceptionAction<List<String>> pea = () ->
|
|
||||||
Files.readAllLines(Paths.get(path(), param));
|
|
||||||
return AccessController.doPrivileged(pea);
|
|
||||||
} catch (PrivilegedActionException e) {
|
|
||||||
Metrics.unwrapIOExceptionAndRethrow(e);
|
|
||||||
throw new InternalError(e.getCause());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getLongValue(SubSystem subsystem, String parm) {
|
|
||||||
String strval = getStringValue(subsystem, parm);
|
|
||||||
return convertStringToLong(strval);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long convertStringToLong(String strval) {
|
|
||||||
long retval = 0;
|
|
||||||
if (strval == null) return 0L;
|
|
||||||
|
|
||||||
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 Long.MAX_VALUE
|
|
||||||
BigInteger b = new BigInteger(strval);
|
|
||||||
if (b.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) {
|
|
||||||
return Long.MAX_VALUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static double getDoubleValue(SubSystem subsystem, String parm) {
|
|
||||||
String strval = getStringValue(subsystem, parm);
|
|
||||||
|
|
||||||
if (strval == null) return 0L;
|
|
||||||
|
|
||||||
double retval = Double.parseDouble(strval);
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getSubSystemlongEntry
|
|
||||||
*
|
|
||||||
* Return the long value from the line containing the string "entryname"
|
|
||||||
* within file "parm" in the "subsystem".
|
|
||||||
*
|
|
||||||
* TODO: Consider using weak references for caching BufferedReader object.
|
|
||||||
*
|
|
||||||
* @param subsystem
|
|
||||||
* @param parm
|
|
||||||
* @param entryname
|
|
||||||
* @return long value
|
|
||||||
*/
|
|
||||||
public static long getLongEntry(SubSystem subsystem, String parm, String entryname) {
|
|
||||||
String val = null;
|
|
||||||
|
|
||||||
if (subsystem == null) return 0L;
|
|
||||||
|
|
||||||
try (Stream<String> lines = Metrics.readFilePrivileged(Paths.get(subsystem.path(), parm))) {
|
|
||||||
|
|
||||||
Optional<String> result = lines.map(line -> line.split(" "))
|
|
||||||
.filter(line -> (line.length == 2 &&
|
|
||||||
line[0].equals(entryname)))
|
|
||||||
.map(line -> line[1])
|
|
||||||
.findFirst();
|
|
||||||
|
|
||||||
return result.isPresent() ? Long.parseLong(result.get()) : 0L;
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
return 0L;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getIntValue(SubSystem subsystem, String parm) {
|
|
||||||
String val = getStringValue(subsystem, parm);
|
|
||||||
|
|
||||||
if (val == null) return 0;
|
|
||||||
|
|
||||||
return Integer.parseInt(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
public static int[] StringRangeToIntArray(String range) {
|
|
||||||
int[] ints = new int[0];
|
|
||||||
|
|
||||||
if (range == null) return ints;
|
|
||||||
|
|
||||||
ArrayList<Integer> results = new ArrayList<>();
|
|
||||||
String strs[] = range.split(",");
|
|
||||||
for (String str : strs) {
|
|
||||||
if (str.contains("-")) {
|
|
||||||
String lohi[] = str.split("-");
|
|
||||||
// validate format
|
|
||||||
if (lohi.length != 2) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int lo = Integer.parseInt(lohi[0]);
|
|
||||||
int hi = Integer.parseInt(lohi[1]);
|
|
||||||
for (int i = lo; i <= hi; i++) {
|
|
||||||
results.add(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
results.add(Integer.parseInt(str));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort results
|
|
||||||
results.sort(null);
|
|
||||||
|
|
||||||
// convert ArrayList to primitive int array
|
|
||||||
ints = new int[results.size()];
|
|
||||||
int i = 0;
|
|
||||||
for (Integer n : results) {
|
|
||||||
ints[i++] = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ints;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class MemorySubSystem extends SubSystem {
|
|
||||||
|
|
||||||
private boolean hierarchical;
|
|
||||||
|
|
||||||
public MemorySubSystem(String root, String mountPoint) {
|
|
||||||
super(root, mountPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isHierarchical() {
|
|
||||||
return hierarchical;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setHierarchical(boolean hierarchical) {
|
|
||||||
this.hierarchical = hierarchical;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,363 @@
|
||||||
|
/*
|
||||||
|
* 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. 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.cgroupv2;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import jdk.internal.platform.CgroupSubsystem;
|
||||||
|
import jdk.internal.platform.CgroupSubsystemController;
|
||||||
|
import jdk.internal.platform.CgroupUtil;
|
||||||
|
|
||||||
|
public class CgroupV2Subsystem implements CgroupSubsystem {
|
||||||
|
|
||||||
|
private static final CgroupV2Subsystem INSTANCE = initSubsystem();
|
||||||
|
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;
|
||||||
|
private static final String MAX_VAL = "max";
|
||||||
|
private static final Object EMPTY_STR = "";
|
||||||
|
|
||||||
|
private CgroupV2Subsystem(CgroupSubsystemController unified) {
|
||||||
|
this.unified = unified;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getLongVal(String file) {
|
||||||
|
return CgroupSubsystemController.getLongValue(unified,
|
||||||
|
file,
|
||||||
|
CgroupV2SubsystemController::convertStringToLong,
|
||||||
|
CgroupSubsystem.LONG_RETVAL_UNLIMITED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CgroupV2Subsystem initSubsystem() {
|
||||||
|
// read mountinfo so as to determine root mount path
|
||||||
|
String mountPath = null;
|
||||||
|
try (Stream<String> lines =
|
||||||
|
CgroupUtil.readFilePrivileged(Paths.get("/proc/self/mountinfo"))) {
|
||||||
|
|
||||||
|
String l = lines.filter(line -> line.contains(" - cgroup2 "))
|
||||||
|
.collect(Collectors.joining());
|
||||||
|
String[] tokens = l.split(" ");
|
||||||
|
mountPath = tokens[4];
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String cgroupPath = null;
|
||||||
|
try {
|
||||||
|
List<String> lines = CgroupUtil.readAllLinesPrivileged(Paths.get("/proc/self/cgroup"));
|
||||||
|
for (String line: lines) {
|
||||||
|
String[] tokens = line.split(":");
|
||||||
|
if (tokens.length != 3) {
|
||||||
|
return null; // something is not right.
|
||||||
|
}
|
||||||
|
if (!"0".equals(tokens[0])) {
|
||||||
|
// hierarchy must be zero for cgroups v2
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
cgroupPath = tokens[2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
CgroupSubsystemController unified = new CgroupV2SubsystemController(
|
||||||
|
mountPath,
|
||||||
|
cgroupPath);
|
||||||
|
return new CgroupV2Subsystem(unified);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CgroupSubsystem getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProvider() {
|
||||||
|
return PROVIDER_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuUsage() {
|
||||||
|
long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "usage_usec");
|
||||||
|
if (micros < 0) {
|
||||||
|
return micros;
|
||||||
|
}
|
||||||
|
return TimeUnit.MICROSECONDS.toNanos(micros);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long[] getPerCpuUsage() {
|
||||||
|
return LONG_ARRAY_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuUserUsage() {
|
||||||
|
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 = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "system_usec");
|
||||||
|
if (micros < 0) {
|
||||||
|
return micros;
|
||||||
|
}
|
||||||
|
return TimeUnit.MICROSECONDS.toNanos(micros);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuPeriod() {
|
||||||
|
return getFromCpuMax(1 /* $PERIOD index */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuQuota() {
|
||||||
|
return getFromCpuMax(0 /* $MAX index */);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getFromCpuMax(int tokenIdx) {
|
||||||
|
String cpuMaxRaw = CgroupSubsystemController.getStringValue(unified, "cpu.max");
|
||||||
|
if (cpuMaxRaw == null) {
|
||||||
|
// likely file not found
|
||||||
|
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
|
||||||
|
}
|
||||||
|
// $MAX $PERIOD
|
||||||
|
String[] tokens = cpuMaxRaw.split("\\s+");
|
||||||
|
if (tokens.length != 2) {
|
||||||
|
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
|
||||||
|
}
|
||||||
|
String quota = tokens[tokenIdx];
|
||||||
|
return limitFromString(quota);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long limitFromString(String strVal) {
|
||||||
|
if (strVal == null || MAX_VAL.equals(strVal)) {
|
||||||
|
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
|
||||||
|
}
|
||||||
|
return Long.parseLong(strVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuShares() {
|
||||||
|
long sharesRaw = getLongVal("cpu.weight");
|
||||||
|
if (sharesRaw == 100 || sharesRaw <= 0) {
|
||||||
|
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
|
||||||
|
}
|
||||||
|
int shares = (int)sharesRaw;
|
||||||
|
// CPU shares (OCI) value needs to get translated into
|
||||||
|
// a proper Cgroups v2 value. See:
|
||||||
|
// https://github.com/containers/crun/blob/master/crun.1.md#cpu-controller
|
||||||
|
//
|
||||||
|
// Use the inverse of (x == OCI value, y == cgroupsv2 value):
|
||||||
|
// ((262142 * y - 1)/9999) + 2 = x
|
||||||
|
//
|
||||||
|
int x = 262142 * shares - 1;
|
||||||
|
double frac = x/9999.0;
|
||||||
|
x = ((int)frac) + 2;
|
||||||
|
if ( x <= PER_CPU_SHARES ) {
|
||||||
|
return PER_CPU_SHARES; // mimic cgroups v1
|
||||||
|
}
|
||||||
|
int f = x/PER_CPU_SHARES;
|
||||||
|
int lower_multiple = f * PER_CPU_SHARES;
|
||||||
|
int upper_multiple = (f + 1) * PER_CPU_SHARES;
|
||||||
|
int distance_lower = Math.max(lower_multiple, x) - Math.min(lower_multiple, x);
|
||||||
|
int distance_upper = Math.max(upper_multiple, x) - Math.min(upper_multiple, x);
|
||||||
|
x = distance_lower <= distance_upper ? lower_multiple : upper_multiple;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuNumPeriods() {
|
||||||
|
return CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "nr_periods");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuNumThrottled() {
|
||||||
|
return CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "nr_throttled");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getCpuThrottledTime() {
|
||||||
|
long micros = CgroupV2SubsystemController.getLongEntry(unified, "cpu.stat", "throttled_usec");
|
||||||
|
if (micros < 0) {
|
||||||
|
return micros;
|
||||||
|
}
|
||||||
|
return TimeUnit.MICROSECONDS.toNanos(micros);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEffectiveCpuCount() {
|
||||||
|
return Runtime.getRuntime().availableProcessors();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getCpuSetCpus() {
|
||||||
|
String cpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus");
|
||||||
|
return getCpuSet(cpuSetVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getEffectiveCpuSetCpus() {
|
||||||
|
String effCpuSetVal = CgroupSubsystemController.getStringValue(unified, "cpuset.cpus.effective");
|
||||||
|
return getCpuSet(effCpuSetVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getCpuSetMems() {
|
||||||
|
String cpuSetMems = CgroupSubsystemController.getStringValue(unified, "cpuset.mems");
|
||||||
|
return getCpuSet(cpuSetMems);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getEffectiveCpuSetMems() {
|
||||||
|
String effCpuSetMems = CgroupSubsystemController.getStringValue(unified, "cpuset.mems.effective");
|
||||||
|
return getCpuSet(effCpuSetMems);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 CgroupV2SubsystemController.getLongEntry(unified, "memory.events", "max");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMemoryLimit() {
|
||||||
|
String strVal = CgroupSubsystemController.getStringValue(unified, "memory.max");
|
||||||
|
return limitFromString(strVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMemoryUsage() {
|
||||||
|
return getLongVal("memory.current");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTcpMemoryUsage() {
|
||||||
|
return CgroupV2SubsystemController.getLongEntry(unified, "memory.stat", "sock");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMemoryAndSwapLimit() {
|
||||||
|
String strVal = CgroupSubsystemController.getStringValue(unified, "memory.swap.max");
|
||||||
|
return limitFromString(strVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMemoryAndSwapUsage() {
|
||||||
|
return getLongVal("memory.swap.current");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMemorySoftLimit() {
|
||||||
|
String softLimitStr = CgroupSubsystemController.getStringValue(unified, "memory.high");
|
||||||
|
return limitFromString(softLimitStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getBlkIOServiceCount() {
|
||||||
|
return sumTokensIOStat(CgroupV2Subsystem::lineToRandWIOs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getBlkIOServiced() {
|
||||||
|
return sumTokensIOStat(CgroupV2Subsystem::lineToRBytesAndWBytesIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long sumTokensIOStat(Function<String, Long> mapFunc) {
|
||||||
|
try {
|
||||||
|
return CgroupUtil.readFilePrivileged(Paths.get(unified.path(), "io.stat"))
|
||||||
|
.map(mapFunc)
|
||||||
|
.collect(Collectors.summingLong(e -> e));
|
||||||
|
} catch (IOException e) {
|
||||||
|
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* 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. 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.cgroupv2;
|
||||||
|
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
import jdk.internal.platform.CgroupSubsystem;
|
||||||
|
import jdk.internal.platform.CgroupSubsystemController;
|
||||||
|
|
||||||
|
public class CgroupV2SubsystemController implements CgroupSubsystemController {
|
||||||
|
|
||||||
|
private final String path;
|
||||||
|
|
||||||
|
public CgroupV2SubsystemController(String mountPath, String cgroupPath) {
|
||||||
|
this.path = Paths.get(mountPath, cgroupPath).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String path() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long convertStringToLong(String strval) {
|
||||||
|
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 */);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -56,9 +56,7 @@ public interface Metrics {
|
||||||
*/
|
*/
|
||||||
public static Metrics systemMetrics() {
|
public static Metrics systemMetrics() {
|
||||||
try {
|
try {
|
||||||
// We currently only support cgroupv1
|
Class<?> c = Class.forName("jdk.internal.platform.CgroupMetrics");
|
||||||
Class<?> c = Class.forName("jdk.internal.platform.cgroupv1.Metrics");
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Method m = c.getMethod("getInstance");
|
Method m = c.getMethod("getInstance");
|
||||||
return (Metrics) m.invoke(null);
|
return (Metrics) m.invoke(null);
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
|
@ -74,7 +72,7 @@ public interface Metrics {
|
||||||
*
|
*
|
||||||
* @implNote
|
* @implNote
|
||||||
* Metrics are currently only supported Linux.
|
* 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.
|
* @return The name of the provider.
|
||||||
*
|
*
|
||||||
|
@ -90,7 +88,8 @@ public interface Metrics {
|
||||||
* Returns the aggregate time, in nanoseconds, consumed by all
|
* Returns the aggregate time, in nanoseconds, consumed by all
|
||||||
* tasks in the Isolation Group.
|
* tasks in the Isolation Group.
|
||||||
*
|
*
|
||||||
* @return Time in nanoseconds or 0L if metric is not available.
|
* @return Time in nanoseconds, -1 if unknown or
|
||||||
|
* -2 if the metric is not supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public long getCpuUsage();
|
public long getCpuUsage();
|
||||||
|
@ -106,7 +105,7 @@ public interface Metrics {
|
||||||
*
|
*
|
||||||
* @return long array of time values. The size of the array is equal
|
* @return long array of time values. The size of the array is equal
|
||||||
* to the total number of physical processors in the system. If
|
* 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 supported or not available, null will be
|
||||||
* returned.
|
* returned.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -116,7 +115,8 @@ public interface Metrics {
|
||||||
* Returns the aggregate user time, in nanoseconds, consumed by all
|
* Returns the aggregate user time, in nanoseconds, consumed by all
|
||||||
* tasks in the Isolation Group.
|
* tasks in the Isolation Group.
|
||||||
*
|
*
|
||||||
* @return User time in nanoseconds or 0L 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();
|
public long getCpuUserUsage();
|
||||||
|
@ -125,7 +125,8 @@ public interface Metrics {
|
||||||
* Returns the aggregate system time, in nanoseconds, consumed by
|
* Returns the aggregate system time, in nanoseconds, consumed by
|
||||||
* all tasks in the Isolation Group.
|
* all tasks in the Isolation Group.
|
||||||
*
|
*
|
||||||
* @return System time in nanoseconds or 0L 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();
|
public long getCpuSystemUsage();
|
||||||
|
@ -138,7 +139,8 @@ public interface Metrics {
|
||||||
* Returns the length of the scheduling period, in
|
* Returns the length of the scheduling period, in
|
||||||
* microseconds, for processes within the Isolation Group.
|
* microseconds, for processes within the Isolation Group.
|
||||||
*
|
*
|
||||||
* @return time in microseconds or 0L 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();
|
public long getCpuPeriod();
|
||||||
|
@ -148,7 +150,8 @@ public interface Metrics {
|
||||||
* during each scheduling period for all tasks in the Isolation
|
* during each scheduling period for all tasks in the Isolation
|
||||||
* Group.
|
* Group.
|
||||||
*
|
*
|
||||||
* @return time in microseconds or -1 if the quota is unlimited.
|
* @return time in microseconds, -1 if the quota is unlimited or
|
||||||
|
* -2 if not supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public long getCpuQuota();
|
public long getCpuQuota();
|
||||||
|
@ -167,17 +170,18 @@ public interface Metrics {
|
||||||
* each process. To request 2 CPUS worth of execution time, CPU shares
|
* each process. To request 2 CPUS worth of execution time, CPU shares
|
||||||
* would be set to 2048.
|
* would be set to 2048.
|
||||||
*
|
*
|
||||||
* @return shares value or -1 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();
|
public long getCpuShares();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of time-slice periods that have elapsed if
|
* Returns the number of time-slice periods that have elapsed if
|
||||||
* a CPU quota has been setup for the Isolation Group; otherwise
|
* a CPU quota has been setup for the Isolation Group
|
||||||
* returns 0.
|
|
||||||
*
|
*
|
||||||
* @return count of elapsed periods or 0 if the quota is unlimited.
|
* @return count of elapsed periods, -1 if the metric is not available
|
||||||
|
* or -2 if the metric is not supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public long getCpuNumPeriods();
|
public long getCpuNumPeriods();
|
||||||
|
@ -187,7 +191,8 @@ public interface Metrics {
|
||||||
* been throttled or limited due to the group exceeding its quota
|
* been throttled or limited due to the group exceeding its quota
|
||||||
* if a CPU quota has been setup for the Isolation Group.
|
* 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, -1 if the metric is not available or
|
||||||
|
* -2 if it is not supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public long getCpuNumThrottled();
|
public long getCpuNumThrottled();
|
||||||
|
@ -197,7 +202,8 @@ public interface Metrics {
|
||||||
* group has been throttled or limited due to the group exceeding
|
* group has been throttled or limited due to the group exceeding
|
||||||
* its quota if a CPU quota has been setup for the Isolation Group.
|
* 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, -1 if the metric is not available
|
||||||
|
* or -2 if it is not supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public long getCpuThrottledTime();
|
public long getCpuThrottledTime();
|
||||||
|
@ -229,8 +235,8 @@ public interface Metrics {
|
||||||
* may be offline. To get the current online CPUs, use
|
* may be offline. To get the current online CPUs, use
|
||||||
* {@link getEffectiveCpuSetCpus()}.
|
* {@link getEffectiveCpuSetCpus()}.
|
||||||
*
|
*
|
||||||
* @return An array of available CPUs or a zero length array
|
* @return An array of available CPUs. Returns null if the metric is not
|
||||||
* if the metric is not available.
|
* available or the metric is not supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public int[] getCpuSetCpus();
|
public int[] getCpuSetCpus();
|
||||||
|
@ -241,8 +247,8 @@ public interface Metrics {
|
||||||
* array is equal to the total number of CPUs and the elements in
|
* array is equal to the total number of CPUs and the elements in
|
||||||
* the array are the physical CPU numbers.
|
* the array are the physical CPU numbers.
|
||||||
*
|
*
|
||||||
* @return An array of available and online CPUs or a zero length
|
* @return An array of available and online CPUs. Returns null
|
||||||
* array if the metric is not available.
|
* if the metric is not available or the metric is not supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public int[] getEffectiveCpuSetCpus();
|
public int[] getEffectiveCpuSetCpus();
|
||||||
|
@ -255,8 +261,8 @@ public interface Metrics {
|
||||||
* may be offline. To get the current online memory nodes, use
|
* may be offline. To get the current online memory nodes, use
|
||||||
* {@link getEffectiveCpuSetMems()}.
|
* {@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.
|
* if the metric is not available or is not supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public int[] getCpuSetMems();
|
public int[] getCpuSetMems();
|
||||||
|
@ -267,33 +273,12 @@ public interface Metrics {
|
||||||
* array is equal to the total number of nodes and the elements in
|
* array is equal to the total number of nodes and the elements in
|
||||||
* the array are the physical node numbers.
|
* the array are the physical node numbers.
|
||||||
*
|
*
|
||||||
* @return An array of available and online nodes or a zero length
|
* @return An array of available and online nodes or null
|
||||||
* array if the metric is not available.
|
* if the metric is not available or is not supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public int[] getEffectiveCpuSetMems();
|
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 metric is not
|
|
||||||
* available.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public double getCpuSetMemoryPressure();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the state of the memory pressure detection support.
|
|
||||||
*
|
|
||||||
* @return true if the support is available and enabled, otherwise false.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public boolean isCpuSetMemoryPressureEnabled();
|
|
||||||
|
|
||||||
/*****************************************************************
|
/*****************************************************************
|
||||||
* Memory Subsystem
|
* Memory Subsystem
|
||||||
****************************************************************/
|
****************************************************************/
|
||||||
|
@ -302,8 +287,9 @@ public interface Metrics {
|
||||||
* Returns the number of times that user memory requests in the
|
* Returns the number of times that user memory requests in the
|
||||||
* Isolation Group have exceeded the memory limit.
|
* 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 -1 if the metric
|
||||||
* is not available.
|
* is not available. Returns -2 if the metric is not
|
||||||
|
* supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public long getMemoryFailCount();
|
public long getMemoryFailCount();
|
||||||
|
@ -312,164 +298,54 @@ public interface Metrics {
|
||||||
* Returns the maximum amount of physical memory, in bytes, that
|
* Returns the maximum amount of physical memory, in bytes, that
|
||||||
* can be allocated in the Isolation Group.
|
* can be allocated in the Isolation Group.
|
||||||
*
|
*
|
||||||
* @return The maximum amount of memory in bytes or -1 if either
|
* @return The maximum amount of memory in bytes or -1 if
|
||||||
* there is no limit set or this metric is not available.
|
* there is no limit or -2 if this metric is not supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public long getMemoryLimit();
|
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 0 if this
|
|
||||||
* metric is not available.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public long getMemoryMaxUsage();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the amount of physical memory, in bytes, that is currently
|
* Returns the amount of physical memory, in bytes, that is currently
|
||||||
* allocated in the current Isolation Group.
|
* 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 -1 if
|
||||||
* metric is not available.
|
* the metric is not available or -2 if the metric is not
|
||||||
|
* supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public long getMemoryUsage();
|
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 0 if none or 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 either
|
|
||||||
* there is no limit set or 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 0 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 0 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 0 if none or 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 either
|
|
||||||
* there is no limit set or 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 0 if this
|
|
||||||
* metric is not available.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public long getTcpMemoryMaxUsage();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the amount of networking physical memory, in bytes, that
|
* Returns the amount of networking physical memory, in bytes, that
|
||||||
* is currently allocated in the current Isolation Group.
|
* 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 -1 if the metric
|
||||||
* metric is not available.
|
* is not available. Returns -2 if this metric is not supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public long getTcpMemoryUsage();
|
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 0 if none or metric
|
|
||||||
* is not available.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public long getMemoryAndSwapFailCount();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the maximum amount of physical memory and swap space,
|
* Returns the maximum amount of physical memory and swap space,
|
||||||
* in bytes, that can be allocated in the Isolation Group.
|
* in bytes, that can be allocated in the Isolation Group.
|
||||||
*
|
*
|
||||||
* @return The maximum amount of memory in bytes or -1 if either
|
* @return The maximum amount of memory in bytes or -1 if
|
||||||
* there is no limit set or this metric is not available.
|
* there is no limit set or -2 if this metric is not supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public long getMemoryAndSwapLimit();
|
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 0 if this
|
|
||||||
* metric is not available.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public long getMemoryAndSwapMaxUsage();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the amount of physical memory and swap space, in bytes,
|
* Returns the amount of physical memory and swap space, in bytes,
|
||||||
* that is currently allocated in the current Isolation Group.
|
* 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 -1 if
|
||||||
* metric is not available.
|
* the metric is not available. Returns -2 if this metric is not
|
||||||
|
* supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public long getMemoryAndSwapUsage();
|
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. Flase 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
|
* Returns the hint to the operating system that allows groups
|
||||||
* to specify the minimum amount of physical memory that they need to
|
* to specify the minimum amount of physical memory that they need to
|
||||||
|
@ -478,8 +354,8 @@ public interface Metrics {
|
||||||
*
|
*
|
||||||
* @return The minimum amount of physical memory, in bytes, that the
|
* @return The minimum amount of physical memory, in bytes, that the
|
||||||
* operating system will try to maintain under low memory
|
* 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, -1 will be
|
||||||
* returned.
|
* returned. Returns -2 if the metric is not supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public long getMemorySoftLimit();
|
public long getMemorySoftLimit();
|
||||||
|
@ -492,7 +368,8 @@ public interface Metrics {
|
||||||
* Returns the number of block I/O requests to the disk that have been
|
* Returns the number of block I/O requests to the disk that have been
|
||||||
* issued by the Isolation Group.
|
* issued by the Isolation Group.
|
||||||
*
|
*
|
||||||
* @return The count of requests or 0 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();
|
public long getBlkIOServiceCount();
|
||||||
|
@ -501,7 +378,8 @@ public interface Metrics {
|
||||||
* Returns the number of block I/O bytes that have been transferred
|
* Returns the number of block I/O bytes that have been transferred
|
||||||
* to/from the disk by the Isolation Group.
|
* 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 -1 if the metric is not
|
||||||
|
* available. Returns -2 if this metric is not supported.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public long getBlkIOServiced();
|
public long getBlkIOServiced();
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -45,10 +45,10 @@ import java.io.PrintStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.lang.module.Configuration;
|
import java.lang.module.Configuration;
|
||||||
import java.lang.module.ModuleDescriptor;
|
import java.lang.module.ModuleDescriptor;
|
||||||
import java.lang.module.ModuleDescriptor.Requires;
|
|
||||||
import java.lang.module.ModuleDescriptor.Exports;
|
import java.lang.module.ModuleDescriptor.Exports;
|
||||||
import java.lang.module.ModuleDescriptor.Opens;
|
import java.lang.module.ModuleDescriptor.Opens;
|
||||||
import java.lang.module.ModuleDescriptor.Provides;
|
import java.lang.module.ModuleDescriptor.Provides;
|
||||||
|
import java.lang.module.ModuleDescriptor.Requires;
|
||||||
import java.lang.module.ModuleFinder;
|
import java.lang.module.ModuleFinder;
|
||||||
import java.lang.module.ModuleReference;
|
import java.lang.module.ModuleReference;
|
||||||
import java.lang.module.ResolvedModule;
|
import java.lang.module.ResolvedModule;
|
||||||
|
@ -62,8 +62,8 @@ import java.nio.charset.Charset;
|
||||||
import java.nio.file.DirectoryStream;
|
import java.nio.file.DirectoryStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.text.Normalizer;
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
|
import java.text.Normalizer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
@ -119,6 +119,7 @@ public final class LauncherHelper {
|
||||||
|
|
||||||
private static final String defaultBundleName =
|
private static final String defaultBundleName =
|
||||||
"sun.launcher.resources.launcher";
|
"sun.launcher.resources.launcher";
|
||||||
|
|
||||||
private static class ResourceBundleHolder {
|
private static class ResourceBundleHolder {
|
||||||
private static final ResourceBundle RB =
|
private static final ResourceBundle RB =
|
||||||
ResourceBundle.getBundle(defaultBundleName);
|
ResourceBundle.getBundle(defaultBundleName);
|
||||||
|
@ -323,91 +324,110 @@ public final class LauncherHelper {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final long longRetvalNotSupported = -2;
|
||||||
|
|
||||||
ostream.println(INDENT + "Provider: " + c.getProvider());
|
ostream.println(INDENT + "Provider: " + c.getProvider());
|
||||||
ostream.println(INDENT + "Effective CPU Count: " + c.getEffectiveCpuCount());
|
ostream.println(INDENT + "Effective CPU Count: " + c.getEffectiveCpuCount());
|
||||||
ostream.println(INDENT + "CPU Period: " + c.getCpuPeriod() +
|
ostream.println(formatCpuVal(c.getCpuPeriod(), INDENT + "CPU Period: ", longRetvalNotSupported));
|
||||||
(c.getCpuPeriod() == -1 ? "" : "us"));
|
ostream.println(formatCpuVal(c.getCpuQuota(), INDENT + "CPU Quota: ", longRetvalNotSupported));
|
||||||
ostream.println(INDENT + "CPU Quota: " + c.getCpuQuota() +
|
ostream.println(formatCpuVal(c.getCpuShares(), INDENT + "CPU Shares: ", longRetvalNotSupported));
|
||||||
(c.getCpuQuota() == -1 ? "" : "us"));
|
|
||||||
ostream.println(INDENT + "CPU Shares: " + c.getCpuShares());
|
|
||||||
|
|
||||||
int cpus[] = c.getCpuSetCpus();
|
int cpus[] = c.getCpuSetCpus();
|
||||||
ostream.println(INDENT + "List of Processors, "
|
if (cpus != null) {
|
||||||
+ cpus.length + " total: ");
|
ostream.println(INDENT + "List of Processors, "
|
||||||
|
+ cpus.length + " total: ");
|
||||||
|
|
||||||
ostream.print(INDENT);
|
ostream.print(INDENT);
|
||||||
for (int i = 0; i < cpus.length; i++) {
|
for (int i = 0; i < cpus.length; i++) {
|
||||||
ostream.print(cpus[i] + " ");
|
ostream.print(cpus[i] + " ");
|
||||||
}
|
}
|
||||||
if (cpus.length > 0) {
|
if (cpus.length > 0) {
|
||||||
ostream.println("");
|
ostream.println("");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ostream.println(INDENT + "List of Processors: N/A");
|
||||||
}
|
}
|
||||||
|
|
||||||
cpus = c.getEffectiveCpuSetCpus();
|
cpus = c.getEffectiveCpuSetCpus();
|
||||||
ostream.println(INDENT + "List of Effective Processors, "
|
if (cpus != null) {
|
||||||
+ cpus.length + " total: ");
|
ostream.println(INDENT + "List of Effective Processors, "
|
||||||
|
+ cpus.length + " total: ");
|
||||||
|
|
||||||
ostream.print(INDENT);
|
ostream.print(INDENT);
|
||||||
for (int i = 0; i < cpus.length; i++) {
|
for (int i = 0; i < cpus.length; i++) {
|
||||||
ostream.print(cpus[i] + " ");
|
ostream.print(cpus[i] + " ");
|
||||||
}
|
}
|
||||||
if (cpus.length > 0) {
|
if (cpus.length > 0) {
|
||||||
ostream.println("");
|
ostream.println("");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ostream.println(INDENT + "List of Effective Processors: N/A");
|
||||||
}
|
}
|
||||||
|
|
||||||
int mems[] = c.getCpuSetMems();
|
int mems[] = c.getCpuSetMems();
|
||||||
ostream.println(INDENT + "List of Memory Nodes, "
|
if (mems != null) {
|
||||||
+ mems.length + " total: ");
|
ostream.println(INDENT + "List of Memory Nodes, "
|
||||||
|
+ mems.length + " total: ");
|
||||||
|
|
||||||
ostream.print(INDENT);
|
ostream.print(INDENT);
|
||||||
for (int i = 0; i < mems.length; i++) {
|
for (int i = 0; i < mems.length; i++) {
|
||||||
ostream.print(mems[i] + " ");
|
ostream.print(mems[i] + " ");
|
||||||
}
|
}
|
||||||
if (mems.length > 0) {
|
if (mems.length > 0) {
|
||||||
ostream.println("");
|
ostream.println("");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ostream.println(INDENT + "List of Memory Nodes: N/A");
|
||||||
}
|
}
|
||||||
|
|
||||||
mems = c.getEffectiveCpuSetMems();
|
mems = c.getEffectiveCpuSetMems();
|
||||||
ostream.println(INDENT + "List of Available Memory Nodes, "
|
if (mems != null) {
|
||||||
+ mems.length + " total: ");
|
ostream.println(INDENT + "List of Available Memory Nodes, "
|
||||||
|
+ mems.length + " total: ");
|
||||||
|
|
||||||
ostream.print(INDENT);
|
ostream.print(INDENT);
|
||||||
for (int i = 0; i < mems.length; i++) {
|
for (int i = 0; i < mems.length; i++) {
|
||||||
ostream.print(mems[i] + " ");
|
ostream.print(mems[i] + " ");
|
||||||
|
}
|
||||||
|
if (mems.length > 0) {
|
||||||
|
ostream.println("");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ostream.println(INDENT + "List of Available Memory Nodes: N/A");
|
||||||
}
|
}
|
||||||
if (mems.length > 0) {
|
|
||||||
ostream.println("");
|
|
||||||
}
|
|
||||||
|
|
||||||
ostream.println(INDENT + "CPUSet Memory Pressure Enabled: "
|
|
||||||
+ c.isCpuSetMemoryPressureEnabled());
|
|
||||||
|
|
||||||
long limit = c.getMemoryLimit();
|
long limit = c.getMemoryLimit();
|
||||||
ostream.println(INDENT + "Memory Limit: " +
|
ostream.println(formatLimitString(limit, INDENT + "Memory Limit: ", longRetvalNotSupported));
|
||||||
((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited"));
|
|
||||||
|
|
||||||
limit = c.getMemorySoftLimit();
|
limit = c.getMemorySoftLimit();
|
||||||
ostream.println(INDENT + "Memory Soft Limit: " +
|
ostream.println(formatLimitString(limit, INDENT + "Memory Soft Limit: ", longRetvalNotSupported));
|
||||||
((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited"));
|
|
||||||
|
|
||||||
limit = c.getMemoryAndSwapLimit();
|
limit = c.getMemoryAndSwapLimit();
|
||||||
ostream.println(INDENT + "Memory & Swap Limit: " +
|
ostream.println(formatLimitString(limit, INDENT + "Memory & Swap Limit: ", longRetvalNotSupported));
|
||||||
((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited"));
|
|
||||||
|
|
||||||
limit = c.getKernelMemoryLimit();
|
|
||||||
ostream.println(INDENT + "Kernel Memory Limit: " +
|
|
||||||
((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited"));
|
|
||||||
|
|
||||||
limit = c.getTcpMemoryLimit();
|
|
||||||
ostream.println(INDENT + "TCP Memory Limit: " +
|
|
||||||
((limit >= 0) ? SizePrefix.scaleValue(limit) : "Unlimited"));
|
|
||||||
|
|
||||||
ostream.println(INDENT + "Out Of Memory Killer Enabled: "
|
|
||||||
+ c.isMemoryOOMKillEnabled());
|
|
||||||
|
|
||||||
ostream.println("");
|
ostream.println("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String formatLimitString(long limit, String prefix, long unavailable) {
|
||||||
|
if (limit >= 0) {
|
||||||
|
return prefix + SizePrefix.scaleValue(limit);
|
||||||
|
} else if (limit == unavailable) {
|
||||||
|
return prefix + "N/A";
|
||||||
|
} else {
|
||||||
|
return prefix + "Unlimited";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String formatCpuVal(long cpuVal, String prefix, long unavailable) {
|
||||||
|
if (cpuVal >= 0) {
|
||||||
|
return prefix + cpuVal + "us";
|
||||||
|
} else if (cpuVal == unavailable) {
|
||||||
|
return prefix + "N/A";
|
||||||
|
} else {
|
||||||
|
return prefix + cpuVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private enum SizePrefix {
|
private enum SizePrefix {
|
||||||
|
|
||||||
KILO(1024, "K"),
|
KILO(1024, "K"),
|
||||||
|
|
|
@ -171,7 +171,7 @@ class OperatingSystemImpl extends BaseOperatingSystemImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isCpuSetSameAsHostCpuSet() {
|
private boolean isCpuSetSameAsHostCpuSet() {
|
||||||
if (containerMetrics != null) {
|
if (containerMetrics != null && containerMetrics.getCpuSetCpus() != null) {
|
||||||
return containerMetrics.getCpuSetCpus().length == getHostConfiguredCpuCount0();
|
return containerMetrics.getCpuSetCpus().length == getHostConfiguredCpuCount0();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -43,14 +43,7 @@ public class TestCgroupMetrics {
|
||||||
}
|
}
|
||||||
|
|
||||||
MetricsTester metricsTester = new MetricsTester();
|
MetricsTester metricsTester = new MetricsTester();
|
||||||
metricsTester.setup();
|
metricsTester.testAll(metrics);
|
||||||
metricsTester.testCpuAccounting();
|
|
||||||
metricsTester.testCpuSchedulingMetrics();
|
|
||||||
metricsTester.testCpuSets();
|
|
||||||
metricsTester.testMemorySubsystem();
|
|
||||||
metricsTester.testBlkIO();
|
|
||||||
metricsTester.testCpuConsumption();
|
|
||||||
metricsTester.testMemoryUsage();
|
|
||||||
System.out.println("TEST PASSED!!!");
|
System.out.println("TEST PASSED!!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,218 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -143,6 +143,11 @@ public class MetricsCpuTester {
|
||||||
|
|
||||||
private static void testCpuShares(long shares) {
|
private static void testCpuShares(long shares) {
|
||||||
Metrics metrics = Metrics.systemMetrics();
|
Metrics metrics = Metrics.systemMetrics();
|
||||||
|
if ("cgroupv2".equals(metrics.getProvider()) && shares < 1024) {
|
||||||
|
// Adjust input shares for < 1024 cpu shares as the
|
||||||
|
// impl. rounds up to the next multiple of 1024
|
||||||
|
shares = 1024;
|
||||||
|
}
|
||||||
long newShares = metrics.getCpuShares();
|
long newShares = metrics.getCpuShares();
|
||||||
if (newShares != shares) {
|
if (newShares != shares) {
|
||||||
throw new RuntimeException("CPU shares not equal, expected : ["
|
throw new RuntimeException("CPU shares not equal, expected : ["
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -22,7 +22,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import jdk.internal.platform.Metrics;
|
import jdk.internal.platform.Metrics;
|
||||||
|
import jdk.internal.platform.CgroupV1Metrics;
|
||||||
|
|
||||||
public class MetricsMemoryTester {
|
public class MetricsMemoryTester {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
@ -65,9 +67,11 @@ public class MetricsMemoryTester {
|
||||||
|
|
||||||
// Allocate 512M of data
|
// Allocate 512M of data
|
||||||
byte[][] bytes = new byte[64][];
|
byte[][] bytes = new byte[64][];
|
||||||
|
boolean atLeastOneAllocationWorked = false;
|
||||||
for (int i = 0; i < 64; i++) {
|
for (int i = 0; i < 64; i++) {
|
||||||
try {
|
try {
|
||||||
bytes[i] = new byte[8 * 1024 * 1024];
|
bytes[i] = new byte[8 * 1024 * 1024];
|
||||||
|
atLeastOneAllocationWorked = true;
|
||||||
// Break out as soon as we see an increase in failcount
|
// Break out as soon as we see an increase in failcount
|
||||||
// to avoid getting killed by the OOM killer.
|
// to avoid getting killed by the OOM killer.
|
||||||
if (Metrics.systemMetrics().getMemoryFailCount() > count) {
|
if (Metrics.systemMetrics().getMemoryFailCount() > count) {
|
||||||
|
@ -77,6 +81,12 @@ public class MetricsMemoryTester {
|
||||||
break;
|
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) {
|
if (Metrics.systemMetrics().getMemoryFailCount() <= count) {
|
||||||
throw new RuntimeException("Memory fail count : new : ["
|
throw new RuntimeException("Memory fail count : new : ["
|
||||||
+ Metrics.systemMetrics().getMemoryFailCount() + "]"
|
+ Metrics.systemMetrics().getMemoryFailCount() + "]"
|
||||||
|
@ -99,14 +109,20 @@ public class MetricsMemoryTester {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void testKernelMemoryLimit(String value) {
|
private static void testKernelMemoryLimit(String value) {
|
||||||
long limit = getMemoryValue(value);
|
Metrics m = Metrics.systemMetrics();
|
||||||
long kmemlimit = Metrics.systemMetrics().getKernelMemoryLimit();
|
if (m instanceof CgroupV1Metrics) {
|
||||||
if (kmemlimit != 0 && limit != kmemlimit) {
|
CgroupV1Metrics mCgroupV1 = (CgroupV1Metrics)m;
|
||||||
throw new RuntimeException("Kernel Memory limit not equal, expected : ["
|
System.out.println("TEST PASSED!!!");
|
||||||
+ limit + "]" + ", got : ["
|
long limit = getMemoryValue(value);
|
||||||
+ kmemlimit + "]");
|
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");
|
||||||
}
|
}
|
||||||
System.out.println("TEST PASSED!!!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void testMemoryAndSwapLimit(String memory, String memAndSwap) {
|
private static void testMemoryAndSwapLimit(String memory, String memAndSwap) {
|
||||||
|
@ -138,9 +154,17 @@ public class MetricsMemoryTester {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void testOomKillFlag(boolean oomKillFlag) {
|
private static void testOomKillFlag(boolean oomKillFlag) {
|
||||||
if (!(oomKillFlag ^ Metrics.systemMetrics().isMemoryOOMKillEnabled())) {
|
Metrics m = Metrics.systemMetrics();
|
||||||
throw new RuntimeException("oomKillFlag error");
|
if (m instanceof CgroupV1Metrics) {
|
||||||
|
CgroupV1Metrics mCgroupV1 = (CgroupV1Metrics)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!!!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -78,10 +78,10 @@ public class TestDockerCpuMetrics {
|
||||||
|
|
||||||
int[] cpuSetMems = Metrics.systemMetrics().getCpuSetMems();
|
int[] cpuSetMems = Metrics.systemMetrics().getCpuSetMems();
|
||||||
String memNodes = null;
|
String memNodes = null;
|
||||||
if (cpuSetMems.length > 1) {
|
if (cpuSetMems != null && cpuSetMems.length > 1) {
|
||||||
int endNode = (cpuSetMems[cpuSetMems.length - 1] - cpuSetMems[0]) / 2 + cpuSetMems[0];
|
int endNode = (cpuSetMems[cpuSetMems.length - 1] - cpuSetMems[0]) / 2 + cpuSetMems[0];
|
||||||
memNodes = cpuSetMems[0] + "-" + endNode;
|
memNodes = cpuSetMems[0] + "-" + endNode;
|
||||||
} else if (cpuSetMems.length == 1) {
|
} else if (cpuSetMems != null && cpuSetMems.length == 1) {
|
||||||
memNodes = cpuSetMems[0] + "";
|
memNodes = cpuSetMems[0] + "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import jdk.internal.platform.Metrics;
|
||||||
import jdk.test.lib.Utils;
|
import jdk.test.lib.Utils;
|
||||||
import jdk.test.lib.containers.docker.Common;
|
import jdk.test.lib.containers.docker.Common;
|
||||||
import jdk.test.lib.containers.docker.DockerRunOptions;
|
import jdk.test.lib.containers.docker.DockerRunOptions;
|
||||||
|
@ -58,10 +59,21 @@ public class TestDockerMemoryMetrics {
|
||||||
testMemoryAndSwapLimit("200m", "1g");
|
testMemoryAndSwapLimit("200m", "1g");
|
||||||
testMemoryAndSwapLimit("100m", "200m");
|
testMemoryAndSwapLimit("100m", "200m");
|
||||||
|
|
||||||
testKernelMemoryLimit("100m");
|
Metrics m = Metrics.systemMetrics();
|
||||||
testKernelMemoryLimit("1g");
|
// 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);
|
testOomKillFlag("100m", false);
|
||||||
|
} else {
|
||||||
|
System.out.println("kernel memory tests and OOM Kill flag tests not " +
|
||||||
|
"possible with cgroupv2.");
|
||||||
|
}
|
||||||
|
}
|
||||||
testOomKillFlag("100m", true);
|
testOomKillFlag("100m", true);
|
||||||
|
|
||||||
testMemoryFailCount("64m");
|
testMemoryFailCount("64m");
|
||||||
|
@ -69,7 +81,9 @@ public class TestDockerMemoryMetrics {
|
||||||
testMemorySoftLimit("500m","200m");
|
testMemorySoftLimit("500m","200m");
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
DockerTestUtils.removeDockerImage(imageName);
|
if (!DockerTestUtils.RETAIN_IMAGE_AFTER_TEST) {
|
||||||
|
DockerTestUtils.removeDockerImage(imageName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +161,8 @@ public class TestDockerMemoryMetrics {
|
||||||
opts.addJavaOpts("-cp", "/test-classes/")
|
opts.addJavaOpts("-cp", "/test-classes/")
|
||||||
.addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED")
|
.addJavaOpts("--add-exports", "java.base/jdk.internal.platform=ALL-UNNAMED")
|
||||||
.addClassOptions("memory", value, oomKillFlag + "");
|
.addClassOptions("memory", value, oomKillFlag + "");
|
||||||
DockerTestUtils.dockerRunJava(opts).shouldHaveExitValue(0).shouldContain("TEST PASSED!!!");
|
OutputAnalyzer oa = DockerTestUtils.dockerRunJava(opts);
|
||||||
|
oa.shouldHaveExitValue(0).shouldContain("TEST PASSED!!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void testMemorySoftLimit(String mem, String softLimit) throws Exception {
|
private static void testMemorySoftLimit(String mem, String softLimit) throws Exception {
|
||||||
|
|
|
@ -32,6 +32,7 @@ import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import jdk.test.lib.Asserts;
|
import jdk.test.lib.Asserts;
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,8 +55,7 @@ public class CPUSetsReader {
|
||||||
|
|
||||||
public static int getNumCpus() {
|
public static int getNumCpus() {
|
||||||
String path = "/proc/cpuinfo";
|
String path = "/proc/cpuinfo";
|
||||||
try {
|
try (Stream<String> stream = Files.lines(Paths.get(path))) {
|
||||||
Stream<String> stream = Files.lines(Paths.get(path));
|
|
||||||
return (int) stream.filter(line -> line.startsWith("processor")).count();
|
return (int) stream.filter(line -> line.startsWith("processor")).count();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
111
test/lib/jdk/test/lib/containers/cgroup/CgroupMetricsTester.java
Normal file
111
test/lib/jdk/test/lib/containers/cgroup/CgroupMetricsTester.java
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.test.lib.containers.cgroup;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
interface CgroupMetricsTester {
|
||||||
|
|
||||||
|
public static final double ERROR_MARGIN = 0.1;
|
||||||
|
public static final String EMPTY_STR = "";
|
||||||
|
|
||||||
|
public void testMemorySubsystem();
|
||||||
|
public void testCpuAccounting();
|
||||||
|
public void testCpuSchedulingMetrics();
|
||||||
|
public void testCpuSets();
|
||||||
|
public void testCpuConsumption() throws IOException, InterruptedException;
|
||||||
|
public void testMemoryUsage() throws Exception;
|
||||||
|
public void testMisc();
|
||||||
|
|
||||||
|
public static long convertStringToLong(String strval, long overflowRetval) {
|
||||||
|
long retval = 0;
|
||||||
|
if (strval == null) return 0L;
|
||||||
|
|
||||||
|
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 Long.MAX_VALUE
|
||||||
|
BigInteger b = new BigInteger(strval);
|
||||||
|
if (b.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) {
|
||||||
|
return overflowRetval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean compareWithErrorMargin(long oldVal, long newVal) {
|
||||||
|
return Math.abs(oldVal - newVal) <= Math.abs(oldVal * ERROR_MARGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean compareWithErrorMargin(double oldVal, double newVal) {
|
||||||
|
return Math.abs(oldVal - newVal) <= Math.abs(oldVal * ERROR_MARGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void fail(String controller, String metric, long oldVal, long testVal) {
|
||||||
|
throw new RuntimeException("Test failed for - " + controller + ":"
|
||||||
|
+ metric + ", expected [" + oldVal + "], got [" + testVal + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void fail(String controller, String metric, String oldVal, String testVal) {
|
||||||
|
throw new RuntimeException("Test failed for - " + controller + ":"
|
||||||
|
+ metric + ", expected [" + oldVal + "], got [" + testVal + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void fail(String controller, String metric, double oldVal, double testVal) {
|
||||||
|
throw new RuntimeException("Test failed for - " + controller + ":"
|
||||||
|
+ metric + ", expected [" + oldVal + "], got [" + testVal + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void fail(String controller, String metric, boolean oldVal, boolean testVal) {
|
||||||
|
throw new RuntimeException("Test failed for - " + controller + ":"
|
||||||
|
+ metric + ", expected [" + oldVal + "], got [" + testVal + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void warn(String controller, String metric, long oldVal, long testVal) {
|
||||||
|
System.err.println("Warning - " + controller + ":" + metric
|
||||||
|
+ ", expected [" + oldVal + "], got [" + testVal + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Integer[] convertCpuSetsToArray(String cpusstr) {
|
||||||
|
if (cpusstr == null || EMPTY_STR.equals(cpusstr)) {
|
||||||
|
return new Integer[0];
|
||||||
|
}
|
||||||
|
// Parse range string in the format 1,2-6,7
|
||||||
|
Integer[] cpuSets = Stream.of(cpusstr.split(",")).flatMap(a -> {
|
||||||
|
if (a.contains("-")) {
|
||||||
|
String[] range = a.split("-");
|
||||||
|
return IntStream.rangeClosed(Integer.parseInt(range[0]),
|
||||||
|
Integer.parseInt(range[1])).boxed();
|
||||||
|
} else {
|
||||||
|
return Stream.of(Integer.parseInt(a));
|
||||||
|
}
|
||||||
|
}).toArray(Integer[]::new);
|
||||||
|
return cpuSets;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2020, Red Hat Inc.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -21,609 +21,58 @@
|
||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
package jdk.test.lib.containers.cgroup;
|
package jdk.test.lib.containers.cgroup;
|
||||||
|
|
||||||
import java.io.File;
|
import java.util.Objects;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Scanner;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.IntStream;
|
|
||||||
import java.util.stream.LongStream;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
import jdk.internal.platform.Metrics;
|
import jdk.internal.platform.Metrics;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cgroup version agnostic metrics tester
|
||||||
|
*
|
||||||
|
*/
|
||||||
public class MetricsTester {
|
public class MetricsTester {
|
||||||
|
|
||||||
private static final double ERROR_MARGIN = 0.1;
|
private static final String CGROUP_V1 = "cgroupv1";
|
||||||
private static long unlimited_minimum = 0x7FFFFFFFFF000000L;
|
private static final String CGROUP_V2 = "cgroupv2";
|
||||||
long startSysVal;
|
|
||||||
long startUserVal;
|
|
||||||
long startUsage;
|
|
||||||
long startPerCpu[];
|
|
||||||
|
|
||||||
enum SubSystem {
|
private static CgroupMetricsTester createInstance(Metrics m) {
|
||||||
MEMORY("memory"),
|
Objects.requireNonNull(m);
|
||||||
CPUSET("cpuset"),
|
if (CGROUP_V1.equals(m.getProvider())) {
|
||||||
CPU("cpu"),
|
MetricsTesterCgroupV1 t = new MetricsTesterCgroupV1();
|
||||||
CPUACCT("cpuacct"),
|
t.setup();
|
||||||
BLKIO("blkio");
|
return t;
|
||||||
|
} else if (CGROUP_V2.equals(m.getProvider())) {
|
||||||
private String value;
|
return new MetricsTesterCgroupV2();
|
||||||
|
} else {
|
||||||
SubSystem(String value) {
|
System.err.println("WARNING: Metrics provider, '" + m.getProvider()
|
||||||
this.value = value;
|
+ "' is unknown!");
|
||||||
}
|
return null;
|
||||||
|
|
||||||
public String value() {
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Set<String> allowedSubSystems =
|
public void testAll(Metrics m) throws Exception {
|
||||||
Stream.of(SubSystem.values()).map(SubSystem::value).collect(Collectors.toSet());
|
CgroupMetricsTester tester = createInstance(m);
|
||||||
|
tester.testCpuAccounting();
|
||||||
private static final Map<String, String[]> subSystemPaths = new HashMap<>();
|
tester.testCpuConsumption();
|
||||||
|
tester.testCpuSchedulingMetrics();
|
||||||
private static void setPath(String[] line) {
|
tester.testCpuSets();
|
||||||
String cgroupPath = line[2];
|
tester.testMemorySubsystem();
|
||||||
String[] subSystems = line[1].split(",");
|
tester.testMemoryUsage();
|
||||||
|
tester.testMisc();
|
||||||
for (String subSystem : subSystems) {
|
|
||||||
if (allowedSubSystems.contains(subSystem)) {
|
|
||||||
String[] paths = subSystemPaths.get(subSystem);
|
|
||||||
String finalPath = "";
|
|
||||||
String root = paths[0];
|
|
||||||
String mountPoint = paths[1];
|
|
||||||
if (root != null && cgroupPath != null) {
|
|
||||||
if (root.equals("/")) {
|
|
||||||
if (!cgroupPath.equals("/")) {
|
|
||||||
finalPath = mountPoint + cgroupPath;
|
|
||||||
} else {
|
|
||||||
finalPath = mountPoint;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (root.equals(cgroupPath)) {
|
|
||||||
finalPath = mountPoint;
|
|
||||||
} else {
|
|
||||||
if (cgroupPath.startsWith(root)) {
|
|
||||||
if (cgroupPath.length() > root.length()) {
|
|
||||||
String cgroupSubstr = cgroupPath.substring(root.length());
|
|
||||||
finalPath = mountPoint + cgroupSubstr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subSystemPaths.put(subSystem, new String[]{finalPath, mountPoint});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void createSubsystems(String[] line) {
|
|
||||||
if (line.length < 5) return;
|
|
||||||
Path p = Paths.get(line[4]);
|
|
||||||
String subsystemName = p.getFileName().toString();
|
|
||||||
if (subsystemName != null) {
|
|
||||||
for (String subSystem : subsystemName.split(",")) {
|
|
||||||
if (allowedSubSystems.contains(subSystem)) {
|
|
||||||
subSystemPaths.put(subSystem, new String[]{line[3], line[4]});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setup() {
|
|
||||||
Metrics metrics = Metrics.systemMetrics();
|
|
||||||
// Initialize CPU usage metrics before we do any testing.
|
|
||||||
startSysVal = metrics.getCpuSystemUsage();
|
|
||||||
startUserVal = metrics.getCpuUserUsage();
|
|
||||||
startUsage = metrics.getCpuUsage();
|
|
||||||
startPerCpu = metrics.getPerCpuUsage();
|
|
||||||
|
|
||||||
try {
|
|
||||||
Stream<String> lines = Files.lines(Paths.get("/proc/self/mountinfo"));
|
|
||||||
lines.filter(line -> line.contains(" - cgroup cgroup "))
|
|
||||||
.map(line -> line.split(" "))
|
|
||||||
.forEach(MetricsTester::createSubsystems);
|
|
||||||
lines.close();
|
|
||||||
|
|
||||||
lines = Files.lines(Paths.get("/proc/self/cgroup"));
|
|
||||||
lines.map(line -> line.split(":"))
|
|
||||||
.filter(line -> (line.length >= 3))
|
|
||||||
.forEach(MetricsTester::setPath);
|
|
||||||
lines.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getFileContents(SubSystem subSystem, String fileName) {
|
|
||||||
String fname = subSystemPaths.get(subSystem.value())[0] + File.separator + fileName;
|
|
||||||
try {
|
|
||||||
return new Scanner(new File(fname)).useDelimiter("\\Z").next();
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
System.err.println("Unable to open : " + fname);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static long getLongValueFromFile(SubSystem subSystem, String fileName) {
|
|
||||||
String data = getFileContents(subSystem, fileName);
|
|
||||||
return data.isEmpty() ? 0L : convertStringToLong(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static long convertStringToLong(String strval) {
|
|
||||||
long retval = 0;
|
|
||||||
if (strval == null) return 0L;
|
|
||||||
|
|
||||||
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 Long.MAX_VALUE
|
|
||||||
BigInteger b = new BigInteger(strval);
|
|
||||||
if (b.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) {
|
|
||||||
return Long.MAX_VALUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static long getLongValueFromFile(SubSystem subSystem, String metric, String subMetric) {
|
|
||||||
String stats = getFileContents(subSystem, metric);
|
|
||||||
String[] tokens = stats.split("[\\r\\n]+");
|
|
||||||
for (int i = 0; i < tokens.length; i++) {
|
|
||||||
if (tokens[i].startsWith(subMetric)) {
|
|
||||||
String strval = tokens[i].split("\\s+")[1];
|
|
||||||
return convertStringToLong(strval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0L;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double getDoubleValueFromFile(SubSystem subSystem, String fileName) {
|
|
||||||
String data = getFileContents(subSystem, fileName);
|
|
||||||
return data.isEmpty() ? 0.0 : Double.parseDouble(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean compareWithErrorMargin(long oldVal, long newVal) {
|
|
||||||
return Math.abs(oldVal - newVal) <= Math.abs(oldVal * ERROR_MARGIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean compareWithErrorMargin(double oldVal, double newVal) {
|
|
||||||
return Math.abs(oldVal - newVal) <= Math.abs(oldVal * ERROR_MARGIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void fail(SubSystem system, String metric, long oldVal, long testVal) {
|
|
||||||
throw new RuntimeException("Test failed for - " + system.value + ":"
|
|
||||||
+ metric + ", expected [" + oldVal + "], got [" + testVal + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void fail(SubSystem system, String metric, String oldVal, String testVal) {
|
|
||||||
throw new RuntimeException("Test failed for - " + system.value + ":"
|
|
||||||
+ metric + ", expected [" + oldVal + "], got [" + testVal + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void fail(SubSystem system, String metric, double oldVal, double testVal) {
|
|
||||||
throw new RuntimeException("Test failed for - " + system.value + ":"
|
|
||||||
+ metric + ", expected [" + oldVal + "], got [" + testVal + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void fail(SubSystem system, String metric, boolean oldVal, boolean testVal) {
|
|
||||||
throw new RuntimeException("Test failed for - " + system.value + ":"
|
|
||||||
+ metric + ", expected [" + oldVal + "], got [" + testVal + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void warn(SubSystem system, String metric, long oldVal, long testVal) {
|
|
||||||
System.err.println("Warning - " + system.value + ":" + metric
|
|
||||||
+ ", expected [" + oldVal + "], got [" + testVal + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testMemorySubsystem() {
|
|
||||||
Metrics metrics = Metrics.systemMetrics();
|
|
||||||
|
|
||||||
// User Memory
|
|
||||||
long oldVal = metrics.getMemoryFailCount();
|
|
||||||
long newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.failcnt");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.failcnt", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getMemoryLimit();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.limit_in_bytes");
|
|
||||||
newVal = newVal > unlimited_minimum ? -1L : newVal;
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.limit_in_bytes", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getMemoryMaxUsage();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.max_usage_in_bytes");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.max_usage_in_bytes", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getMemoryUsage();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.usage_in_bytes");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.usage_in_bytes", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kernel memory
|
|
||||||
oldVal = metrics.getKernelMemoryFailCount();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.failcnt");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.kmem.failcnt", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getKernelMemoryLimit();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.limit_in_bytes");
|
|
||||||
newVal = newVal > unlimited_minimum ? -1L : newVal;
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.kmem.limit_in_bytes", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getKernelMemoryMaxUsage();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.max_usage_in_bytes");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.kmem.max_usage_in_bytes", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getKernelMemoryUsage();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.usage_in_bytes");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.kmem.usage_in_bytes", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TCP Memory
|
|
||||||
oldVal = metrics.getTcpMemoryFailCount();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.tcp.failcnt");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.kmem.tcp.failcnt", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getTcpMemoryLimit();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.tcp.limit_in_bytes");
|
|
||||||
newVal = newVal > unlimited_minimum ? -1L : newVal;
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.kmem.tcp.limit_in_bytes", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getTcpMemoryMaxUsage();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.tcp.max_usage_in_bytes");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.kmem.tcp.max_usage_in_bytes", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getTcpMemoryUsage();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.kmem.tcp.usage_in_bytes");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.kmem.tcp.usage_in_bytes", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Memory and Swap
|
|
||||||
oldVal = metrics.getMemoryAndSwapFailCount();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.memsw.failcnt");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.memsw.failcnt", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getMemoryAndSwapLimit();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.memsw.limit_in_bytes");
|
|
||||||
newVal = newVal > unlimited_minimum ? -1L : newVal;
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.memsw.limit_in_bytes", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getMemoryAndSwapMaxUsage();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.memsw.max_usage_in_bytes");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.memsw.max_usage_in_bytes", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getMemoryAndSwapUsage();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.memsw.usage_in_bytes");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.memsw.usage_in_bytes", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getMemorySoftLimit();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.MEMORY, "memory.soft_limit_in_bytes");
|
|
||||||
newVal = newVal > unlimited_minimum ? -1L : newVal;
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.MEMORY, "memory.soft_limit_in_bytes", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean oomKillEnabled = metrics.isMemoryOOMKillEnabled();
|
|
||||||
boolean newOomKillEnabled = getLongValueFromFile(SubSystem.MEMORY,
|
|
||||||
"memory.oom_control", "oom_kill_disable") == 0L ? true : false;
|
|
||||||
if (oomKillEnabled != newOomKillEnabled) {
|
|
||||||
throw new RuntimeException("Test failed for - " + SubSystem.MEMORY.value + ":"
|
|
||||||
+ "memory.oom_control:oom_kill_disable" + ", expected ["
|
|
||||||
+ oomKillEnabled + "], got [" + newOomKillEnabled + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCpuAccounting() {
|
|
||||||
Metrics metrics = Metrics.systemMetrics();
|
|
||||||
long oldVal = metrics.getCpuUsage();
|
|
||||||
long newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpuacct.usage");
|
|
||||||
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
warn(SubSystem.CPUACCT, "cpuacct.usage", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
Long[] newVals = Stream.of(getFileContents(SubSystem.CPUACCT, "cpuacct.usage_percpu")
|
|
||||||
.split("\\s+"))
|
|
||||||
.map(Long::parseLong)
|
|
||||||
.toArray(Long[]::new);
|
|
||||||
Long[] oldVals = LongStream.of(metrics.getPerCpuUsage()).boxed().toArray(Long[]::new);
|
|
||||||
for (int i = 0; i < oldVals.length; i++) {
|
|
||||||
if (!compareWithErrorMargin(oldVals[i], newVals[i])) {
|
|
||||||
warn(SubSystem.CPUACCT, "cpuacct.usage_percpu", oldVals[i], newVals[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getCpuUserUsage();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpuacct.stat", "user");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
warn(SubSystem.CPUACCT, "cpuacct.usage - user", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getCpuSystemUsage();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpuacct.stat", "system");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
warn(SubSystem.CPUACCT, "cpuacct.usage - system", oldVal, newVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCpuSchedulingMetrics() {
|
|
||||||
Metrics metrics = Metrics.systemMetrics();
|
|
||||||
long oldVal = metrics.getCpuPeriod();
|
|
||||||
long newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpu.cfs_period_us");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.CPUACCT, "cpu.cfs_period_us", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getCpuQuota();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpu.cfs_quota_us");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.CPUACCT, "cpu.cfs_quota_us", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getCpuShares();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpu.shares");
|
|
||||||
if (newVal == 0 || newVal == 1024) newVal = -1;
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.CPUACCT, "cpu.shares", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getCpuNumPeriods();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpu.stat", "nr_periods");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.CPUACCT, "cpu.stat - nr_periods", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getCpuNumThrottled();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpu.stat", "nr_throttled");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.CPUACCT, "cpu.stat - nr_throttled", oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getCpuThrottledTime();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.CPUACCT, "cpu.stat", "throttled_time");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.CPUACCT, "cpu.stat - throttled_time", oldVal, newVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCpuSets() {
|
|
||||||
Metrics metrics = Metrics.systemMetrics();
|
|
||||||
Integer[] oldVal = Arrays.stream(metrics.getCpuSetCpus()).boxed().toArray(Integer[]::new);
|
|
||||||
Arrays.sort(oldVal);
|
|
||||||
|
|
||||||
String cpusstr = getFileContents(SubSystem.CPUSET, "cpuset.cpus");
|
|
||||||
// Parse range string in the format 1,2-6,7
|
|
||||||
Integer[] newVal = Stream.of(cpusstr.split(",")).flatMap(a -> {
|
|
||||||
if (a.contains("-")) {
|
|
||||||
String[] range = a.split("-");
|
|
||||||
return IntStream.rangeClosed(Integer.parseInt(range[0]),
|
|
||||||
Integer.parseInt(range[1])).boxed();
|
|
||||||
} else {
|
|
||||||
return Stream.of(Integer.parseInt(a));
|
|
||||||
}
|
|
||||||
}).toArray(Integer[]::new);
|
|
||||||
Arrays.sort(newVal);
|
|
||||||
if (Arrays.compare(oldVal, newVal) != 0) {
|
|
||||||
fail(SubSystem.CPUSET, "cpuset.cpus", Arrays.toString(oldVal),
|
|
||||||
Arrays.toString(newVal));
|
|
||||||
}
|
|
||||||
|
|
||||||
int [] cpuSets = metrics.getEffectiveCpuSetCpus();
|
|
||||||
|
|
||||||
// Skip this test if this metric is supported on this platform
|
|
||||||
if (cpuSets.length != 0) {
|
|
||||||
oldVal = Arrays.stream(cpuSets).boxed().toArray(Integer[]::new);
|
|
||||||
Arrays.sort(oldVal);
|
|
||||||
cpusstr = getFileContents(SubSystem.CPUSET, "cpuset.effective_cpus");
|
|
||||||
newVal = Stream.of(cpusstr.split(",")).flatMap(a -> {
|
|
||||||
if (a.contains("-")) {
|
|
||||||
String[] range = a.split("-");
|
|
||||||
return IntStream.rangeClosed(Integer.parseInt(range[0]),
|
|
||||||
Integer.parseInt(range[1])).boxed();
|
|
||||||
} else {
|
|
||||||
return Stream.of(Integer.parseInt(a));
|
|
||||||
}
|
|
||||||
}).toArray(Integer[]::new);
|
|
||||||
Arrays.sort(newVal);
|
|
||||||
if (Arrays.compare(oldVal, newVal) != 0) {
|
|
||||||
fail(SubSystem.CPUSET, "cpuset.effective_cpus", Arrays.toString(oldVal),
|
|
||||||
Arrays.toString(newVal));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = Arrays.stream(metrics.getCpuSetMems()).boxed().toArray(Integer[]::new);
|
|
||||||
Arrays.sort(oldVal);
|
|
||||||
cpusstr = getFileContents(SubSystem.CPUSET, "cpuset.mems");
|
|
||||||
newVal = Stream.of(cpusstr.split(",")).flatMap(a -> {
|
|
||||||
if (a.contains("-")) {
|
|
||||||
String[] range = a.split("-");
|
|
||||||
return IntStream.rangeClosed(Integer.parseInt(range[0]),
|
|
||||||
Integer.parseInt(range[1])).boxed();
|
|
||||||
} else {
|
|
||||||
return Stream.of(Integer.parseInt(a));
|
|
||||||
}
|
|
||||||
}).toArray(Integer[]::new);
|
|
||||||
Arrays.sort(newVal);
|
|
||||||
if (Arrays.compare(oldVal, newVal) != 0) {
|
|
||||||
fail(SubSystem.CPUSET, "cpuset.mems", Arrays.toString(oldVal),
|
|
||||||
Arrays.toString(newVal));
|
|
||||||
}
|
|
||||||
|
|
||||||
int [] cpuSetMems = metrics.getEffectiveCpuSetMems();
|
|
||||||
|
|
||||||
// Skip this test if this metric is supported on this platform
|
|
||||||
if (cpuSetMems.length != 0) {
|
|
||||||
oldVal = Arrays.stream(cpuSetMems).boxed().toArray(Integer[]::new);
|
|
||||||
Arrays.sort(oldVal);
|
|
||||||
cpusstr = getFileContents(SubSystem.CPUSET, "cpuset.effective_mems");
|
|
||||||
newVal = Stream.of(cpusstr.split(",")).flatMap(a -> {
|
|
||||||
if (a.contains("-")) {
|
|
||||||
String[] range = a.split("-");
|
|
||||||
return IntStream.rangeClosed(Integer.parseInt(range[0]),
|
|
||||||
Integer.parseInt(range[1])).boxed();
|
|
||||||
} else {
|
|
||||||
return Stream.of(Integer.parseInt(a));
|
|
||||||
}
|
|
||||||
}).toArray(Integer[]::new);
|
|
||||||
Arrays.sort(newVal);
|
|
||||||
if (Arrays.compare(oldVal, newVal) != 0) {
|
|
||||||
fail(SubSystem.CPUSET, "cpuset.effective_mems", Arrays.toString(oldVal),
|
|
||||||
Arrays.toString(newVal));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
double oldValue = metrics.getCpuSetMemoryPressure();
|
|
||||||
double newValue = getDoubleValueFromFile(SubSystem.CPUSET, "cpuset.memory_pressure");
|
|
||||||
if (!compareWithErrorMargin(oldValue, newValue)) {
|
|
||||||
fail(SubSystem.CPUSET, "cpuset.memory_pressure", oldValue, newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean oldV = metrics.isCpuSetMemoryPressureEnabled();
|
|
||||||
boolean newV = getLongValueFromFile(SubSystem.CPUSET,
|
|
||||||
"cpuset.memory_pressure_enabled") == 1 ? true : false;
|
|
||||||
if (oldV != newV) {
|
|
||||||
fail(SubSystem.CPUSET, "cpuset.memory_pressure_enabled", oldV, newV);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testBlkIO() {
|
|
||||||
Metrics metrics = Metrics.systemMetrics();
|
|
||||||
long oldVal = metrics.getBlkIOServiceCount();
|
|
||||||
long newVal = getLongValueFromFile(SubSystem.BLKIO,
|
|
||||||
"blkio.throttle.io_service_bytes", "Total");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.BLKIO, "blkio.throttle.io_service_bytes - Total",
|
|
||||||
oldVal, newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
oldVal = metrics.getBlkIOServiced();
|
|
||||||
newVal = getLongValueFromFile(SubSystem.BLKIO, "blkio.throttle.io_serviced", "Total");
|
|
||||||
if (!compareWithErrorMargin(oldVal, newVal)) {
|
|
||||||
fail(SubSystem.BLKIO, "blkio.throttle.io_serviced - Total", oldVal, newVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCpuConsumption() throws IOException, InterruptedException {
|
|
||||||
Metrics metrics = Metrics.systemMetrics();
|
|
||||||
// make system call
|
|
||||||
long newSysVal = metrics.getCpuSystemUsage();
|
|
||||||
long newUserVal = metrics.getCpuUserUsage();
|
|
||||||
long newUsage = metrics.getCpuUsage();
|
|
||||||
long[] newPerCpu = metrics.getPerCpuUsage();
|
|
||||||
|
|
||||||
// system/user CPU usage counters may be slowly increasing.
|
|
||||||
// allow for equal values for a pass
|
|
||||||
if (newSysVal < startSysVal) {
|
|
||||||
fail(SubSystem.CPU, "getCpuSystemUsage", newSysVal, startSysVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
// system/user CPU usage counters may be slowly increasing.
|
|
||||||
// allow for equal values for a pass
|
|
||||||
if (newUserVal < startUserVal) {
|
|
||||||
fail(SubSystem.CPU, "getCpuUserUsage", newUserVal, startUserVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newUsage <= startUsage) {
|
|
||||||
fail(SubSystem.CPU, "getCpuUsage", newUsage, startUsage);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean success = false;
|
|
||||||
for (int i = 0; i < startPerCpu.length; i++) {
|
|
||||||
if (newPerCpu[i] > startPerCpu[i]) {
|
|
||||||
success = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!success) fail(SubSystem.CPU, "getPerCpuUsage", Arrays.toString(newPerCpu),
|
|
||||||
Arrays.toString(startPerCpu));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testMemoryUsage() throws Exception {
|
|
||||||
Metrics metrics = Metrics.systemMetrics();
|
|
||||||
long memoryMaxUsage = metrics.getMemoryMaxUsage();
|
|
||||||
long memoryUsage = metrics.getMemoryUsage();
|
|
||||||
long newMemoryMaxUsage = 0, newMemoryUsage = 0;
|
|
||||||
|
|
||||||
// allocate memory in a loop and check more than once for new values
|
|
||||||
// otherwise we might see seldom the effect of decreasing new memory values
|
|
||||||
// e.g. because the system could free up memory
|
|
||||||
byte[][] bytes = new byte[32][];
|
|
||||||
for (int i = 0; i < 32; i++) {
|
|
||||||
bytes[i] = new byte[8*1024*1024];
|
|
||||||
newMemoryUsage = metrics.getMemoryUsage();
|
|
||||||
if (newMemoryUsage > memoryUsage) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newMemoryMaxUsage = metrics.getMemoryMaxUsage();
|
|
||||||
|
|
||||||
if (newMemoryMaxUsage < memoryMaxUsage) {
|
|
||||||
fail(SubSystem.MEMORY, "getMemoryMaxUsage", memoryMaxUsage,
|
|
||||||
newMemoryMaxUsage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newMemoryUsage < memoryUsage) {
|
|
||||||
fail(SubSystem.MEMORY, "getMemoryUsage", memoryUsage, newMemoryUsage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
Metrics m = Metrics.systemMetrics();
|
||||||
// If cgroups is not configured, report success
|
// If cgroups is not configured, report success
|
||||||
Metrics metrics = Metrics.systemMetrics();
|
if (m == null) {
|
||||||
if (metrics == null) {
|
|
||||||
System.out.println("TEST PASSED!!!");
|
System.out.println("TEST PASSED!!!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MetricsTester metricsTester = new MetricsTester();
|
MetricsTester metricsTester = new MetricsTester();
|
||||||
metricsTester.setup();
|
metricsTester.testAll(m);
|
||||||
metricsTester.testCpuAccounting();
|
|
||||||
metricsTester.testCpuSchedulingMetrics();
|
|
||||||
metricsTester.testCpuSets();
|
|
||||||
metricsTester.testMemorySubsystem();
|
|
||||||
metricsTester.testBlkIO();
|
|
||||||
metricsTester.testCpuConsumption();
|
|
||||||
metricsTester.testMemoryUsage();
|
|
||||||
System.out.println("TEST PASSED!!!");
|
System.out.println("TEST PASSED!!!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,566 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* 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.test.lib.containers.cgroup;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.LongStream;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import jdk.internal.platform.Metrics;
|
||||||
|
import jdk.internal.platform.CgroupV1Metrics;
|
||||||
|
|
||||||
|
public class MetricsTesterCgroupV1 implements CgroupMetricsTester {
|
||||||
|
|
||||||
|
private static long unlimited_minimum = 0x7FFFFFFFFF000000L;
|
||||||
|
long startSysVal;
|
||||||
|
long startUserVal;
|
||||||
|
long startUsage;
|
||||||
|
long startPerCpu[];
|
||||||
|
|
||||||
|
enum Controller {
|
||||||
|
MEMORY("memory"),
|
||||||
|
CPUSET("cpuset"),
|
||||||
|
CPU("cpu"),
|
||||||
|
CPUACCT("cpuacct"),
|
||||||
|
BLKIO("blkio");
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
Controller(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Set<String> allowedSubSystems =
|
||||||
|
Stream.of(Controller.values()).map(Controller::value).collect(Collectors.toSet());
|
||||||
|
|
||||||
|
private static final Map<String, String[]> subSystemPaths = new HashMap<>();
|
||||||
|
|
||||||
|
private static void setPath(String[] line) {
|
||||||
|
String cgroupPath = line[2];
|
||||||
|
String[] subSystems = line[1].split(",");
|
||||||
|
|
||||||
|
for (String subSystem : subSystems) {
|
||||||
|
if (allowedSubSystems.contains(subSystem)) {
|
||||||
|
String[] paths = subSystemPaths.get(subSystem);
|
||||||
|
String finalPath = "";
|
||||||
|
String root = paths[0];
|
||||||
|
String mountPoint = paths[1];
|
||||||
|
if (root != null && cgroupPath != null) {
|
||||||
|
if (root.equals("/")) {
|
||||||
|
if (!cgroupPath.equals("/")) {
|
||||||
|
finalPath = mountPoint + cgroupPath;
|
||||||
|
} else {
|
||||||
|
finalPath = mountPoint;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (root.equals(cgroupPath)) {
|
||||||
|
finalPath = mountPoint;
|
||||||
|
} else {
|
||||||
|
if (cgroupPath.startsWith(root)) {
|
||||||
|
if (cgroupPath.length() > root.length()) {
|
||||||
|
String cgroupSubstr = cgroupPath.substring(root.length());
|
||||||
|
finalPath = mountPoint + cgroupSubstr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subSystemPaths.put(subSystem, new String[]{finalPath, mountPoint});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void createSubsystems(String[] line) {
|
||||||
|
if (line.length < 5) return;
|
||||||
|
Path p = Paths.get(line[4]);
|
||||||
|
String subsystemName = p.getFileName().toString();
|
||||||
|
if (subsystemName != null) {
|
||||||
|
for (String subSystem : subsystemName.split(",")) {
|
||||||
|
if (allowedSubSystems.contains(subSystem)) {
|
||||||
|
subSystemPaths.put(subSystem, new String[]{line[3], line[4]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setup() {
|
||||||
|
Metrics metrics = Metrics.systemMetrics();
|
||||||
|
// Initialize CPU usage metrics before we do any testing.
|
||||||
|
startSysVal = metrics.getCpuSystemUsage();
|
||||||
|
startUserVal = metrics.getCpuUserUsage();
|
||||||
|
startUsage = metrics.getCpuUsage();
|
||||||
|
startPerCpu = metrics.getPerCpuUsage();
|
||||||
|
if (startPerCpu == null) {
|
||||||
|
startPerCpu = new long[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Stream<String> lines = Files.lines(Paths.get("/proc/self/mountinfo"));
|
||||||
|
lines.filter(line -> line.contains(" - cgroup cgroup "))
|
||||||
|
.map(line -> line.split(" "))
|
||||||
|
.forEach(MetricsTesterCgroupV1::createSubsystems);
|
||||||
|
lines.close();
|
||||||
|
|
||||||
|
lines = Files.lines(Paths.get("/proc/self/cgroup"));
|
||||||
|
lines.map(line -> line.split(":"))
|
||||||
|
.filter(line -> (line.length >= 3))
|
||||||
|
.forEach(MetricsTesterCgroupV1::setPath);
|
||||||
|
lines.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getFileContents(Controller subSystem, String fileName) {
|
||||||
|
String fname = subSystemPaths.get(subSystem.value())[0] + File.separator + fileName;
|
||||||
|
try {
|
||||||
|
return new Scanner(new File(fname)).useDelimiter("\\Z").next();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
System.err.println("Unable to open : " + fname);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long getLongValueFromFile(Controller subSystem, String fileName) {
|
||||||
|
String data = getFileContents(subSystem, fileName);
|
||||||
|
return (data == null || data.isEmpty()) ? 0L : convertStringToLong(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long convertStringToLong(String strval) {
|
||||||
|
return CgroupMetricsTester.convertStringToLong(strval, Long.MAX_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long getLongValueFromFile(Controller subSystem, String metric, String subMetric) {
|
||||||
|
String stats = getFileContents(subSystem, metric);
|
||||||
|
String[] tokens = stats.split("[\\r\\n]+");
|
||||||
|
for (int i = 0; i < tokens.length; i++) {
|
||||||
|
if (tokens[i].startsWith(subMetric)) {
|
||||||
|
String strval = tokens[i].split("\\s+")[1];
|
||||||
|
return convertStringToLong(strval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double getDoubleValueFromFile(Controller subSystem, String fileName) {
|
||||||
|
String data = getFileContents(subSystem, fileName);
|
||||||
|
return data.isEmpty() ? 0.0 : Double.parseDouble(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fail(Controller system, String metric, long oldVal, long testVal) {
|
||||||
|
CgroupMetricsTester.fail(system.value, metric, oldVal, testVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fail(Controller system, String metric, String oldVal, String testVal) {
|
||||||
|
CgroupMetricsTester.fail(system.value, metric, oldVal, testVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fail(Controller system, String metric, double oldVal, double testVal) {
|
||||||
|
CgroupMetricsTester.fail(system.value, metric, oldVal, testVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fail(Controller system, String metric, boolean oldVal, boolean testVal) {
|
||||||
|
CgroupMetricsTester.fail(system.value, metric, oldVal, testVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void warn(Controller system, String metric, long oldVal, long testVal) {
|
||||||
|
CgroupMetricsTester.warn(system.value, metric, oldVal, testVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMemorySubsystem() {
|
||||||
|
CgroupV1Metrics metrics = (CgroupV1Metrics)Metrics.systemMetrics();
|
||||||
|
|
||||||
|
// User Memory
|
||||||
|
long oldVal = metrics.getMemoryFailCount();
|
||||||
|
long newVal = getLongValueFromFile(Controller.MEMORY, "memory.failcnt");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.failcnt", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getMemoryLimit();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.limit_in_bytes");
|
||||||
|
newVal = newVal > unlimited_minimum ? -1L : newVal;
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.limit_in_bytes", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getMemoryMaxUsage();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.max_usage_in_bytes");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.max_usage_in_bytes", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getMemoryUsage();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.usage_in_bytes");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.usage_in_bytes", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kernel memory
|
||||||
|
oldVal = metrics.getKernelMemoryFailCount();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.kmem.failcnt");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.kmem.failcnt", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getKernelMemoryLimit();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.kmem.limit_in_bytes");
|
||||||
|
newVal = newVal > unlimited_minimum ? -1L : newVal;
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.kmem.limit_in_bytes", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getKernelMemoryMaxUsage();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.kmem.max_usage_in_bytes");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.kmem.max_usage_in_bytes", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getKernelMemoryUsage();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.kmem.usage_in_bytes");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.kmem.usage_in_bytes", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TCP Memory
|
||||||
|
oldVal = metrics.getTcpMemoryFailCount();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.kmem.tcp.failcnt");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.kmem.tcp.failcnt", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getTcpMemoryLimit();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.kmem.tcp.limit_in_bytes");
|
||||||
|
newVal = newVal > unlimited_minimum ? -1L : newVal;
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.kmem.tcp.limit_in_bytes", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getTcpMemoryMaxUsage();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.kmem.tcp.max_usage_in_bytes");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.kmem.tcp.max_usage_in_bytes", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getTcpMemoryUsage();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.kmem.tcp.usage_in_bytes");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.kmem.tcp.usage_in_bytes", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memory and Swap
|
||||||
|
oldVal = metrics.getMemoryAndSwapFailCount();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.memsw.failcnt");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.memsw.failcnt", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getMemoryAndSwapLimit();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.memsw.limit_in_bytes");
|
||||||
|
newVal = newVal > unlimited_minimum ? -1L : newVal;
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.memsw.limit_in_bytes", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getMemoryAndSwapMaxUsage();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.memsw.max_usage_in_bytes");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.memsw.max_usage_in_bytes", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getMemoryAndSwapUsage();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.memsw.usage_in_bytes");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.memsw.usage_in_bytes", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getMemorySoftLimit();
|
||||||
|
newVal = getLongValueFromFile(Controller.MEMORY, "memory.soft_limit_in_bytes");
|
||||||
|
newVal = newVal > unlimited_minimum ? -1L : newVal;
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.MEMORY, "memory.soft_limit_in_bytes", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean oomKillEnabled = metrics.isMemoryOOMKillEnabled();
|
||||||
|
boolean newOomKillEnabled = getLongValueFromFile(Controller.MEMORY,
|
||||||
|
"memory.oom_control", "oom_kill_disable") == 0L ? true : false;
|
||||||
|
if (oomKillEnabled != newOomKillEnabled) {
|
||||||
|
throw new RuntimeException("Test failed for - " + Controller.MEMORY.value + ":"
|
||||||
|
+ "memory.oom_control:oom_kill_disable" + ", expected ["
|
||||||
|
+ oomKillEnabled + "], got [" + newOomKillEnabled + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCpuAccounting() {
|
||||||
|
CgroupV1Metrics metrics = (CgroupV1Metrics)Metrics.systemMetrics();
|
||||||
|
long oldVal = metrics.getCpuUsage();
|
||||||
|
long newVal = getLongValueFromFile(Controller.CPUACCT, "cpuacct.usage");
|
||||||
|
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
warn(Controller.CPUACCT, "cpuacct.usage", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
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[] 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getCpuUserUsage();
|
||||||
|
newVal = getLongValueFromFile(Controller.CPUACCT, "cpuacct.stat", "user");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
warn(Controller.CPUACCT, "cpuacct.usage - user", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getCpuSystemUsage();
|
||||||
|
newVal = getLongValueFromFile(Controller.CPUACCT, "cpuacct.stat", "system");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
warn(Controller.CPUACCT, "cpuacct.usage - system", oldVal, newVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCpuSchedulingMetrics() {
|
||||||
|
CgroupV1Metrics metrics = (CgroupV1Metrics)Metrics.systemMetrics();
|
||||||
|
long oldVal = metrics.getCpuPeriod();
|
||||||
|
long newVal = getLongValueFromFile(Controller.CPUACCT, "cpu.cfs_period_us");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.CPUACCT, "cpu.cfs_period_us", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getCpuQuota();
|
||||||
|
newVal = getLongValueFromFile(Controller.CPUACCT, "cpu.cfs_quota_us");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.CPUACCT, "cpu.cfs_quota_us", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getCpuShares();
|
||||||
|
newVal = getLongValueFromFile(Controller.CPUACCT, "cpu.shares");
|
||||||
|
if (newVal == 0 || newVal == 1024) newVal = -1;
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.CPUACCT, "cpu.shares", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getCpuNumPeriods();
|
||||||
|
newVal = getLongValueFromFile(Controller.CPUACCT, "cpu.stat", "nr_periods");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.CPUACCT, "cpu.stat - nr_periods", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getCpuNumThrottled();
|
||||||
|
newVal = getLongValueFromFile(Controller.CPUACCT, "cpu.stat", "nr_throttled");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.CPUACCT, "cpu.stat - nr_throttled", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getCpuThrottledTime();
|
||||||
|
newVal = getLongValueFromFile(Controller.CPUACCT, "cpu.stat", "throttled_time");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.CPUACCT, "cpu.stat - throttled_time", oldVal, newVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCpuSets() {
|
||||||
|
CgroupV1Metrics metrics = (CgroupV1Metrics)Metrics.systemMetrics();
|
||||||
|
Integer[] oldVal = Arrays.stream(metrics.getCpuSetCpus()).boxed().toArray(Integer[]::new);
|
||||||
|
Arrays.sort(oldVal);
|
||||||
|
|
||||||
|
String cpusstr = getFileContents(Controller.CPUSET, "cpuset.cpus");
|
||||||
|
// Parse range string in the format 1,2-6,7
|
||||||
|
Integer[] newVal = CgroupMetricsTester.convertCpuSetsToArray(cpusstr);
|
||||||
|
Arrays.sort(newVal);
|
||||||
|
if (Arrays.compare(oldVal, newVal) != 0) {
|
||||||
|
fail(Controller.CPUSET, "cpuset.cpus", Arrays.toString(oldVal),
|
||||||
|
Arrays.toString(newVal));
|
||||||
|
}
|
||||||
|
|
||||||
|
int [] cpuSets = metrics.getEffectiveCpuSetCpus();
|
||||||
|
|
||||||
|
// Skip this test if this metric is not supported on this platform
|
||||||
|
if (cpuSets.length != 0) {
|
||||||
|
oldVal = Arrays.stream(cpuSets).boxed().toArray(Integer[]::new);
|
||||||
|
Arrays.sort(oldVal);
|
||||||
|
cpusstr = getFileContents(Controller.CPUSET, "cpuset.effective_cpus");
|
||||||
|
newVal = CgroupMetricsTester.convertCpuSetsToArray(cpusstr);
|
||||||
|
Arrays.sort(newVal);
|
||||||
|
if (Arrays.compare(oldVal, newVal) != 0) {
|
||||||
|
fail(Controller.CPUSET, "cpuset.effective_cpus", Arrays.toString(oldVal),
|
||||||
|
Arrays.toString(newVal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = Arrays.stream(metrics.getCpuSetMems()).boxed().toArray(Integer[]::new);
|
||||||
|
Arrays.sort(oldVal);
|
||||||
|
cpusstr = getFileContents(Controller.CPUSET, "cpuset.mems");
|
||||||
|
newVal = CgroupMetricsTester.convertCpuSetsToArray(cpusstr);
|
||||||
|
Arrays.sort(newVal);
|
||||||
|
if (Arrays.compare(oldVal, newVal) != 0) {
|
||||||
|
fail(Controller.CPUSET, "cpuset.mems", Arrays.toString(oldVal),
|
||||||
|
Arrays.toString(newVal));
|
||||||
|
}
|
||||||
|
|
||||||
|
int [] cpuSetMems = metrics.getEffectiveCpuSetMems();
|
||||||
|
|
||||||
|
// Skip this test if this metric is not supported on this platform
|
||||||
|
if (cpuSetMems.length != 0) {
|
||||||
|
oldVal = Arrays.stream(cpuSetMems).boxed().toArray(Integer[]::new);
|
||||||
|
Arrays.sort(oldVal);
|
||||||
|
cpusstr = getFileContents(Controller.CPUSET, "cpuset.effective_mems");
|
||||||
|
newVal = CgroupMetricsTester.convertCpuSetsToArray(cpusstr);
|
||||||
|
Arrays.sort(newVal);
|
||||||
|
if (Arrays.compare(oldVal, newVal) != 0) {
|
||||||
|
fail(Controller.CPUSET, "cpuset.effective_mems", Arrays.toString(oldVal),
|
||||||
|
Arrays.toString(newVal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double oldValue = metrics.getCpuSetMemoryPressure();
|
||||||
|
double newValue = getDoubleValueFromFile(Controller.CPUSET, "cpuset.memory_pressure");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldValue, newValue)) {
|
||||||
|
fail(Controller.CPUSET, "cpuset.memory_pressure", oldValue, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean oldV = metrics.isCpuSetMemoryPressureEnabled();
|
||||||
|
boolean newV = getLongValueFromFile(Controller.CPUSET,
|
||||||
|
"cpuset.memory_pressure_enabled") == 1 ? true : false;
|
||||||
|
if (oldV != newV) {
|
||||||
|
fail(Controller.CPUSET, "cpuset.memory_pressure_enabled", oldV, newV);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testBlkIO() {
|
||||||
|
CgroupV1Metrics metrics = (CgroupV1Metrics)Metrics.systemMetrics();
|
||||||
|
long oldVal = metrics.getBlkIOServiceCount();
|
||||||
|
long newVal = getLongValueFromFile(Controller.BLKIO,
|
||||||
|
"blkio.throttle.io_service_bytes", "Total");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.BLKIO, "blkio.throttle.io_service_bytes - Total",
|
||||||
|
oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getBlkIOServiced();
|
||||||
|
newVal = getLongValueFromFile(Controller.BLKIO, "blkio.throttle.io_serviced", "Total");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail(Controller.BLKIO, "blkio.throttle.io_serviced - Total", oldVal, newVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCpuConsumption() throws IOException, InterruptedException {
|
||||||
|
CgroupV1Metrics metrics = (CgroupV1Metrics)Metrics.systemMetrics();
|
||||||
|
// make system call
|
||||||
|
long newSysVal = metrics.getCpuSystemUsage();
|
||||||
|
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
|
||||||
|
if (newSysVal < startSysVal) {
|
||||||
|
fail(Controller.CPU, "getCpuSystemUsage", newSysVal, startSysVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// system/user CPU usage counters may be slowly increasing.
|
||||||
|
// allow for equal values for a pass
|
||||||
|
if (newUserVal < startUserVal) {
|
||||||
|
fail(Controller.CPU, "getCpuUserUsage", newUserVal, startUserVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newUsage <= startUsage) {
|
||||||
|
fail(Controller.CPU, "getCpuUsage", newUsage, startUsage);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = false;
|
||||||
|
for (int i = 0; i < startPerCpu.length; i++) {
|
||||||
|
if (newPerCpu[i] > startPerCpu[i]) {
|
||||||
|
success = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!success) fail(Controller.CPU, "getPerCpuUsage", Arrays.toString(newPerCpu),
|
||||||
|
Arrays.toString(startPerCpu));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMemoryUsage() throws Exception {
|
||||||
|
CgroupV1Metrics metrics = (CgroupV1Metrics)Metrics.systemMetrics();
|
||||||
|
long memoryMaxUsage = metrics.getMemoryMaxUsage();
|
||||||
|
long memoryUsage = metrics.getMemoryUsage();
|
||||||
|
long newMemoryMaxUsage = 0, newMemoryUsage = 0;
|
||||||
|
|
||||||
|
// allocate memory in a loop and check more than once for new values
|
||||||
|
// otherwise we might see seldom the effect of decreasing new memory values
|
||||||
|
// e.g. because the system could free up memory
|
||||||
|
byte[][] bytes = new byte[32][];
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
bytes[i] = new byte[8*1024*1024];
|
||||||
|
newMemoryUsage = metrics.getMemoryUsage();
|
||||||
|
if (newMemoryUsage > memoryUsage) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newMemoryMaxUsage = metrics.getMemoryMaxUsage();
|
||||||
|
|
||||||
|
if (newMemoryMaxUsage < memoryMaxUsage) {
|
||||||
|
fail(Controller.MEMORY, "getMemoryMaxUsage", memoryMaxUsage,
|
||||||
|
newMemoryMaxUsage);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newMemoryUsage < memoryUsage) {
|
||||||
|
fail(Controller.MEMORY, "getMemoryUsage", memoryUsage, newMemoryUsage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void testMisc() {
|
||||||
|
testBlkIO();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,468 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdk.test.lib.containers.cgroup;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import jdk.internal.platform.CgroupSubsystem;
|
||||||
|
import jdk.internal.platform.Metrics;
|
||||||
|
|
||||||
|
public class MetricsTesterCgroupV2 implements CgroupMetricsTester {
|
||||||
|
|
||||||
|
private static final long UNLIMITED = -1;
|
||||||
|
private static final UnifiedController UNIFIED = new UnifiedController();
|
||||||
|
private static final String MAX = "max";
|
||||||
|
private static final int PER_CPU_SHARES = 1024;
|
||||||
|
|
||||||
|
private final long startSysVal;
|
||||||
|
private final long startUserVal;
|
||||||
|
private final long startUsage;
|
||||||
|
|
||||||
|
static class UnifiedController {
|
||||||
|
|
||||||
|
private static final String NAME = "unified";
|
||||||
|
private final String path;
|
||||||
|
|
||||||
|
UnifiedController() {
|
||||||
|
path = constructPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String constructPath() {
|
||||||
|
String mountPath;
|
||||||
|
String cgroupPath;
|
||||||
|
try {
|
||||||
|
List<String> fifthTokens = Files.lines(Paths.get("/proc/self/mountinfo"))
|
||||||
|
.filter( l -> l.contains("- cgroup2"))
|
||||||
|
.map(UnifiedController::splitAndMountPath)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (fifthTokens.size() != 1) {
|
||||||
|
throw new AssertionError("Expected only one cgroup2 line");
|
||||||
|
}
|
||||||
|
mountPath = fifthTokens.get(0);
|
||||||
|
|
||||||
|
List<String> cgroupPaths = Files.lines(Paths.get("/proc/self/cgroup"))
|
||||||
|
.filter( l -> l.startsWith("0:"))
|
||||||
|
.map(UnifiedController::splitAndCgroupPath)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (cgroupPaths.size() != 1) {
|
||||||
|
throw new AssertionError("Expected only one unified controller line");
|
||||||
|
}
|
||||||
|
cgroupPath = cgroupPaths.get(0);
|
||||||
|
return Paths.get(mountPath, cgroupPath).toString();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String splitAndMountPath(String input) {
|
||||||
|
String[] tokens = input.split("\\s+");
|
||||||
|
return tokens[4]; // fifth entry is the mount path
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String splitAndCgroupPath(String input) {
|
||||||
|
String[] tokens = input.split(":");
|
||||||
|
return tokens[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getLongLimitValueFromFile(String file) {
|
||||||
|
String strVal = getStringVal(file);
|
||||||
|
if (MAX.equals(strVal)) {
|
||||||
|
return UNLIMITED;
|
||||||
|
}
|
||||||
|
return convertStringToLong(strVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MetricsTesterCgroupV2() {
|
||||||
|
Metrics metrics = Metrics.systemMetrics();
|
||||||
|
// Initialize CPU usage metrics before we do any testing.
|
||||||
|
startSysVal = metrics.getCpuSystemUsage();
|
||||||
|
startUserVal = metrics.getCpuUserUsage();
|
||||||
|
startUsage = metrics.getCpuUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getLongValueFromFile(String file) {
|
||||||
|
return convertStringToLong(getStringVal(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getLongValueEntryFromFile(String file, String metric) {
|
||||||
|
Path filePath = Paths.get(UNIFIED.getPath(), file);
|
||||||
|
try {
|
||||||
|
String strVal = Files.lines(filePath).filter(l -> l.startsWith(metric)).collect(Collectors.joining());
|
||||||
|
String[] keyValues = strVal.split("\\s+");
|
||||||
|
String value = keyValues[1];
|
||||||
|
return convertStringToLong(value);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStringVal(String file) {
|
||||||
|
Path filePath = Paths.get(UNIFIED.getPath(), file);
|
||||||
|
try {
|
||||||
|
return Files.lines(filePath).collect(Collectors.joining());
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fail(String metric, long oldVal, long newVal) {
|
||||||
|
CgroupMetricsTester.fail(UnifiedController.NAME, metric, oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fail(String metric, String oldVal, String newVal) {
|
||||||
|
CgroupMetricsTester.fail(UnifiedController.NAME, metric, oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void warn(String metric, long oldVal, long newVal) {
|
||||||
|
CgroupMetricsTester.warn(UnifiedController.NAME, metric, oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getCpuShares(String file) {
|
||||||
|
long rawVal = getLongValueFromFile(file);
|
||||||
|
if (rawVal == 0 || rawVal == 100) {
|
||||||
|
return UNLIMITED;
|
||||||
|
}
|
||||||
|
int shares = (int)rawVal;
|
||||||
|
// CPU shares (OCI) value needs to get translated into
|
||||||
|
// a proper Cgroups v2 value. See:
|
||||||
|
// https://github.com/containers/crun/blob/master/crun.1.md#cpu-controller
|
||||||
|
//
|
||||||
|
// Use the inverse of (x == OCI value, y == cgroupsv2 value):
|
||||||
|
// ((262142 * y - 1)/9999) + 2 = x
|
||||||
|
//
|
||||||
|
int x = 262142 * shares - 1;
|
||||||
|
double frac = x/9999.0;
|
||||||
|
x = ((int)frac) + 2;
|
||||||
|
if ( x <= PER_CPU_SHARES ) {
|
||||||
|
return PER_CPU_SHARES; // mimic cgroups v1
|
||||||
|
}
|
||||||
|
int f = x/PER_CPU_SHARES;
|
||||||
|
int lower_multiple = f * PER_CPU_SHARES;
|
||||||
|
int upper_multiple = (f + 1) * PER_CPU_SHARES;
|
||||||
|
int distance_lower = Math.max(lower_multiple, x) - Math.min(lower_multiple, x);
|
||||||
|
int distance_upper = Math.max(upper_multiple, x) - Math.min(upper_multiple, x);
|
||||||
|
x = distance_lower <= distance_upper ? lower_multiple : upper_multiple;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getCpuMaxValueFromFile(String file) {
|
||||||
|
return getCpuValueFromFile(file, 0 /* $MAX index */);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getCpuPeriodValueFromFile(String file) {
|
||||||
|
return getCpuValueFromFile(file, 1 /* $PERIOD index */);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
return UNLIMITED;
|
||||||
|
}
|
||||||
|
return convertStringToLong(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long convertStringToLong(String val) {
|
||||||
|
return CgroupMetricsTester.convertStringToLong(val, UNLIMITED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void testMemorySubsystem() {
|
||||||
|
Metrics metrics = Metrics.systemMetrics();
|
||||||
|
|
||||||
|
// User Memory
|
||||||
|
long oldVal = metrics.getMemoryFailCount();
|
||||||
|
long newVal = getLongValueEntryFromFile("memory.events", "max");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail("memory.events[max]", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getMemoryLimit();
|
||||||
|
newVal = getLongLimitValueFromFile("memory.max");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail("memory.max", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getMemoryUsage();
|
||||||
|
newVal = getLongValueFromFile("memory.current");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail("memory.current", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getTcpMemoryUsage();
|
||||||
|
newVal = getLongValueEntryFromFile("memory.stat", "sock");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail("memory.stat[sock]", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getMemoryAndSwapLimit();
|
||||||
|
newVal = getLongLimitValueFromFile("memory.swap.max");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail("memory.swap.max", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getMemoryAndSwapUsage();
|
||||||
|
newVal = getLongValueFromFile("memory.swap.current");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail("memory.swap.current", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getMemorySoftLimit();
|
||||||
|
newVal = getLongLimitValueFromFile("memory.high");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail("memory.high", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void testCpuAccounting() {
|
||||||
|
Metrics metrics = Metrics.systemMetrics();
|
||||||
|
long oldVal = metrics.getCpuUsage();
|
||||||
|
long newVal = TimeUnit.MICROSECONDS.toNanos(getLongValueEntryFromFile("cpu.stat", "usage_usec"));
|
||||||
|
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
warn("cpu.stat[usage_usec]", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getCpuUserUsage();
|
||||||
|
newVal = TimeUnit.MICROSECONDS.toNanos(getLongValueEntryFromFile("cpu.stat", "user_usec"));
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
warn("cpu.stat[user_usec]", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getCpuSystemUsage();
|
||||||
|
newVal = TimeUnit.MICROSECONDS.toNanos(getLongValueEntryFromFile("cpu.stat", "system_usec"));
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
warn("cpu.stat[system_usec]", oldVal, newVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void testCpuSchedulingMetrics() {
|
||||||
|
Metrics metrics = Metrics.systemMetrics();
|
||||||
|
long oldVal = metrics.getCpuPeriod();
|
||||||
|
long newVal = getCpuPeriodValueFromFile("cpu.max");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail("cpu.max[$PERIOD]", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getCpuQuota();
|
||||||
|
newVal = getCpuMaxValueFromFile("cpu.max");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail("cpu.max[$MAX]", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getCpuShares();
|
||||||
|
newVal = getCpuShares("cpu.weight");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail("cpu.weight", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getCpuNumPeriods();
|
||||||
|
newVal = getLongValueEntryFromFile("cpu.stat", "nr_periods");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail("cpu.stat[nr_periods]", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getCpuNumThrottled();
|
||||||
|
newVal = getLongValueEntryFromFile("cpu.stat", "nr_throttled");
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail("cpu.stat[nr_throttled]", oldVal, newVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVal = metrics.getCpuThrottledTime();
|
||||||
|
newVal = TimeUnit.MICROSECONDS.toNanos(getLongValueEntryFromFile("cpu.stat", "throttled_usec"));
|
||||||
|
if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
|
||||||
|
fail("cpu.stat[throttled_usec]", oldVal, newVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void testCpuSets() {
|
||||||
|
Metrics metrics = Metrics.systemMetrics();
|
||||||
|
int[] cpus = mapNullToEmpty(metrics.getCpuSetCpus());
|
||||||
|
Integer[] oldVal = Arrays.stream(cpus).boxed().toArray(Integer[]::new);
|
||||||
|
Arrays.sort(oldVal);
|
||||||
|
|
||||||
|
String cpusstr = getStringVal("cpuset.cpus");
|
||||||
|
// Parse range string in the format 1,2-6,7
|
||||||
|
Integer[] newVal = CgroupMetricsTester.convertCpuSetsToArray(cpusstr);
|
||||||
|
Arrays.sort(newVal);
|
||||||
|
if (Arrays.compare(oldVal, newVal) != 0) {
|
||||||
|
fail("cpuset.cpus", Arrays.toString(oldVal),
|
||||||
|
Arrays.toString(newVal));
|
||||||
|
}
|
||||||
|
|
||||||
|
cpus = mapNullToEmpty(metrics.getEffectiveCpuSetCpus());
|
||||||
|
oldVal = Arrays.stream(cpus).boxed().toArray(Integer[]::new);
|
||||||
|
Arrays.sort(oldVal);
|
||||||
|
cpusstr = getStringVal("cpuset.cpus.effective");
|
||||||
|
newVal = CgroupMetricsTester.convertCpuSetsToArray(cpusstr);
|
||||||
|
Arrays.sort(newVal);
|
||||||
|
if (Arrays.compare(oldVal, newVal) != 0) {
|
||||||
|
fail("cpuset.cpus.effective", Arrays.toString(oldVal),
|
||||||
|
Arrays.toString(newVal));
|
||||||
|
}
|
||||||
|
|
||||||
|
cpus = mapNullToEmpty(metrics.getCpuSetMems());
|
||||||
|
oldVal = Arrays.stream(cpus).boxed().toArray(Integer[]::new);
|
||||||
|
Arrays.sort(oldVal);
|
||||||
|
cpusstr = getStringVal("cpuset.mems");
|
||||||
|
newVal = CgroupMetricsTester.convertCpuSetsToArray(cpusstr);
|
||||||
|
Arrays.sort(newVal);
|
||||||
|
if (Arrays.compare(oldVal, newVal) != 0) {
|
||||||
|
fail("cpuset.mems", Arrays.toString(oldVal),
|
||||||
|
Arrays.toString(newVal));
|
||||||
|
}
|
||||||
|
|
||||||
|
cpus = mapNullToEmpty(metrics.getEffectiveCpuSetMems());
|
||||||
|
oldVal = Arrays.stream(cpus).boxed().toArray(Integer[]::new);
|
||||||
|
Arrays.sort(oldVal);
|
||||||
|
cpusstr = getStringVal("cpuset.mems.effective");
|
||||||
|
newVal = CgroupMetricsTester.convertCpuSetsToArray(cpusstr);
|
||||||
|
Arrays.sort(newVal);
|
||||||
|
if (Arrays.compare(oldVal, newVal) != 0) {
|
||||||
|
fail("cpuset.mems.effective", Arrays.toString(oldVal),
|
||||||
|
Arrays.toString(newVal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
public void testCpuConsumption() {
|
||||||
|
Metrics metrics = Metrics.systemMetrics();
|
||||||
|
// make system call
|
||||||
|
long newSysVal = metrics.getCpuSystemUsage();
|
||||||
|
long newUserVal = metrics.getCpuUserUsage();
|
||||||
|
long newUsage = metrics.getCpuUsage();
|
||||||
|
|
||||||
|
// system/user CPU usage counters may be slowly increasing.
|
||||||
|
// allow for equal values for a pass
|
||||||
|
if (newSysVal < startSysVal) {
|
||||||
|
fail("getCpuSystemUsage", newSysVal, startSysVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// system/user CPU usage counters may be slowly increasing.
|
||||||
|
// allow for equal values for a pass
|
||||||
|
if (newUserVal < startUserVal) {
|
||||||
|
fail("getCpuUserUsage", newUserVal, startUserVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newUsage <= startUsage) {
|
||||||
|
fail("getCpuUsage", newUsage, startUsage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void testMemoryUsage() {
|
||||||
|
Metrics metrics = Metrics.systemMetrics();
|
||||||
|
long memoryUsage = metrics.getMemoryUsage();
|
||||||
|
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
|
||||||
|
// values. For example because the system could free up memory
|
||||||
|
byte[][] bytes = new byte[32][];
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
bytes[i] = new byte[8*1024*1024];
|
||||||
|
newMemoryUsage = metrics.getMemoryUsage();
|
||||||
|
if (newMemoryUsage > memoryUsage) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newMemoryUsage < memoryUsage) {
|
||||||
|
fail("getMemoryUsage", memoryUsage, newMemoryUsage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void testMisc() {
|
||||||
|
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 CgroupSubsystem.LONG_RETVAL_UNLIMITED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue