2626package jdk .internal .platform ;
2727
2828import java .io .IOException ;
29- import java .io .UncheckedIOException ;
3029import java .lang .System .Logger ;
3130import java .lang .System .Logger .Level ;
3231import java .nio .file .Path ;
3837import java .util .Optional ;
3938import java .util .Objects ;
4039import java .util .function .Consumer ;
41- import java .util .regex .Matcher ;
42- import java .util .regex .Pattern ;
43- import java .util .stream .Stream ;
4440
4541import jdk .internal .platform .cgroupv1 .CgroupV1Subsystem ;
4642import 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
0 commit comments