mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 07:14:30 +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.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -26,41 +26,37 @@
|
|||
package jdk.internal.platform.cgroupv1;
|
||||
|
||||
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.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 {
|
||||
private MemorySubSystem memory;
|
||||
private SubSystem cpu;
|
||||
private SubSystem cpuacct;
|
||||
private SubSystem cpuset;
|
||||
private SubSystem blkio;
|
||||
public class CgroupV1Subsystem implements CgroupSubsystem, CgroupV1Metrics {
|
||||
private CgroupV1MemorySubSystemController memory;
|
||||
private CgroupV1SubsystemController cpu;
|
||||
private CgroupV1SubsystemController cpuacct;
|
||||
private CgroupV1SubsystemController cpuset;
|
||||
private CgroupV1SubsystemController blkio;
|
||||
private boolean activeSubSystems;
|
||||
|
||||
// Values returned larger than this number are unlimited.
|
||||
static long unlimited_minimum = 0x7FFFFFFFFF000000L;
|
||||
|
||||
private static final Metrics INSTANCE = initContainerSubSystems();
|
||||
private static final CgroupV1Subsystem INSTANCE = initSubSystem();
|
||||
|
||||
private static final String PROVIDER_NAME = "cgroupv1";
|
||||
|
||||
private Metrics() {
|
||||
private CgroupV1Subsystem() {
|
||||
activeSubSystems = false;
|
||||
}
|
||||
|
||||
public static Metrics getInstance() {
|
||||
public static CgroupV1Subsystem getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static Metrics initContainerSubSystems() {
|
||||
Metrics metrics = new Metrics();
|
||||
private static CgroupV1Subsystem initSubSystem() {
|
||||
CgroupV1Subsystem subsystem = new CgroupV1Subsystem();
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
try (Stream<String> lines =
|
||||
readFilePrivileged(Paths.get("/proc/self/mountinfo"))) {
|
||||
CgroupUtil.readFilePrivileged(Paths.get("/proc/self/mountinfo"))) {
|
||||
|
||||
lines.filter(line -> line.contains(" - cgroup "))
|
||||
.map(line -> line.split(" "))
|
||||
.forEach(entry -> createSubSystem(metrics, entry));
|
||||
.forEach(entry -> createSubSystemController(subsystem, entry));
|
||||
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
|
@ -107,47 +103,28 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
|||
*
|
||||
*/
|
||||
try (Stream<String> lines =
|
||||
readFilePrivileged(Paths.get("/proc/self/cgroup"))) {
|
||||
CgroupUtil.readFilePrivileged(Paths.get("/proc/self/cgroup"))) {
|
||||
|
||||
lines.map(line -> line.split(":"))
|
||||
.filter(line -> (line.length >= 3))
|
||||
.forEach(line -> setSubSystemPath(metrics, line));
|
||||
.forEach(line -> setSubSystemControllerPath(subsystem, line));
|
||||
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return Metrics object if we found any subsystems.
|
||||
if (metrics.activeSubSystems()) {
|
||||
return metrics;
|
||||
if (subsystem.activeSubSystems()) {
|
||||
return subsystem;
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
private static void createSubSystem(Metrics metric, String[] mountentry) {
|
||||
private static void createSubSystemController(CgroupV1Subsystem subsystem, String[] mountentry) {
|
||||
if (mountentry.length < 5) return;
|
||||
|
||||
Path p = Paths.get(mountentry[4]);
|
||||
|
@ -156,19 +133,19 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
|||
for (String subsystemName: subsystemNames) {
|
||||
switch (subsystemName) {
|
||||
case "memory":
|
||||
metric.setMemorySubSystem(new MemorySubSystem(mountentry[3], mountentry[4]));
|
||||
subsystem.setMemorySubSystem(new CgroupV1MemorySubSystemController(mountentry[3], mountentry[4]));
|
||||
break;
|
||||
case "cpuset":
|
||||
metric.setCpuSetSubSystem(new SubSystem(mountentry[3], mountentry[4]));
|
||||
subsystem.setCpuSetController(new CgroupV1SubsystemController(mountentry[3], mountentry[4]));
|
||||
break;
|
||||
case "cpuacct":
|
||||
metric.setCpuAcctSubSystem(new SubSystem(mountentry[3], mountentry[4]));
|
||||
subsystem.setCpuAcctController(new CgroupV1SubsystemController(mountentry[3], mountentry[4]));
|
||||
break;
|
||||
case "cpu":
|
||||
metric.setCpuSubSystem(new SubSystem(mountentry[3], mountentry[4]));
|
||||
subsystem.setCpuController(new CgroupV1SubsystemController(mountentry[3], mountentry[4]));
|
||||
break;
|
||||
case "blkio":
|
||||
metric.setBlkIOSubSystem(new SubSystem(mountentry[3], mountentry[4]));
|
||||
subsystem.setBlkIOController(new CgroupV1SubsystemController(mountentry[3], mountentry[4]));
|
||||
break;
|
||||
default:
|
||||
// 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
|
||||
*/
|
||||
private static void setSubSystemPath(Metrics metric, String[] entry) {
|
||||
String controller;
|
||||
private static void setSubSystemControllerPath(CgroupV1Subsystem subsystem, String[] entry) {
|
||||
String controllerName;
|
||||
String base;
|
||||
SubSystem subsystem = null;
|
||||
SubSystem subsystem2 = null;
|
||||
CgroupV1SubsystemController controller = null;
|
||||
CgroupV1SubsystemController controller2 = null;
|
||||
|
||||
controller = entry[1];
|
||||
controllerName = entry[1];
|
||||
base = entry[2];
|
||||
if (controller != null && base != null) {
|
||||
switch (controller) {
|
||||
if (controllerName != null && base != null) {
|
||||
switch (controllerName) {
|
||||
case "memory":
|
||||
subsystem = metric.MemorySubSystem();
|
||||
controller = subsystem.memoryController();
|
||||
break;
|
||||
case "cpuset":
|
||||
subsystem = metric.CpuSetSubSystem();
|
||||
controller = subsystem.cpuSetController();
|
||||
break;
|
||||
case "cpu,cpuacct":
|
||||
case "cpuacct,cpu":
|
||||
subsystem = metric.CpuSubSystem();
|
||||
subsystem2 = metric.CpuAcctSubSystem();
|
||||
controller = subsystem.cpuController();
|
||||
controller2 = subsystem.cpuAcctController();
|
||||
break;
|
||||
case "cpuacct":
|
||||
subsystem = metric.CpuAcctSubSystem();
|
||||
controller = subsystem.cpuAcctController();
|
||||
break;
|
||||
case "cpu":
|
||||
subsystem = metric.CpuSubSystem();
|
||||
controller = subsystem.cpuController();
|
||||
break;
|
||||
case "blkio":
|
||||
subsystem = metric.BlkIOSubSystem();
|
||||
controller = subsystem.blkIOController();
|
||||
break;
|
||||
// Ignore subsystems that we don't support
|
||||
default:
|
||||
|
@ -216,23 +193,23 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
|||
}
|
||||
}
|
||||
|
||||
if (subsystem != null) {
|
||||
subsystem.setPath(base);
|
||||
if (subsystem instanceof MemorySubSystem) {
|
||||
MemorySubSystem memorySubSystem = (MemorySubSystem)subsystem;
|
||||
if (controller != null) {
|
||||
controller.setPath(base);
|
||||
if (controller instanceof CgroupV1MemorySubSystemController) {
|
||||
CgroupV1MemorySubSystemController memorySubSystem = (CgroupV1MemorySubSystemController)controller;
|
||||
boolean isHierarchial = getHierarchical(memorySubSystem);
|
||||
memorySubSystem.setHierarchical(isHierarchial);
|
||||
}
|
||||
metric.setActiveSubSystems();
|
||||
subsystem.setActiveSubSystems();
|
||||
}
|
||||
if (subsystem2 != null) {
|
||||
subsystem2.setPath(base);
|
||||
if (controller2 != null) {
|
||||
controller2.setPath(base);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static boolean getHierarchical(MemorySubSystem subsystem) {
|
||||
long hierarchical = SubSystem.getLongValue(subsystem, "memory.use_hierarchy");
|
||||
private static boolean getHierarchical(CgroupV1MemorySubSystemController controller) {
|
||||
long hierarchical = getLongValue(controller, "memory.use_hierarchy");
|
||||
return hierarchical > 0;
|
||||
}
|
||||
|
||||
|
@ -244,46 +221,54 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
|||
return activeSubSystems;
|
||||
}
|
||||
|
||||
private void setMemorySubSystem(MemorySubSystem memory) {
|
||||
private void setMemorySubSystem(CgroupV1MemorySubSystemController memory) {
|
||||
this.memory = memory;
|
||||
}
|
||||
|
||||
private void setCpuSubSystem(SubSystem cpu) {
|
||||
private void setCpuController(CgroupV1SubsystemController cpu) {
|
||||
this.cpu = cpu;
|
||||
}
|
||||
|
||||
private void setCpuAcctSubSystem(SubSystem cpuacct) {
|
||||
private void setCpuAcctController(CgroupV1SubsystemController cpuacct) {
|
||||
this.cpuacct = cpuacct;
|
||||
}
|
||||
|
||||
private void setCpuSetSubSystem(SubSystem cpuset) {
|
||||
private void setCpuSetController(CgroupV1SubsystemController cpuset) {
|
||||
this.cpuset = cpuset;
|
||||
}
|
||||
|
||||
private void setBlkIOSubSystem(SubSystem blkio) {
|
||||
private void setBlkIOController(CgroupV1SubsystemController blkio) {
|
||||
this.blkio = blkio;
|
||||
}
|
||||
|
||||
private SubSystem MemorySubSystem() {
|
||||
private CgroupV1SubsystemController memoryController() {
|
||||
return memory;
|
||||
}
|
||||
|
||||
private SubSystem CpuSubSystem() {
|
||||
private CgroupV1SubsystemController cpuController() {
|
||||
return cpu;
|
||||
}
|
||||
|
||||
private SubSystem CpuAcctSubSystem() {
|
||||
private CgroupV1SubsystemController cpuAcctController() {
|
||||
return cpuacct;
|
||||
}
|
||||
|
||||
private SubSystem CpuSetSubSystem() {
|
||||
private CgroupV1SubsystemController cpuSetController() {
|
||||
return cpuset;
|
||||
}
|
||||
|
||||
private SubSystem BlkIOSubSystem() {
|
||||
private CgroupV1SubsystemController blkIOController() {
|
||||
return blkio;
|
||||
}
|
||||
|
||||
private static long getLongValue(CgroupSubsystemController controller,
|
||||
String parm) {
|
||||
return CgroupSubsystemController.getLongValue(controller,
|
||||
parm,
|
||||
CgroupV1SubsystemController::convertStringToLong,
|
||||
CgroupSubsystem.LONG_RETVAL_UNLIMITED);
|
||||
}
|
||||
|
||||
public String getProvider() {
|
||||
return PROVIDER_NAME;
|
||||
}
|
||||
|
@ -294,13 +279,13 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
|||
|
||||
|
||||
public long getCpuUsage() {
|
||||
return SubSystem.getLongValue(cpuacct, "cpuacct.usage");
|
||||
return getLongValue(cpuacct, "cpuacct.usage");
|
||||
}
|
||||
|
||||
public long[] getPerCpuUsage() {
|
||||
String usagelist = SubSystem.getStringValue(cpuacct, "cpuacct.usage_percpu");
|
||||
String usagelist = CgroupSubsystemController.getStringValue(cpuacct, "cpuacct.usage_percpu");
|
||||
if (usagelist == null) {
|
||||
return new long[0];
|
||||
return null;
|
||||
}
|
||||
|
||||
String list[] = usagelist.split(" ");
|
||||
|
@ -312,11 +297,11 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
|||
}
|
||||
|
||||
public long getCpuUserUsage() {
|
||||
return SubSystem.getLongEntry(cpuacct, "cpuacct.stat", "user");
|
||||
return CgroupV1SubsystemController.getLongEntry(cpuacct, "cpuacct.stat", "user");
|
||||
}
|
||||
|
||||
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() {
|
||||
return SubSystem.getLongValue(cpu, "cpu.cfs_period_us");
|
||||
return getLongValue(cpu, "cpu.cfs_period_us");
|
||||
}
|
||||
|
||||
public long getCpuQuota() {
|
||||
return SubSystem.getLongValue(cpu, "cpu.cfs_quota_us");
|
||||
return getLongValue(cpu, "cpu.cfs_quota_us");
|
||||
}
|
||||
|
||||
public long getCpuShares() {
|
||||
long retval = SubSystem.getLongValue(cpu, "cpu.shares");
|
||||
long retval = getLongValue(cpu, "cpu.shares");
|
||||
if (retval == 0 || retval == 1024)
|
||||
return -1;
|
||||
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
|
||||
else
|
||||
return retval;
|
||||
}
|
||||
|
||||
public long getCpuNumPeriods() {
|
||||
return SubSystem.getLongEntry(cpu, "cpu.stat", "nr_periods");
|
||||
return CgroupV1SubsystemController.getLongEntry(cpu, "cpu.stat", "nr_periods");
|
||||
}
|
||||
|
||||
public long getCpuNumThrottled() {
|
||||
return SubSystem.getLongEntry(cpu, "cpu.stat", "nr_throttled");
|
||||
return CgroupV1SubsystemController.getLongEntry(cpu, "cpu.stat", "nr_throttled");
|
||||
}
|
||||
|
||||
public long getCpuThrottledTime() {
|
||||
return SubSystem.getLongEntry(cpu, "cpu.stat", "throttled_time");
|
||||
return CgroupV1SubsystemController.getLongEntry(cpu, "cpu.stat", "throttled_time");
|
||||
}
|
||||
|
||||
public long getEffectiveCpuCount() {
|
||||
|
@ -363,27 +348,27 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
|||
****************************************************************/
|
||||
|
||||
public int[] getCpuSetCpus() {
|
||||
return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.cpus"));
|
||||
return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.cpus"));
|
||||
}
|
||||
|
||||
public int[] getEffectiveCpuSetCpus() {
|
||||
return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.effective_cpus"));
|
||||
return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.effective_cpus"));
|
||||
}
|
||||
|
||||
public int[] getCpuSetMems() {
|
||||
return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.mems"));
|
||||
return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.mems"));
|
||||
}
|
||||
|
||||
public int[] getEffectiveCpuSetMems() {
|
||||
return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.effective_mems"));
|
||||
return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.effective_mems"));
|
||||
}
|
||||
|
||||
public double getCpuSetMemoryPressure() {
|
||||
return SubSystem.getDoubleValue(cpuset, "cpuset.memory_pressure");
|
||||
return CgroupV1SubsystemController.getDoubleValue(cpuset, "cpuset.memory_pressure");
|
||||
}
|
||||
|
||||
public boolean isCpuSetMemoryPressureEnabled() {
|
||||
long val = SubSystem.getLongValue(cpuset, "cpuset.memory_pressure_enabled");
|
||||
public Boolean isCpuSetMemoryPressureEnabled() {
|
||||
long val = getLongValue(cpuset, "cpuset.memory_pressure_enabled");
|
||||
return (val == 1);
|
||||
}
|
||||
|
||||
|
@ -394,112 +379,98 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
|||
|
||||
|
||||
public long getMemoryFailCount() {
|
||||
return SubSystem.getLongValue(memory, "memory.failcnt");
|
||||
return getLongValue(memory, "memory.failcnt");
|
||||
}
|
||||
|
||||
public long getMemoryLimit() {
|
||||
long retval = SubSystem.getLongValue(memory, "memory.limit_in_bytes");
|
||||
if (retval > unlimited_minimum) {
|
||||
long retval = getLongValue(memory, "memory.limit_in_bytes");
|
||||
if (retval > CgroupV1SubsystemController.UNLIMITED_MIN) {
|
||||
if (memory.isHierarchical()) {
|
||||
// memory.limit_in_bytes returned unlimited, attempt
|
||||
// hierarchical memory limit
|
||||
String match = "hierarchical_memory_limit";
|
||||
retval = SubSystem.getLongValueMatchingLine(memory,
|
||||
retval = CgroupV1SubsystemController.getLongValueMatchingLine(memory,
|
||||
"memory.stat",
|
||||
match,
|
||||
Metrics::convertHierachicalLimitLine);
|
||||
match);
|
||||
}
|
||||
}
|
||||
return retval > unlimited_minimum ? -1L : 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
|
||||
return CgroupV1SubsystemController.longValOrUnlimited(retval);
|
||||
}
|
||||
|
||||
public long getMemoryMaxUsage() {
|
||||
return SubSystem.getLongValue(memory, "memory.max_usage_in_bytes");
|
||||
return getLongValue(memory, "memory.max_usage_in_bytes");
|
||||
}
|
||||
|
||||
public long getMemoryUsage() {
|
||||
return SubSystem.getLongValue(memory, "memory.usage_in_bytes");
|
||||
return getLongValue(memory, "memory.usage_in_bytes");
|
||||
}
|
||||
|
||||
public long getKernelMemoryFailCount() {
|
||||
return SubSystem.getLongValue(memory, "memory.kmem.failcnt");
|
||||
return getLongValue(memory, "memory.kmem.failcnt");
|
||||
}
|
||||
|
||||
public long getKernelMemoryLimit() {
|
||||
long retval = SubSystem.getLongValue(memory, "memory.kmem.limit_in_bytes");
|
||||
return retval > unlimited_minimum ? -1L : retval;
|
||||
return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.kmem.limit_in_bytes"));
|
||||
}
|
||||
|
||||
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() {
|
||||
return SubSystem.getLongValue(memory, "memory.kmem.usage_in_bytes");
|
||||
return getLongValue(memory, "memory.kmem.usage_in_bytes");
|
||||
}
|
||||
|
||||
public long getTcpMemoryFailCount() {
|
||||
return SubSystem.getLongValue(memory, "memory.kmem.tcp.failcnt");
|
||||
return getLongValue(memory, "memory.kmem.tcp.failcnt");
|
||||
}
|
||||
|
||||
public long getTcpMemoryLimit() {
|
||||
long retval = SubSystem.getLongValue(memory, "memory.kmem.tcp.limit_in_bytes");
|
||||
return retval > unlimited_minimum ? -1L : retval;
|
||||
return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.kmem.tcp.limit_in_bytes"));
|
||||
}
|
||||
|
||||
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() {
|
||||
return SubSystem.getLongValue(memory, "memory.kmem.tcp.usage_in_bytes");
|
||||
return getLongValue(memory, "memory.kmem.tcp.usage_in_bytes");
|
||||
}
|
||||
|
||||
public long getMemoryAndSwapFailCount() {
|
||||
return SubSystem.getLongValue(memory, "memory.memsw.failcnt");
|
||||
return getLongValue(memory, "memory.memsw.failcnt");
|
||||
}
|
||||
|
||||
public long getMemoryAndSwapLimit() {
|
||||
long retval = SubSystem.getLongValue(memory, "memory.memsw.limit_in_bytes");
|
||||
if (retval > unlimited_minimum) {
|
||||
long retval = getLongValue(memory, "memory.memsw.limit_in_bytes");
|
||||
if (retval > CgroupV1SubsystemController.UNLIMITED_MIN) {
|
||||
if (memory.isHierarchical()) {
|
||||
// memory.memsw.limit_in_bytes returned unlimited, attempt
|
||||
// hierarchical memory limit
|
||||
String match = "hierarchical_memsw_limit";
|
||||
retval = SubSystem.getLongValueMatchingLine(memory,
|
||||
retval = CgroupV1SubsystemController.getLongValueMatchingLine(memory,
|
||||
"memory.stat",
|
||||
match,
|
||||
Metrics::convertHierachicalLimitLine);
|
||||
match);
|
||||
}
|
||||
}
|
||||
return retval > unlimited_minimum ? -1L : retval;
|
||||
return CgroupV1SubsystemController.longValOrUnlimited(retval);
|
||||
}
|
||||
|
||||
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() {
|
||||
return SubSystem.getLongValue(memory, "memory.memsw.usage_in_bytes");
|
||||
return getLongValue(memory, "memory.memsw.usage_in_bytes");
|
||||
}
|
||||
|
||||
public boolean isMemoryOOMKillEnabled() {
|
||||
long val = SubSystem.getLongEntry(memory, "memory.oom_control", "oom_kill_disable");
|
||||
public Boolean isMemoryOOMKillEnabled() {
|
||||
long val = CgroupV1SubsystemController.getLongEntry(memory, "memory.oom_control", "oom_kill_disable");
|
||||
return (val == 0);
|
||||
}
|
||||
|
||||
public long getMemorySoftLimit() {
|
||||
long retval = SubSystem.getLongValue(memory, "memory.soft_limit_in_bytes");
|
||||
return retval > unlimited_minimum ? -1L : retval;
|
||||
return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.soft_limit_in_bytes"));
|
||||
}
|
||||
|
||||
|
||||
|
@ -509,11 +480,11 @@ public class Metrics implements jdk.internal.platform.Metrics {
|
|||
|
||||
|
||||
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() {
|
||||
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 */);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue