2626package jdk .internal .platform ;
2727
2828import java .io .IOException ;
29+ import java .io .UncheckedIOException ;
2930import java .lang .System .Logger ;
3031import java .lang .System .Logger .Level ;
3132import java .nio .file .Path ;
3738import java .util .Optional ;
3839import java .util .Objects ;
3940import java .util .function .Consumer ;
41+ import java .util .regex .Matcher ;
42+ import java .util .regex .Pattern ;
43+ import java .util .stream .Stream ;
4044
4145import jdk .internal .platform .cgroupv1 .CgroupV1Subsystem ;
4246import jdk .internal .platform .cgroupv2 .CgroupV2Subsystem ;
@@ -50,11 +54,36 @@ public class CgroupSubsystemFactory {
5054 private static final String MEMORY_CTRL = "memory" ;
5155 private static final String PIDS_CTRL = "pids" ;
5256
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+
5382 static CgroupMetrics create () {
5483 Optional <CgroupTypeResult > optResult = null ;
5584 try {
5685 optResult = determineType ("/proc/self/mountinfo" , "/proc/cgroups" , "/proc/self/cgroup" );
57- } catch (IOException e ) {
86+ } catch (IOException | UncheckedIOException e ) {
5887 return null ;
5988 }
6089 return create (optResult );
@@ -75,7 +104,8 @@ public static CgroupMetrics create(Optional<CgroupTypeResult> optResult) {
75104 // not ready to deal with that on a per-controller basis. Return no metrics
76105 // in that case
77106 if (result .isAnyCgroupV1Controllers () && result .isAnyCgroupV2Controllers ()) {
78- warn ("Mixed cgroupv1 and cgroupv2 not supported. Metrics disabled." );
107+ Logger logger = System .getLogger ("jdk.internal.platform" );
108+ logger .log (Level .DEBUG , "Mixed cgroupv1 and cgroupv2 not supported. Metrics disabled." );
79109 return null ;
80110 }
81111
@@ -91,11 +121,6 @@ public static CgroupMetrics create(Optional<CgroupTypeResult> optResult) {
91121 }
92122 }
93123
94- private static void warn (String msg ) {
95- Logger logger = System .getLogger ("jdk.internal.platform" );
96- logger .log (Level .DEBUG , msg );
97- }
98-
99124 /*
100125 * Determine the type of the cgroup system (v1 - legacy or hybrid - or, v2 - unified)
101126 * based on three files:
@@ -171,15 +196,16 @@ public static Optional<CgroupTypeResult> determineType(String mountInfo,
171196 // See:
172197 // setCgroupV1Path() for the action run for cgroups v1 systems
173198 // setCgroupV2Path() for the action run for cgroups v2 systems
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 ))) {
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+ }
179205 // The limit value of 3 is because /proc/self/cgroup contains three
180206 // colon-separated tokens per line. The last token, cgroup path, might
181207 // contain a ':'.
182- action . accept (line .split (":" , 3 ));
208+ selfCgroupLines . map (line -> line .split (":" , 3 )). forEach ( action );
183209 }
184210
185211 CgroupTypeResult result = new CgroupTypeResult (isCgroupsV2 ,
@@ -255,8 +281,8 @@ private static void setCgroupV1Path(Map<String, CgroupInfo> infos,
255281 /**
256282 * Amends cgroup infos with mount path and mount root. The passed in
257283 * 'mntInfoLine' represents a single line in, for example,
258- * /proc/self/mountinfo. Each line is parsed with {@link MountInfo#parse},
259- * so as to extract the relevant tokens from the line.
284+ * /proc/self/mountinfo. Each line is matched with MOUNTINFO_PATTERN
285+ * (see above), so as to extract the relevant tokens from the line.
260286 *
261287 * Host example cgroups v1:
262288 *
@@ -277,13 +303,13 @@ private static void setCgroupV1Path(Map<String, CgroupInfo> infos,
277303 private static boolean amendCgroupInfos (String mntInfoLine ,
278304 Map <String , CgroupInfo > infos ,
279305 boolean isCgroupsV2 ) {
280- MountInfo mountInfo = MountInfo . parse (mntInfoLine );
306+ Matcher lineMatcher = MOUNTINFO_PATTERN . matcher (mntInfoLine . trim () );
281307 boolean cgroupv1ControllerFound = false ;
282308 boolean cgroupv2ControllerFound = false ;
283- if (mountInfo != null ) {
284- String mountRoot = mountInfo . mountRoot ;
285- String mountPath = mountInfo . mountPath ;
286- String fsType = mountInfo . fsType ;
309+ if (lineMatcher . matches () ) {
310+ String mountRoot = lineMatcher . group ( 1 ) ;
311+ String mountPath = lineMatcher . group ( 2 ) ;
312+ String fsType = lineMatcher . group ( 3 ) ;
287313 if (fsType .equals ("cgroup" )) {
288314 Path p = Paths .get (mountPath );
289315 String [] controllerNames = p .getFileName ().toString ().split ("," );
@@ -319,59 +345,6 @@ private static boolean amendCgroupInfos(String mntInfoLine,
319345 return cgroupv1ControllerFound || cgroupv2ControllerFound ;
320346 }
321347
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-
375348 private static void setMountPoints (CgroupInfo info , String mountPath , String mountRoot ) {
376349 if (info .getMountPoint () != null ) {
377350 // On some systems duplicate controllers get mounted in addition to
0 commit comments