Skip to content

Commit eaacc56

Browse files
committed
[GR-46420] Reduce JDK dependencies of cgroup support.
PullRequest: labsjdk-ce/8
2 parents b4e2248 + e5b92fd commit eaacc56

File tree

7 files changed

+150
-132
lines changed

7 files changed

+150
-132
lines changed

src/java.base/linux/classes/jdk/internal/platform/CgroupInfo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public void setCgroupPath(String cgroupPath) {
107107
*
108108
*/
109109
static CgroupInfo fromCgroupsLine(String line) {
110-
String[] tokens = line.split("\\s+");
110+
String[] tokens = line.split("\t");
111111
if (tokens.length != 4) {
112112
return null;
113113
}

src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemController.java

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,12 @@
2626
package jdk.internal.platform;
2727

2828
import java.io.IOException;
29-
import java.io.UncheckedIOException;
3029
import java.math.BigInteger;
3130
import java.nio.file.Path;
3231
import java.nio.file.Paths;
3332
import java.util.ArrayList;
3433
import java.util.List;
35-
import java.util.Optional;
3634
import java.util.function.Function;
37-
import java.util.stream.Stream;
3835

3936
/**
4037
* Cgroup version agnostic controller logic
@@ -161,16 +158,18 @@ public static double getDoubleValue(CgroupSubsystemController controller, String
161158
public static long getLongEntry(CgroupSubsystemController controller, String param, String entryname, long defaultRetval) {
162159
if (controller == null) return defaultRetval;
163160

164-
try (Stream<String> lines = CgroupUtil.readFilePrivileged(Paths.get(controller.path(), param))) {
165-
166-
Optional<String> result = lines.map(line -> line.split(" "))
167-
.filter(line -> (line.length == 2 &&
168-
line[0].equals(entryname)))
169-
.map(line -> line[1])
170-
.findFirst();
161+
try {
162+
long result = defaultRetval;
163+
for (String line : CgroupUtil.readAllLinesPrivileged(Paths.get(controller.path(), param))) {
164+
String[] tokens = line.split(" ");
165+
if (tokens.length == 2 && tokens[0].equals(entryname)) {
166+
result = Long.parseLong(tokens[1]);
167+
break;
168+
}
169+
}
171170

172-
return result.isPresent() ? Long.parseLong(result.get()) : defaultRetval;
173-
} catch (UncheckedIOException | IOException e) {
171+
return result;
172+
} catch (IOException e) {
174173
return defaultRetval;
175174
}
176175
}

src/java.base/linux/classes/jdk/internal/platform/CgroupSubsystemFactory.java

Lines changed: 73 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
package jdk.internal.platform;
2727

2828
import java.io.IOException;
29-
import java.io.UncheckedIOException;
3029
import java.lang.System.Logger;
3130
import java.lang.System.Logger.Level;
3231
import java.nio.file.Path;
@@ -38,9 +37,6 @@
3837
import java.util.Optional;
3938
import java.util.Objects;
4039
import java.util.function.Consumer;
41-
import java.util.regex.Matcher;
42-
import java.util.regex.Pattern;
43-
import java.util.stream.Stream;
4440

4541
import jdk.internal.platform.cgroupv1.CgroupV1Subsystem;
4642
import jdk.internal.platform.cgroupv2.CgroupV2Subsystem;
@@ -54,36 +50,11 @@ public class CgroupSubsystemFactory {
5450
private static final String MEMORY_CTRL = "memory";
5551
private static final String PIDS_CTRL = "pids";
5652

57-
/*
58-
* From https://www.kernel.org/doc/Documentation/filesystems/proc.txt
59-
*
60-
* 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
61-
* (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
62-
*
63-
* (1) mount ID: unique identifier of the mount (may be reused after umount)
64-
* (2) parent ID: ID of parent (or of self for the top of the mount tree)
65-
* (3) major:minor: value of st_dev for files on filesystem
66-
* (4) root: root of the mount within the filesystem
67-
* (5) mount point: mount point relative to the process's root
68-
* (6) mount options: per mount options
69-
* (7) optional fields: zero or more fields of the form "tag[:value]"
70-
* (8) separator: marks the end of the optional fields
71-
* (9) filesystem type: name of filesystem of the form "type[.subtype]"
72-
* (10) mount source: filesystem specific information or "none"
73-
* (11) super options: per super block options
74-
*/
75-
private static final Pattern MOUNTINFO_PATTERN = Pattern.compile(
76-
"^[^\\s]+\\s+[^\\s]+\\s+[^\\s]+\\s+" + // (1), (2), (3)
77-
"([^\\s]+)\\s+([^\\s]+)\\s+" + // (4), (5) - group 1, 2: root, mount point
78-
"[^-]+-\\s+" + // (6), (7), (8)
79-
"([^\\s]+)\\s+" + // (9) - group 3: filesystem type
80-
".*$"); // (10), (11)
81-
8253
static CgroupMetrics create() {
8354
Optional<CgroupTypeResult> optResult = null;
8455
try {
8556
optResult = determineType("/proc/self/mountinfo", "/proc/cgroups", "/proc/self/cgroup");
86-
} catch (IOException | UncheckedIOException e) {
57+
} catch (IOException e) {
8758
return null;
8859
}
8960
return create(optResult);
@@ -104,8 +75,7 @@ public static CgroupMetrics create(Optional<CgroupTypeResult> optResult) {
10475
// not ready to deal with that on a per-controller basis. Return no metrics
10576
// in that case
10677
if (result.isAnyCgroupV1Controllers() && result.isAnyCgroupV2Controllers()) {
107-
Logger logger = System.getLogger("jdk.internal.platform");
108-
logger.log(Level.DEBUG, "Mixed cgroupv1 and cgroupv2 not supported. Metrics disabled.");
78+
warn("Mixed cgroupv1 and cgroupv2 not supported. Metrics disabled.");
10979
return null;
11080
}
11181

@@ -121,6 +91,11 @@ public static CgroupMetrics create(Optional<CgroupTypeResult> optResult) {
12191
}
12292
}
12393

94+
private static void warn(String msg) {
95+
Logger logger = System.getLogger("jdk.internal.platform");
96+
logger.log(Level.DEBUG, msg);
97+
}
98+
12499
/*
125100
* Determine the type of the cgroup system (v1 - legacy or hybrid - or, v2 - unified)
126101
* based on three files:
@@ -196,16 +171,15 @@ public static Optional<CgroupTypeResult> determineType(String mountInfo,
196171
// See:
197172
// setCgroupV1Path() for the action run for cgroups v1 systems
198173
// setCgroupV2Path() for the action run for cgroups v2 systems
199-
try (Stream<String> selfCgroupLines =
200-
CgroupUtil.readFilePrivileged(Paths.get(selfCgroup))) {
201-
Consumer<String[]> action = (tokens -> setCgroupV1Path(infos, tokens));
202-
if (isCgroupsV2) {
203-
action = (tokens -> setCgroupV2Path(infos, tokens));
204-
}
174+
Consumer<String[]> action = (tokens -> setCgroupV1Path(infos, tokens));
175+
if (isCgroupsV2) {
176+
action = (tokens -> setCgroupV2Path(infos, tokens));
177+
}
178+
for (String line : CgroupUtil.readAllLinesPrivileged(Paths.get(selfCgroup))) {
205179
// The limit value of 3 is because /proc/self/cgroup contains three
206180
// colon-separated tokens per line. The last token, cgroup path, might
207181
// contain a ':'.
208-
selfCgroupLines.map(line -> line.split(":", 3)).forEach(action);
182+
action.accept(line.split(":", 3));
209183
}
210184

211185
CgroupTypeResult result = new CgroupTypeResult(isCgroupsV2,
@@ -281,8 +255,8 @@ private static void setCgroupV1Path(Map<String, CgroupInfo> infos,
281255
/**
282256
* Amends cgroup infos with mount path and mount root. The passed in
283257
* 'mntInfoLine' represents a single line in, for example,
284-
* /proc/self/mountinfo. Each line is matched with MOUNTINFO_PATTERN
285-
* (see above), so as to extract the relevant tokens from the line.
258+
* /proc/self/mountinfo. Each line is parsed with {@link MountInfo#parse},
259+
* so as to extract the relevant tokens from the line.
286260
*
287261
* Host example cgroups v1:
288262
*
@@ -303,13 +277,13 @@ private static void setCgroupV1Path(Map<String, CgroupInfo> infos,
303277
private static boolean amendCgroupInfos(String mntInfoLine,
304278
Map<String, CgroupInfo> infos,
305279
boolean isCgroupsV2) {
306-
Matcher lineMatcher = MOUNTINFO_PATTERN.matcher(mntInfoLine.trim());
280+
MountInfo mountInfo = MountInfo.parse(mntInfoLine);
307281
boolean cgroupv1ControllerFound = false;
308282
boolean cgroupv2ControllerFound = false;
309-
if (lineMatcher.matches()) {
310-
String mountRoot = lineMatcher.group(1);
311-
String mountPath = lineMatcher.group(2);
312-
String fsType = lineMatcher.group(3);
283+
if (mountInfo != null) {
284+
String mountRoot = mountInfo.mountRoot;
285+
String mountPath = mountInfo.mountPath;
286+
String fsType = mountInfo.fsType;
313287
if (fsType.equals("cgroup")) {
314288
Path p = Paths.get(mountPath);
315289
String[] controllerNames = p.getFileName().toString().split(",");
@@ -345,6 +319,59 @@ private static boolean amendCgroupInfos(String mntInfoLine,
345319
return cgroupv1ControllerFound || cgroupv2ControllerFound;
346320
}
347321

322+
private record MountInfo(String mountRoot, String mountPath, String fsType) {
323+
/*
324+
* From https://www.kernel.org/doc/Documentation/filesystems/proc.txt
325+
*
326+
* 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
327+
* (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
328+
*
329+
* (1) mount ID: unique identifier of the mount (may be reused after umount)
330+
* (2) parent ID: ID of parent (or of self for the top of the mount tree)
331+
* (3) major:minor: value of st_dev for files on filesystem
332+
* (4) root: root of the mount within the filesystem
333+
* (5) mount point: mount point relative to the process's root
334+
* (6) mount options: per mount options
335+
* (7) optional fields: zero or more fields of the form "tag[:value]"
336+
* (8) separator: marks the end of the optional fields
337+
* (9) filesystem type: name of filesystem of the form "type[.subtype]"
338+
* (10) mount source: filesystem specific information or "none"
339+
* (11) super options: per super block options
340+
*/
341+
static MountInfo parse(String line) {
342+
String mountRoot = null;
343+
String mountPath = null;
344+
345+
int separatorOrdinal = -1;
346+
// loop over space-separated tokens
347+
for (int tOrdinal = 1, tStart = 0, tEnd = line.indexOf(' '); tEnd != -1; tOrdinal++, tStart = tEnd + 1, tEnd = line.indexOf(' ', tStart)) {
348+
if (tStart == tEnd) {
349+
break; // unexpected empty token
350+
}
351+
switch (tOrdinal) {
352+
case 1, 2, 3, 6 -> {} // skip token
353+
case 4 -> mountRoot = line.substring(tStart, tEnd); // root token
354+
case 5 -> mountPath = line.substring(tStart, tEnd); // mount point token
355+
default -> {
356+
assert tOrdinal >= 7;
357+
if (separatorOrdinal == -1) {
358+
// check if we found a separator token
359+
if (tEnd - tStart == 1 && line.charAt(tStart) == '-') {
360+
separatorOrdinal = tOrdinal;
361+
}
362+
continue; // skip token
363+
}
364+
if (tOrdinal == separatorOrdinal + 1) { // filesystem type token
365+
String fsType = line.substring(tStart, tEnd);
366+
return new MountInfo(mountRoot, mountPath, fsType);
367+
}
368+
}
369+
}
370+
}
371+
return null; // parsing failed
372+
}
373+
}
374+
348375
private static void setMountPoints(CgroupInfo info, String mountPath, String mountRoot) {
349376
if (info.getMountPoint() != null) {
350377
// On some systems duplicate controllers get mounted in addition to

src/java.base/linux/classes/jdk/internal/platform/CgroupUtil.java

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,32 +26,19 @@
2626
package jdk.internal.platform;
2727

2828
import java.io.BufferedReader;
29+
import java.io.FileReader;
2930
import java.io.IOException;
30-
import java.io.UncheckedIOException;
31-
import java.nio.file.Files;
31+
import java.nio.charset.StandardCharsets;
3232
import java.nio.file.Path;
3333
import java.nio.file.Paths;
3434
import java.security.AccessController;
3535
import java.security.PrivilegedActionException;
3636
import java.security.PrivilegedExceptionAction;
37+
import java.util.ArrayList;
3738
import java.util.List;
38-
import java.util.stream.Stream;
3939

4040
public final class CgroupUtil {
4141

42-
@SuppressWarnings("removal")
43-
public static Stream<String> readFilePrivileged(Path path) throws IOException {
44-
try {
45-
PrivilegedExceptionAction<Stream<String>> pea = () -> Files.lines(path);
46-
return AccessController.doPrivileged(pea);
47-
} catch (PrivilegedActionException e) {
48-
unwrapIOExceptionAndRethrow(e);
49-
throw new InternalError(e.getCause());
50-
} catch (UncheckedIOException e) {
51-
throw e.getCause();
52-
}
53-
}
54-
5542
static void unwrapIOExceptionAndRethrow(PrivilegedActionException pae) throws IOException {
5643
Throwable x = pae.getCause();
5744
if (x instanceof IOException)
@@ -64,29 +51,34 @@ static void unwrapIOExceptionAndRethrow(PrivilegedActionException pae) throws IO
6451

6552
static String readStringValue(CgroupSubsystemController controller, String param) throws IOException {
6653
PrivilegedExceptionAction<BufferedReader> pea = () ->
67-
Files.newBufferedReader(Paths.get(controller.path(), param));
54+
new BufferedReader(new FileReader(Paths.get(controller.path(), param).toString(), StandardCharsets.UTF_8));
6855
try (@SuppressWarnings("removal") BufferedReader bufferedReader =
6956
AccessController.doPrivileged(pea)) {
7057
String line = bufferedReader.readLine();
7158
return line;
7259
} catch (PrivilegedActionException e) {
7360
unwrapIOExceptionAndRethrow(e);
7461
throw new InternalError(e.getCause());
75-
} catch (UncheckedIOException e) {
76-
throw e.getCause();
7762
}
7863
}
7964

8065
@SuppressWarnings("removal")
8166
public static List<String> readAllLinesPrivileged(Path path) throws IOException {
8267
try {
83-
PrivilegedExceptionAction<List<String>> pea = () -> Files.readAllLines(path);
68+
PrivilegedExceptionAction<List<String>> pea = () -> {
69+
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(path.toString(), StandardCharsets.UTF_8))) {
70+
String line;
71+
List<String> lines = new ArrayList<>();
72+
while ((line = bufferedReader.readLine()) != null) {
73+
lines.add(line);
74+
}
75+
return lines;
76+
}
77+
};
8478
return AccessController.doPrivileged(pea);
8579
} catch (PrivilegedActionException e) {
8680
unwrapIOExceptionAndRethrow(e);
8781
throw new InternalError(e.getCause());
88-
} catch (UncheckedIOException e) {
89-
throw e.getCause();
9082
}
9183
}
9284
}

src/java.base/linux/classes/jdk/internal/platform/cgroupv1/CgroupV1SubsystemController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public static long getLongValueMatchingLine(CgroupSubsystemController controller
107107
}
108108

109109
public static long convertHierachicalLimitLine(String line) {
110-
String[] tokens = line.split("\\s");
110+
String[] tokens = line.split(" ");
111111
if (tokens.length == 2) {
112112
String strVal = tokens[1];
113113
return CgroupV1SubsystemController.convertStringToLong(strVal);

src/java.base/linux/classes/jdk/internal/platform/cgroupv2/CgroupV2Subsystem.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@
2626
package jdk.internal.platform.cgroupv2;
2727

2828
import java.io.IOException;
29-
import java.io.UncheckedIOException;
3029
import java.nio.file.Paths;
3130
import java.util.concurrent.TimeUnit;
3231
import java.util.function.Function;
33-
import java.util.stream.Collectors;
3432

3533
import jdk.internal.platform.CgroupInfo;
3634
import jdk.internal.platform.CgroupSubsystem;
@@ -143,7 +141,7 @@ private long getFromCpuMax(int tokenIdx) {
143141
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
144142
}
145143
// $MAX $PERIOD
146-
String[] tokens = cpuMaxRaw.split("\\s+");
144+
String[] tokens = cpuMaxRaw.split(" ");
147145
if (tokens.length != 2) {
148146
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
149147
}
@@ -329,10 +327,12 @@ public long getBlkIOServiced() {
329327

330328
private long sumTokensIOStat(Function<String, Long> mapFunc) {
331329
try {
332-
return CgroupUtil.readFilePrivileged(Paths.get(unified.path(), "io.stat"))
333-
.map(mapFunc)
334-
.collect(Collectors.summingLong(e -> e));
335-
} catch (UncheckedIOException | IOException e) {
330+
long sum = 0L;
331+
for (String line : CgroupUtil.readAllLinesPrivileged(Paths.get(unified.path(), "io.stat"))) {
332+
sum += mapFunc.apply(line);
333+
}
334+
return sum;
335+
} catch (IOException e) {
336336
return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
337337
}
338338
}
@@ -359,7 +359,7 @@ private static Long ioStatLineToLong(String line, String[] matchNames) {
359359
if (line == null || EMPTY_STR.equals(line)) {
360360
return Long.valueOf(0);
361361
}
362-
String[] tokens = line.split("\\s+");
362+
String[] tokens = line.split(" ");
363363
long retval = 0;
364364
for (String t: tokens) {
365365
String[] valKeys = t.split("=");

0 commit comments

Comments
 (0)