mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8254001: [Metrics] Enhance parsing of cgroup interface files for version detection
Reviewed-by: hseigel, andrew
This commit is contained in:
parent
65492129a9
commit
a50725db2a
5 changed files with 453 additions and 274 deletions
|
@ -26,17 +26,21 @@
|
|||
package jdk.internal.platform;
|
||||
|
||||
/**
|
||||
* Data structure to hold info from /proc/self/cgroup
|
||||
* Data structure to hold info from /proc/self/cgroup,
|
||||
* /proc/cgroups and /proc/self/mountinfo
|
||||
*
|
||||
* man 7 cgroups
|
||||
*
|
||||
* @see CgroupSubsystemFactory
|
||||
*/
|
||||
class CgroupInfo {
|
||||
public class CgroupInfo {
|
||||
|
||||
private final String name;
|
||||
private final int hierarchyId;
|
||||
private final boolean enabled;
|
||||
private String mountPoint;
|
||||
private String mountRoot;
|
||||
private String cgroupPath;
|
||||
|
||||
private CgroupInfo(String name, int hierarchyId, boolean enabled) {
|
||||
this.name = name;
|
||||
|
@ -44,18 +48,64 @@ class CgroupInfo {
|
|||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
int getHierarchyId() {
|
||||
public int getHierarchyId() {
|
||||
return hierarchyId;
|
||||
}
|
||||
|
||||
boolean isEnabled() {
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public String getMountPoint() {
|
||||
return mountPoint;
|
||||
}
|
||||
|
||||
public void setMountPoint(String mountPoint) {
|
||||
this.mountPoint = mountPoint;
|
||||
}
|
||||
|
||||
public String getMountRoot() {
|
||||
return mountRoot;
|
||||
}
|
||||
|
||||
public void setMountRoot(String mountRoot) {
|
||||
this.mountRoot = mountRoot;
|
||||
}
|
||||
|
||||
public String getCgroupPath() {
|
||||
return cgroupPath;
|
||||
}
|
||||
|
||||
public void setCgroupPath(String cgroupPath) {
|
||||
this.cgroupPath = cgroupPath;
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a CgroupInfo instance from a line in /proc/cgroups.
|
||||
* Comment token (hash) is handled by the caller.
|
||||
*
|
||||
* Example (annotated):
|
||||
*
|
||||
* #subsys_name hierarchy num_cgroups enabled
|
||||
* cpuset 10 1 1 (a)
|
||||
* cpu 7 8 1 (b)
|
||||
* [...]
|
||||
*
|
||||
* Line (a) would yield:
|
||||
* info = new CgroupInfo("cpuset", 10, true);
|
||||
* return info;
|
||||
* Line (b) results in:
|
||||
* info = new CgroupInfo("cpu", 7, true);
|
||||
* return info;
|
||||
*
|
||||
*
|
||||
* See CgroupSubsystemFactory.determineType()
|
||||
*
|
||||
*/
|
||||
static CgroupInfo fromCgroupsLine(String line) {
|
||||
String[] tokens = line.split("\\s+");
|
||||
if (tokens.length != 4) {
|
||||
|
|
|
@ -26,13 +26,17 @@
|
|||
package jdk.internal.platform;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.System.Logger;
|
||||
import java.lang.System.Logger.Level;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -68,17 +72,19 @@ public class CgroupSubsystemFactory {
|
|||
*/
|
||||
private static final Pattern MOUNTINFO_PATTERN = Pattern.compile(
|
||||
"^[^\\s]+\\s+[^\\s]+\\s+[^\\s]+\\s+" + // (1), (2), (3)
|
||||
"[^\\s]+\\s+([^\\s]+)\\s+" + // (4), (5) - group 1: mount point
|
||||
"([^\\s]+)\\s+([^\\s]+)\\s+" + // (4), (5) - group 1, 2: root, mount point
|
||||
"[^-]+-\\s+" + // (6), (7), (8)
|
||||
"([^\\s]+)\\s+" + // (9) - group 2: filesystem type
|
||||
"([^\\s]+)\\s+" + // (9) - group 3: filesystem type
|
||||
".*$"); // (10), (11)
|
||||
|
||||
static CgroupMetrics create() {
|
||||
Optional<CgroupTypeResult> optResult = null;
|
||||
try {
|
||||
optResult = determineType("/proc/self/mountinfo", "/proc/cgroups");
|
||||
optResult = determineType("/proc/self/mountinfo", "/proc/cgroups", "/proc/self/cgroup");
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
} catch (UncheckedIOException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (optResult.isEmpty()) {
|
||||
|
@ -100,17 +106,37 @@ public class CgroupSubsystemFactory {
|
|||
return null;
|
||||
}
|
||||
|
||||
Map<String, CgroupInfo> infos = result.getInfos();
|
||||
if (result.isCgroupV2()) {
|
||||
CgroupSubsystem subsystem = CgroupV2Subsystem.getInstance();
|
||||
// For unified it doesn't matter which controller we pick.
|
||||
CgroupInfo anyController = infos.get(MEMORY_CTRL);
|
||||
CgroupSubsystem subsystem = CgroupV2Subsystem.getInstance(anyController);
|
||||
return subsystem != null ? new CgroupMetrics(subsystem) : null;
|
||||
} else {
|
||||
CgroupV1Subsystem subsystem = CgroupV1Subsystem.getInstance();
|
||||
CgroupV1Subsystem subsystem = CgroupV1Subsystem.getInstance(infos);
|
||||
return subsystem != null ? new CgroupV1MetricsImpl(subsystem) : null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<CgroupTypeResult> determineType(String mountInfo, String cgroups) throws IOException {
|
||||
Map<String, CgroupInfo> infos = new HashMap<>();
|
||||
/*
|
||||
* Determine the type of the cgroup system (v1 - legacy or hybrid - or, v2 - unified)
|
||||
* based on three files:
|
||||
*
|
||||
* (1) mountInfo (i.e. /proc/self/mountinfo)
|
||||
* (2) cgroups (i.e. /proc/cgroups)
|
||||
* (3) selfCgroup (i.e. /proc/self/cgroup)
|
||||
*
|
||||
* File 'cgroups' is inspected for the hierarchy ID of the mounted cgroup pseudo
|
||||
* filesystem. The hierarchy ID, in turn, helps us distinguish cgroups v2 and
|
||||
* cgroup v1. For a system with zero hierarchy ID, but with >= 1 relevant cgroup
|
||||
* controllers mounted in 'mountInfo' we can infer it's cgroups v2. Anything else
|
||||
* will be cgroup v1 (hybrid or legacy). File 'selfCgroup' is being used for
|
||||
* figuring out the mount path of the controller in the cgroup hierarchy.
|
||||
*/
|
||||
public static Optional<CgroupTypeResult> determineType(String mountInfo,
|
||||
String cgroups,
|
||||
String selfCgroup) throws IOException {
|
||||
final Map<String, CgroupInfo> infos = new HashMap<>();
|
||||
List<String> lines = CgroupUtil.readAllLinesPrivileged(Paths.get(cgroups));
|
||||
for (String line : lines) {
|
||||
if (line.startsWith("#")) {
|
||||
|
@ -141,44 +167,187 @@ public class CgroupSubsystemFactory {
|
|||
anyControllersEnabled = anyControllersEnabled || info.isEnabled();
|
||||
}
|
||||
|
||||
// If there are no mounted, relevant cgroup controllers in mountinfo and only
|
||||
// 0 hierarchy IDs in /proc/cgroups have been seen, we are on a cgroups v1 system.
|
||||
// If there are no mounted, relevant cgroup controllers in 'mountinfo' and only
|
||||
// 0 hierarchy IDs in file 'cgroups' have been seen, we are on a cgroups v1 system.
|
||||
// However, continuing in that case does not make sense as we'd need
|
||||
// information from mountinfo for the mounted controller paths which we wouldn't
|
||||
// find anyway in that case.
|
||||
try (Stream<String> mntInfo = CgroupUtil.readFilePrivileged(Paths.get(mountInfo))) {
|
||||
boolean anyCgroupMounted = mntInfo.anyMatch(CgroupSubsystemFactory::isRelevantControllerMount);
|
||||
if (!anyCgroupMounted && isCgroupsV2) {
|
||||
return Optional.empty();
|
||||
}
|
||||
lines = CgroupUtil.readAllLinesPrivileged(Paths.get(mountInfo));
|
||||
boolean anyCgroupMounted = false;
|
||||
for (String line: lines) {
|
||||
boolean cgroupsControllerFound = amendCgroupInfos(line, infos, isCgroupsV2);
|
||||
anyCgroupMounted = anyCgroupMounted || cgroupsControllerFound;
|
||||
}
|
||||
CgroupTypeResult result = new CgroupTypeResult(isCgroupsV2, anyControllersEnabled, anyCgroupsV2Controller, anyCgroupsV1Controller);
|
||||
if (!anyCgroupMounted) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
// Map a cgroup version specific 'action' to a line in 'selfCgroup' (i.e.
|
||||
// /proc/self/cgroups) , split on the ':' token, so as to set the appropriate
|
||||
// path to the cgroup controller in cgroup data structures 'infos'.
|
||||
// See:
|
||||
// setCgroupV1Path() for the action run for cgroups v1 systems
|
||||
// setCgroupV2Path() for the action run for cgroups v2 systems
|
||||
try (Stream<String> selfCgroupLines =
|
||||
CgroupUtil.readFilePrivileged(Paths.get(selfCgroup))) {
|
||||
Consumer<String[]> action = (tokens -> setCgroupV1Path(infos, tokens));
|
||||
if (isCgroupsV2) {
|
||||
action = (tokens -> setCgroupV2Path(infos, tokens));
|
||||
}
|
||||
selfCgroupLines.map(line -> line.split(":"))
|
||||
.filter(tokens -> (tokens.length >= 3))
|
||||
.forEach(action);
|
||||
}
|
||||
|
||||
CgroupTypeResult result = new CgroupTypeResult(isCgroupsV2,
|
||||
anyControllersEnabled,
|
||||
anyCgroupsV2Controller,
|
||||
anyCgroupsV1Controller,
|
||||
Collections.unmodifiableMap(infos));
|
||||
return Optional.of(result);
|
||||
}
|
||||
|
||||
private static boolean isRelevantControllerMount(String line) {
|
||||
Matcher lineMatcher = MOUNTINFO_PATTERN.matcher(line.trim());
|
||||
if (lineMatcher.matches()) {
|
||||
String mountPoint = lineMatcher.group(1);
|
||||
String fsType = lineMatcher.group(2);
|
||||
if (fsType.equals("cgroup")) {
|
||||
String filename = Paths.get(mountPoint).getFileName().toString();
|
||||
for (String fn: filename.split(",")) {
|
||||
switch (fn) {
|
||||
case MEMORY_CTRL: // fall through
|
||||
case CPU_CTRL:
|
||||
case CPUSET_CTRL:
|
||||
case CPUACCT_CTRL:
|
||||
case BLKIO_CTRL:
|
||||
return true;
|
||||
default: break; // ignore not recognized controllers
|
||||
}
|
||||
}
|
||||
} else if (fsType.equals("cgroup2")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
/*
|
||||
* Sets the path to the cgroup controller for cgroups v2 based on a line
|
||||
* in /proc/self/cgroup file (represented as the 'tokens' array).
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* 0::/
|
||||
*
|
||||
* => tokens = [ "0", "", "/" ]
|
||||
*/
|
||||
private static void setCgroupV2Path(Map<String, CgroupInfo> infos,
|
||||
String[] tokens) {
|
||||
int hierarchyId = Integer.parseInt(tokens[0]);
|
||||
String cgroupPath = tokens[2];
|
||||
for (CgroupInfo info: infos.values()) {
|
||||
assert hierarchyId == info.getHierarchyId() && hierarchyId == 0;
|
||||
info.setCgroupPath(cgroupPath);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the path to the cgroup controller for cgroups v1 based on a line
|
||||
* in /proc/self/cgroup file (represented as the 'tokens' array).
|
||||
*
|
||||
* Note that multiple controllers might be joined at a single path.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* 7:cpu,cpuacct:/system.slice/docker-74ad896fb40bbefe0f181069e4417505fffa19052098f27edf7133f31423bc0b.scope
|
||||
*
|
||||
* => tokens = [ "7", "cpu,cpuacct", "/system.slice/docker-74ad896fb40bbefe0f181069e4417505fffa19052098f27edf7133f31423bc0b.scope" ]
|
||||
*/
|
||||
private static void setCgroupV1Path(Map<String, CgroupInfo> infos,
|
||||
String[] tokens) {
|
||||
String controllerName = tokens[1];
|
||||
String cgroupPath = tokens[2];
|
||||
if (controllerName != null && cgroupPath != null) {
|
||||
for (String cName: controllerName.split(",")) {
|
||||
switch (cName) {
|
||||
case MEMORY_CTRL: // fall through
|
||||
case CPUSET_CTRL:
|
||||
case CPUACCT_CTRL:
|
||||
case CPU_CTRL:
|
||||
case BLKIO_CTRL:
|
||||
CgroupInfo info = infos.get(cName);
|
||||
info.setCgroupPath(cgroupPath);
|
||||
break;
|
||||
// Ignore not recognized controllers
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Amends cgroup infos with mount path and mount root. The passed in
|
||||
* 'mntInfoLine' represents a single line in, for example,
|
||||
* /proc/self/mountinfo. Each line is matched with MOUNTINFO_PATTERN
|
||||
* (see above), so as to extract the relevant tokens from the line.
|
||||
*
|
||||
* Host example cgroups v1:
|
||||
*
|
||||
* 44 30 0:41 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,seclabel,devices
|
||||
*
|
||||
* Container example cgroups v1:
|
||||
*
|
||||
* 1901 1894 0:37 /system.slice/docker-2291eeb92093f9d761aaf971782b575e9be56bd5930d4b5759b51017df3c1387.scope /sys/fs/cgroup/cpu,cpuacct ro,nosuid,nodev,noexec,relatime master:12 - cgroup cgroup rw,seclabel,cpu,cpuacct
|
||||
*
|
||||
* Container example cgroups v2:
|
||||
*
|
||||
* 1043 1034 0:27 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - cgroup2 cgroup2 rw,seclabel,nsdelegate
|
||||
*
|
||||
*
|
||||
* @return {@code true} iff a relevant controller has been found at the
|
||||
* given line
|
||||
*/
|
||||
private static boolean amendCgroupInfos(String mntInfoLine,
|
||||
Map<String, CgroupInfo> infos,
|
||||
boolean isCgroupsV2) {
|
||||
Matcher lineMatcher = MOUNTINFO_PATTERN.matcher(mntInfoLine.trim());
|
||||
boolean cgroupv1ControllerFound = false;
|
||||
boolean cgroupv2ControllerFound = false;
|
||||
if (lineMatcher.matches()) {
|
||||
String mountRoot = lineMatcher.group(1);
|
||||
String mountPath = lineMatcher.group(2);
|
||||
String fsType = lineMatcher.group(3);
|
||||
if (fsType.equals("cgroup")) {
|
||||
Path p = Paths.get(mountPath);
|
||||
String[] controllerNames = p.getFileName().toString().split(",");
|
||||
for (String controllerName: controllerNames) {
|
||||
switch (controllerName) {
|
||||
case MEMORY_CTRL: // fall-through
|
||||
case CPU_CTRL:
|
||||
case CPUACCT_CTRL:
|
||||
case BLKIO_CTRL: {
|
||||
CgroupInfo info = infos.get(controllerName);
|
||||
assert info.getMountPoint() == null;
|
||||
assert info.getMountRoot() == null;
|
||||
info.setMountPoint(mountPath);
|
||||
info.setMountRoot(mountRoot);
|
||||
cgroupv1ControllerFound = true;
|
||||
break;
|
||||
}
|
||||
case CPUSET_CTRL: {
|
||||
CgroupInfo info = infos.get(controllerName);
|
||||
if (info.getMountPoint() != null) {
|
||||
// On some systems duplicate cpuset controllers get mounted in addition to
|
||||
// the main cgroup controllers most likely under /sys/fs/cgroup. In that
|
||||
// case pick the one under /sys/fs/cgroup and discard others.
|
||||
if (!info.getMountPoint().startsWith("/sys/fs/cgroup")) {
|
||||
info.setMountPoint(mountPath);
|
||||
info.setMountRoot(mountRoot);
|
||||
}
|
||||
} else {
|
||||
info.setMountPoint(mountPath);
|
||||
info.setMountRoot(mountRoot);
|
||||
}
|
||||
cgroupv1ControllerFound = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// Ignore controllers which we don't recognize
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (fsType.equals("cgroup2")) {
|
||||
if (isCgroupsV2) { // will be false for hybrid
|
||||
// All controllers have the same mount point and root mount
|
||||
// for unified hierarchy.
|
||||
for (CgroupInfo info: infos.values()) {
|
||||
assert info.getMountPoint() == null;
|
||||
assert info.getMountRoot() == null;
|
||||
info.setMountPoint(mountPath);
|
||||
info.setMountRoot(mountRoot);
|
||||
}
|
||||
}
|
||||
cgroupv2ControllerFound = true;
|
||||
}
|
||||
}
|
||||
return cgroupv1ControllerFound || cgroupv2ControllerFound;
|
||||
}
|
||||
|
||||
public static final class CgroupTypeResult {
|
||||
|
@ -186,15 +355,18 @@ public class CgroupSubsystemFactory {
|
|||
private final boolean anyControllersEnabled;
|
||||
private final boolean anyCgroupV2Controllers;
|
||||
private final boolean anyCgroupV1Controllers;
|
||||
private final Map<String, CgroupInfo> infos;
|
||||
|
||||
private CgroupTypeResult(boolean isCgroupV2,
|
||||
boolean anyControllersEnabled,
|
||||
boolean anyCgroupV2Controllers,
|
||||
boolean anyCgroupV1Controllers) {
|
||||
boolean anyCgroupV1Controllers,
|
||||
Map<String, CgroupInfo> infos) {
|
||||
this.isCgroupV2 = isCgroupV2;
|
||||
this.anyControllersEnabled = anyControllersEnabled;
|
||||
this.anyCgroupV1Controllers = anyCgroupV1Controllers;
|
||||
this.anyCgroupV2Controllers = anyCgroupV2Controllers;
|
||||
this.infos = infos;
|
||||
}
|
||||
|
||||
public boolean isCgroupV2() {
|
||||
|
@ -212,5 +384,9 @@ public class CgroupSubsystemFactory {
|
|||
public boolean isAnyCgroupV1Controllers() {
|
||||
return anyCgroupV1Controllers;
|
||||
}
|
||||
|
||||
public Map<String, CgroupInfo> getInfos() {
|
||||
return infos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,15 +25,11 @@
|
|||
|
||||
package jdk.internal.platform.cgroupv1;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.internal.platform.CgroupInfo;
|
||||
import jdk.internal.platform.CgroupSubsystem;
|
||||
import jdk.internal.platform.CgroupSubsystemController;
|
||||
import jdk.internal.platform.CgroupUtil;
|
||||
import jdk.internal.platform.CgroupV1Metrics;
|
||||
|
||||
public class CgroupV1Subsystem implements CgroupSubsystem, CgroupV1Metrics {
|
||||
|
@ -42,172 +38,107 @@ public class CgroupV1Subsystem implements CgroupSubsystem, CgroupV1Metrics {
|
|||
private CgroupV1SubsystemController cpuacct;
|
||||
private CgroupV1SubsystemController cpuset;
|
||||
private CgroupV1SubsystemController blkio;
|
||||
private boolean activeSubSystems;
|
||||
|
||||
private static final CgroupV1Subsystem INSTANCE = initSubSystem();
|
||||
private static volatile CgroupV1Subsystem INSTANCE;
|
||||
|
||||
private static final String PROVIDER_NAME = "cgroupv1";
|
||||
|
||||
private CgroupV1Subsystem() {
|
||||
activeSubSystems = false;
|
||||
}
|
||||
private CgroupV1Subsystem() {}
|
||||
|
||||
public static CgroupV1Subsystem getInstance() {
|
||||
/**
|
||||
* Get a singleton instance of CgroupV1Subsystem. Initially, it creates a new
|
||||
* object by retrieving the pre-parsed information from cgroup interface
|
||||
* files from the provided 'infos' map.
|
||||
*
|
||||
* See CgroupSubsystemFactory.determineType() where the actual parsing of
|
||||
* cgroup interface files happens.
|
||||
*
|
||||
* @return A singleton CgroupV1Subsystem instance, never null
|
||||
*/
|
||||
public static CgroupV1Subsystem getInstance(Map<String, CgroupInfo> infos) {
|
||||
if (INSTANCE == null) {
|
||||
CgroupV1Subsystem tmpSubsystem = initSubSystem(infos);
|
||||
synchronized (CgroupV1Subsystem.class) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = tmpSubsystem;
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static CgroupV1Subsystem initSubSystem() {
|
||||
private static CgroupV1Subsystem initSubSystem(Map<String, CgroupInfo> infos) {
|
||||
CgroupV1Subsystem subsystem = new CgroupV1Subsystem();
|
||||
|
||||
/**
|
||||
* Find the cgroup mount points for subsystems
|
||||
* by reading /proc/self/mountinfo
|
||||
*
|
||||
* Example for docker MemorySubSystem subsystem:
|
||||
* 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/MemorySubSystem ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,MemorySubSystem
|
||||
*
|
||||
* Example for host:
|
||||
* 34 28 0:29 / /sys/fs/cgroup/MemorySubSystem rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,MemorySubSystem
|
||||
boolean anyActiveControllers = false;
|
||||
/*
|
||||
* Find the cgroup mount points for subsystem controllers
|
||||
* by looking up relevant data in the infos map
|
||||
*/
|
||||
try (Stream<String> lines =
|
||||
CgroupUtil.readFilePrivileged(Paths.get("/proc/self/mountinfo"))) {
|
||||
|
||||
lines.filter(line -> line.contains(" - cgroup "))
|
||||
.map(line -> line.split(" "))
|
||||
.forEach(entry -> createSubSystemController(subsystem, entry));
|
||||
|
||||
} catch (UncheckedIOException e) {
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read /proc/self/cgroup and map host mount point to
|
||||
* local one via /proc/self/mountinfo content above
|
||||
*
|
||||
* Docker example:
|
||||
* 5:memory:/docker/6558aed8fc662b194323ceab5b964f69cf36b3e8af877a14b80256e93aecb044
|
||||
*
|
||||
* Host example:
|
||||
* 5:memory:/user.slice
|
||||
*
|
||||
* Construct a path to the process specific memory and cpuset
|
||||
* cgroup directory.
|
||||
*
|
||||
* For a container running under Docker from memory example above
|
||||
* the paths would be:
|
||||
*
|
||||
* /sys/fs/cgroup/memory
|
||||
*
|
||||
* For a Host from memory example above the path would be:
|
||||
*
|
||||
* /sys/fs/cgroup/memory/user.slice
|
||||
*
|
||||
*/
|
||||
try (Stream<String> lines =
|
||||
CgroupUtil.readFilePrivileged(Paths.get("/proc/self/cgroup"))) {
|
||||
|
||||
lines.map(line -> line.split(":"))
|
||||
.filter(line -> (line.length >= 3))
|
||||
.forEach(line -> setSubSystemControllerPath(subsystem, line));
|
||||
|
||||
} catch (UncheckedIOException e) {
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
for (CgroupInfo info: infos.values()) {
|
||||
switch (info.getName()) {
|
||||
case "memory": {
|
||||
if (info.getMountRoot() != null && info.getMountPoint() != null) {
|
||||
CgroupV1MemorySubSystemController controller = new CgroupV1MemorySubSystemController(info.getMountRoot(), info.getMountPoint());
|
||||
controller.setPath(info.getCgroupPath());
|
||||
boolean isHierarchial = getHierarchical(controller);
|
||||
controller.setHierarchical(isHierarchial);
|
||||
boolean isSwapEnabled = getSwapEnabled(controller);
|
||||
controller.setSwapEnabled(isSwapEnabled);
|
||||
subsystem.setMemorySubSystem(controller);
|
||||
anyActiveControllers = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "cpuset": {
|
||||
if (info.getMountRoot() != null && info.getMountPoint() != null) {
|
||||
CgroupV1SubsystemController controller = new CgroupV1SubsystemController(info.getMountRoot(), info.getMountPoint());
|
||||
controller.setPath(info.getCgroupPath());
|
||||
subsystem.setCpuSetController(controller);
|
||||
anyActiveControllers = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "cpuacct": {
|
||||
if (info.getMountRoot() != null && info.getMountPoint() != null) {
|
||||
CgroupV1SubsystemController controller = new CgroupV1SubsystemController(info.getMountRoot(), info.getMountPoint());
|
||||
controller.setPath(info.getCgroupPath());
|
||||
subsystem.setCpuAcctController(controller);
|
||||
anyActiveControllers = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "cpu": {
|
||||
if (info.getMountRoot() != null && info.getMountPoint() != null) {
|
||||
CgroupV1SubsystemController controller = new CgroupV1SubsystemController(info.getMountRoot(), info.getMountPoint());
|
||||
controller.setPath(info.getCgroupPath());
|
||||
subsystem.setCpuController(controller);
|
||||
anyActiveControllers = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "blkio": {
|
||||
if (info.getMountRoot() != null && info.getMountPoint() != null) {
|
||||
CgroupV1SubsystemController controller = new CgroupV1SubsystemController(info.getMountRoot(), info.getMountPoint());
|
||||
controller.setPath(info.getCgroupPath());
|
||||
subsystem.setBlkIOController(controller);
|
||||
anyActiveControllers = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new AssertionError("Unrecognized controller in infos: " + info.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// Return Metrics object if we found any subsystems.
|
||||
if (subsystem.activeSubSystems()) {
|
||||
if (anyActiveControllers) {
|
||||
return subsystem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* createSubSystem objects and initialize mount points
|
||||
*/
|
||||
private static void createSubSystemController(CgroupV1Subsystem subsystem, String[] mountentry) {
|
||||
if (mountentry.length < 5) return;
|
||||
|
||||
Path p = Paths.get(mountentry[4]);
|
||||
String[] subsystemNames = p.getFileName().toString().split(",");
|
||||
|
||||
for (String subsystemName: subsystemNames) {
|
||||
switch (subsystemName) {
|
||||
case "memory":
|
||||
subsystem.setMemorySubSystem(new CgroupV1MemorySubSystemController(mountentry[3], mountentry[4]));
|
||||
break;
|
||||
case "cpuset":
|
||||
subsystem.setCpuSetController(new CgroupV1SubsystemController(mountentry[3], mountentry[4]));
|
||||
break;
|
||||
case "cpuacct":
|
||||
subsystem.setCpuAcctController(new CgroupV1SubsystemController(mountentry[3], mountentry[4]));
|
||||
break;
|
||||
case "cpu":
|
||||
subsystem.setCpuController(new CgroupV1SubsystemController(mountentry[3], mountentry[4]));
|
||||
break;
|
||||
case "blkio":
|
||||
subsystem.setBlkIOController(new CgroupV1SubsystemController(mountentry[3], mountentry[4]));
|
||||
break;
|
||||
default:
|
||||
// Ignore subsystems that we don't support
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* setSubSystemPath based on the contents of /proc/self/cgroup
|
||||
*/
|
||||
private static void setSubSystemControllerPath(CgroupV1Subsystem subsystem, String[] entry) {
|
||||
String controllerName = entry[1];
|
||||
String base = entry[2];
|
||||
|
||||
if (controllerName != null && base != null) {
|
||||
for (String cName: controllerName.split(",")) {
|
||||
switch (cName) {
|
||||
case "memory":
|
||||
setPath(subsystem, subsystem.memoryController(), base);
|
||||
break;
|
||||
case "cpuset":
|
||||
setPath(subsystem, subsystem.cpuSetController(), base);
|
||||
break;
|
||||
case "cpu":
|
||||
setPath(subsystem, subsystem.cpuController(), base);
|
||||
break;
|
||||
case "cpuacct":
|
||||
setPath(subsystem, subsystem.cpuAcctController(), base);
|
||||
break;
|
||||
case "blkio":
|
||||
setPath(subsystem, subsystem.blkIOController(), base);
|
||||
break;
|
||||
// Ignore subsystems that we don't support
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void setPath(CgroupV1Subsystem subsystem, CgroupV1SubsystemController controller, String base) {
|
||||
if (controller != null) {
|
||||
controller.setPath(base);
|
||||
if (controller instanceof CgroupV1MemorySubSystemController) {
|
||||
CgroupV1MemorySubSystemController memorySubSystem = (CgroupV1MemorySubSystemController)controller;
|
||||
boolean isHierarchial = getHierarchical(memorySubSystem);
|
||||
memorySubSystem.setHierarchical(isHierarchial);
|
||||
boolean isSwapEnabled = getSwapEnabled(memorySubSystem);
|
||||
memorySubSystem.setSwapEnabled(isSwapEnabled);
|
||||
}
|
||||
subsystem.setActiveSubSystems();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static boolean getSwapEnabled(CgroupV1MemorySubSystemController controller) {
|
||||
long retval = getLongValue(controller, "memory.memsw.limit_in_bytes");
|
||||
return retval > 0;
|
||||
|
@ -219,14 +150,6 @@ public class CgroupV1Subsystem implements CgroupSubsystem, CgroupV1Metrics {
|
|||
return hierarchical > 0;
|
||||
}
|
||||
|
||||
private void setActiveSubSystems() {
|
||||
activeSubSystems = true;
|
||||
}
|
||||
|
||||
private boolean activeSubSystems() {
|
||||
return activeSubSystems;
|
||||
}
|
||||
|
||||
private void setMemorySubSystem(CgroupV1MemorySubSystemController memory) {
|
||||
this.memory = memory;
|
||||
}
|
||||
|
@ -247,26 +170,6 @@ public class CgroupV1Subsystem implements CgroupSubsystem, CgroupV1Metrics {
|
|||
this.blkio = blkio;
|
||||
}
|
||||
|
||||
private CgroupV1SubsystemController memoryController() {
|
||||
return memory;
|
||||
}
|
||||
|
||||
private CgroupV1SubsystemController cpuController() {
|
||||
return cpu;
|
||||
}
|
||||
|
||||
private CgroupV1SubsystemController cpuAcctController() {
|
||||
return cpuacct;
|
||||
}
|
||||
|
||||
private CgroupV1SubsystemController cpuSetController() {
|
||||
return cpuset;
|
||||
}
|
||||
|
||||
private CgroupV1SubsystemController blkIOController() {
|
||||
return blkio;
|
||||
}
|
||||
|
||||
private static long getLongValue(CgroupSubsystemController controller,
|
||||
String parm) {
|
||||
return CgroupSubsystemController.getLongValue(controller,
|
||||
|
|
|
@ -28,19 +28,18 @@ package jdk.internal.platform.cgroupv2;
|
|||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
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.CgroupInfo;
|
||||
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 volatile CgroupV2Subsystem INSTANCE;
|
||||
private static final long[] LONG_ARRAY_NOT_SUPPORTED = null;
|
||||
private static final int[] INT_ARRAY_UNAVAILABLE = null;
|
||||
private final CgroupSubsystemController unified;
|
||||
|
@ -65,48 +64,29 @@ public class CgroupV2Subsystem implements CgroupSubsystem {
|
|||
return getLongVal(file, 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 (UncheckedIOException e) {
|
||||
return null;
|
||||
} 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.
|
||||
/**
|
||||
* Get the singleton instance of a cgroups v2 subsystem. On initialization,
|
||||
* a new object from the given cgroup information 'anyController' is being
|
||||
* created. Note that the cgroup information has been parsed from cgroup
|
||||
* interface files ahead of time.
|
||||
*
|
||||
* See CgroupSubsystemFactory.determineType() for the cgroup interface
|
||||
* files parsing logic.
|
||||
*
|
||||
* @return A singleton CgroupSubsystem instance, never null.
|
||||
*/
|
||||
public static CgroupSubsystem getInstance(CgroupInfo anyController) {
|
||||
if (INSTANCE == null) {
|
||||
CgroupSubsystemController unified = new CgroupV2SubsystemController(
|
||||
anyController.getMountPoint(),
|
||||
anyController.getCgroupPath());
|
||||
CgroupV2Subsystem tmpCgroupSystem = new CgroupV2Subsystem(unified);
|
||||
synchronized (CgroupV2Subsystem.class) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = tmpCgroupSystem;
|
||||
}
|
||||
if (!"0".equals(tokens[0])) {
|
||||
// hierarchy must be zero for cgroups v2
|
||||
return null;
|
||||
}
|
||||
cgroupPath = tokens[2];
|
||||
break;
|
||||
}
|
||||
} catch (UncheckedIOException e) {
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
CgroupSubsystemController unified = new CgroupV2SubsystemController(
|
||||
mountPath,
|
||||
cgroupPath);
|
||||
return new CgroupV2Subsystem(unified);
|
||||
}
|
||||
|
||||
public static CgroupSubsystem getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue