1 /* 2 * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.internal.platform.cgroupv1; 27 28 import java.io.IOException; 29 import java.nio.file.Path; 30 import java.nio.file.Paths; 31 import java.util.stream.Stream; 32 33 import jdk.internal.platform.CgroupSubsystem; 34 import jdk.internal.platform.CgroupSubsystemController; 35 import jdk.internal.platform.CgroupUtil; 36 import jdk.internal.platform.MetricsCgroupV1; 37 38 public class CgroupV1Subsystem implements CgroupSubsystem, MetricsCgroupV1 { 39 private CgroupV1MemorySubSystemController memory; 40 private CgroupV1SubsystemController cpu; 41 private CgroupV1SubsystemController cpuacct; 42 private CgroupV1SubsystemController cpuset; 43 private CgroupV1SubsystemController blkio; 44 private boolean activeSubSystems; 45 46 private static final CgroupV1Subsystem INSTANCE = initSubSystem(); 47 48 private static final String PROVIDER_NAME = "cgroupv1"; 49 50 private CgroupV1Subsystem() { 51 activeSubSystems = false; 52 } 53 54 public static CgroupV1Subsystem getInstance() { 55 return INSTANCE; 56 } 57 58 private static CgroupV1Subsystem initSubSystem() { 59 CgroupV1Subsystem subsystem = new CgroupV1Subsystem(); 60 61 /** 62 * Find the cgroup mount points for subsystems 63 * by reading /proc/self/mountinfo 64 * 65 * Example for docker MemorySubSystem subsystem: 66 * 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/MemorySubSystem ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,MemorySubSystem 67 * 68 * Example for host: 69 * 34 28 0:29 / /sys/fs/cgroup/MemorySubSystem rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,MemorySubSystem 70 */ 71 try (Stream<String> lines = 72 CgroupUtil.readFilePrivileged(Paths.get("/proc/self/mountinfo"))) { 73 74 lines.filter(line -> line.contains(" - cgroup ")) 75 .map(line -> line.split(" ")) 76 .forEach(entry -> createSubSystemController(subsystem, entry)); 77 78 } catch (IOException e) { 79 return null; 80 } 81 82 /** 83 * Read /proc/self/cgroup and map host mount point to 84 * local one via /proc/self/mountinfo content above 85 * 86 * Docker example: 87 * 5:memory:/docker/6558aed8fc662b194323ceab5b964f69cf36b3e8af877a14b80256e93aecb044 88 * 89 * Host example: 90 * 5:memory:/user.slice 91 * 92 * Construct a path to the process specific memory and cpuset 93 * cgroup directory. 94 * 95 * For a container running under Docker from memory example above 96 * the paths would be: 97 * 98 * /sys/fs/cgroup/memory 99 * 100 * For a Host from memory example above the path would be: 101 * 102 * /sys/fs/cgroup/memory/user.slice 103 * 104 */ 105 try (Stream<String> lines = 106 CgroupUtil.readFilePrivileged(Paths.get("/proc/self/cgroup"))) { 107 108 lines.map(line -> line.split(":")) 109 .filter(line -> (line.length >= 3)) 110 .forEach(line -> setSubSystemControllerPath(subsystem, line)); 111 112 } catch (IOException e) { 113 return null; 114 } 115 116 // Return Metrics object if we found any subsystems. 117 if (subsystem.activeSubSystems()) { 118 return subsystem; 119 } 120 121 return null; 122 } 123 124 /** 125 * createSubSystem objects and initialize mount points 126 */ 127 private static void createSubSystemController(CgroupV1Subsystem subsystem, String[] mountentry) { 128 if (mountentry.length < 5) return; 129 130 Path p = Paths.get(mountentry[4]); 131 String[] subsystemNames = p.getFileName().toString().split(","); 132 133 for (String subsystemName: subsystemNames) { 134 switch (subsystemName) { 135 case "memory": 136 subsystem.setMemorySubSystem(new CgroupV1MemorySubSystemController(mountentry[3], mountentry[4])); 137 break; 138 case "cpuset": 139 subsystem.setCpuSetController(new CgroupV1SubsystemController(mountentry[3], mountentry[4])); 140 break; 141 case "cpuacct": 142 subsystem.setCpuAcctController(new CgroupV1SubsystemController(mountentry[3], mountentry[4])); 143 break; 144 case "cpu": 145 subsystem.setCpuController(new CgroupV1SubsystemController(mountentry[3], mountentry[4])); 146 break; 147 case "blkio": 148 subsystem.setBlkIOController(new CgroupV1SubsystemController(mountentry[3], mountentry[4])); 149 break; 150 default: 151 // Ignore subsystems that we don't support 152 break; 153 } 154 } 155 } 156 157 /** 158 * setSubSystemPath based on the contents of /proc/self/cgroup 159 */ 160 private static void setSubSystemControllerPath(CgroupV1Subsystem subsystem, String[] entry) { 161 String controllerName; 162 String base; 163 CgroupV1SubsystemController controller = null; 164 CgroupV1SubsystemController controller2 = null; 165 166 controllerName = entry[1]; 167 base = entry[2]; 168 if (controllerName != null && base != null) { 169 switch (controllerName) { 170 case "memory": 171 controller = subsystem.memoryController(); 172 break; 173 case "cpuset": 174 controller = subsystem.cpuSetController(); 175 break; 176 case "cpu,cpuacct": 177 case "cpuacct,cpu": 178 controller = subsystem.cpuController(); 179 controller2 = subsystem.cpuAcctController(); 180 break; 181 case "cpuacct": 182 controller = subsystem.cpuAcctController(); 183 break; 184 case "cpu": 185 controller = subsystem.cpuController(); 186 break; 187 case "blkio": 188 controller = subsystem.blkIOController(); 189 break; 190 // Ignore subsystems that we don't support 191 default: 192 break; 193 } 194 } 195 196 if (controller != null) { 197 controller.setPath(base); 198 if (controller instanceof CgroupV1MemorySubSystemController) { 199 CgroupV1MemorySubSystemController memorySubSystem = (CgroupV1MemorySubSystemController)controller; 200 boolean isHierarchial = getHierarchical(memorySubSystem); 201 memorySubSystem.setHierarchical(isHierarchial); 202 } 203 subsystem.setActiveSubSystems(); 204 } 205 if (controller2 != null) { 206 controller2.setPath(base); 207 } 208 } 209 210 211 private static boolean getHierarchical(CgroupV1MemorySubSystemController controller) { 212 long hierarchical = getLongValue(controller, "memory.use_hierarchy"); 213 return hierarchical > 0; 214 } 215 216 private void setActiveSubSystems() { 217 activeSubSystems = true; 218 } 219 220 private boolean activeSubSystems() { 221 return activeSubSystems; 222 } 223 224 private void setMemorySubSystem(CgroupV1MemorySubSystemController memory) { 225 this.memory = memory; 226 } 227 228 private void setCpuController(CgroupV1SubsystemController cpu) { 229 this.cpu = cpu; 230 } 231 232 private void setCpuAcctController(CgroupV1SubsystemController cpuacct) { 233 this.cpuacct = cpuacct; 234 } 235 236 private void setCpuSetController(CgroupV1SubsystemController cpuset) { 237 this.cpuset = cpuset; 238 } 239 240 private void setBlkIOController(CgroupV1SubsystemController blkio) { 241 this.blkio = blkio; 242 } 243 244 private CgroupV1SubsystemController memoryController() { 245 return memory; 246 } 247 248 private CgroupV1SubsystemController cpuController() { 249 return cpu; 250 } 251 252 private CgroupV1SubsystemController cpuAcctController() { 253 return cpuacct; 254 } 255 256 private CgroupV1SubsystemController cpuSetController() { 257 return cpuset; 258 } 259 260 private CgroupV1SubsystemController blkIOController() { 261 return blkio; 262 } 263 264 private static long getLongValue(CgroupSubsystemController controller, 265 String parm) { 266 return CgroupSubsystemController.getLongValue(controller, 267 parm, 268 CgroupV1SubsystemController::convertStringToLong); 269 } 270 271 public String getProvider() { 272 return PROVIDER_NAME; 273 } 274 275 /***************************************************************** 276 * CPU Accounting Subsystem 277 ****************************************************************/ 278 279 280 public long getCpuUsage() { 281 return getLongValue(cpuacct, "cpuacct.usage"); 282 } 283 284 public long[] getPerCpuUsage() { 285 String usagelist = CgroupSubsystemController.getStringValue(cpuacct, "cpuacct.usage_percpu"); 286 if (usagelist == null) { 287 return null; 288 } 289 290 String list[] = usagelist.split(" "); 291 long percpu[] = new long[list.length]; 292 for (int i = 0; i < list.length; i++) { 293 percpu[i] = Long.parseLong(list[i]); 294 } 295 return percpu; 296 } 297 298 public long getCpuUserUsage() { 299 return CgroupSubsystemController.getLongEntry(cpuacct, "cpuacct.stat", "user"); 300 } 301 302 public long getCpuSystemUsage() { 303 return CgroupSubsystemController.getLongEntry(cpuacct, "cpuacct.stat", "system"); 304 } 305 306 307 /***************************************************************** 308 * CPU Subsystem 309 ****************************************************************/ 310 311 312 public long getCpuPeriod() { 313 return getLongValue(cpu, "cpu.cfs_period_us"); 314 } 315 316 public long getCpuQuota() { 317 return getLongValue(cpu, "cpu.cfs_quota_us"); 318 } 319 320 public long getCpuShares() { 321 long retval = getLongValue(cpu, "cpu.shares"); 322 if (retval == 0 || retval == 1024) 323 return CgroupSubsystem.LONG_RETVAL_UNLIMITED; 324 else 325 return retval; 326 } 327 328 public long getCpuNumPeriods() { 329 return CgroupSubsystemController.getLongEntry(cpu, "cpu.stat", "nr_periods"); 330 } 331 332 public long getCpuNumThrottled() { 333 return CgroupSubsystemController.getLongEntry(cpu, "cpu.stat", "nr_throttled"); 334 } 335 336 public long getCpuThrottledTime() { 337 return CgroupSubsystemController.getLongEntry(cpu, "cpu.stat", "throttled_time"); 338 } 339 340 public long getEffectiveCpuCount() { 341 return Runtime.getRuntime().availableProcessors(); 342 } 343 344 345 /***************************************************************** 346 * CPUSet Subsystem 347 ****************************************************************/ 348 349 public int[] getCpuSetCpus() { 350 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.cpus")); 351 } 352 353 public int[] getEffectiveCpuSetCpus() { 354 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.effective_cpus")); 355 } 356 357 public int[] getCpuSetMems() { 358 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.mems")); 359 } 360 361 public int[] getEffectiveCpuSetMems() { 362 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.effective_mems")); 363 } 364 365 public double getCpuSetMemoryPressure() { 366 return CgroupSubsystemController.getDoubleValue(cpuset, "cpuset.memory_pressure"); 367 } 368 369 public Boolean isCpuSetMemoryPressureEnabled() { 370 long val = getLongValue(cpuset, "cpuset.memory_pressure_enabled"); 371 return (val == 1); 372 } 373 374 375 /***************************************************************** 376 * Memory Subsystem 377 ****************************************************************/ 378 379 380 public long getMemoryFailCount() { 381 return getLongValue(memory, "memory.failcnt"); 382 } 383 384 public long getMemoryLimit() { 385 long retval = getLongValue(memory, "memory.limit_in_bytes"); 386 if (retval > CgroupV1SubsystemController.UNLIMITED_MIN) { 387 if (memory.isHierarchical()) { 388 // memory.limit_in_bytes returned unlimited, attempt 389 // hierarchical memory limit 390 String match = "hierarchical_memory_limit"; 391 retval = CgroupSubsystemController.getLongValueMatchingLine(memory, 392 "memory.stat", 393 match, 394 CgroupV1Subsystem::convertHierachicalLimitLine); 395 } 396 } 397 return CgroupV1SubsystemController.longValOrUnlimited(retval); 398 } 399 400 public static long convertHierachicalLimitLine(String line) { 401 String[] tokens = line.split("\\s"); 402 if (tokens.length == 2) { 403 String strVal = tokens[1]; 404 return CgroupV1SubsystemController.convertStringToLong(strVal); 405 } 406 return CgroupV1SubsystemController.UNLIMITED_MIN + 1; // unlimited 407 } 408 409 public long getMemoryMaxUsage() { 410 return getLongValue(memory, "memory.max_usage_in_bytes"); 411 } 412 413 public long getMemoryUsage() { 414 return getLongValue(memory, "memory.usage_in_bytes"); 415 } 416 417 public long getKernelMemoryFailCount() { 418 return getLongValue(memory, "memory.kmem.failcnt"); 419 } 420 421 public long getKernelMemoryLimit() { 422 return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.kmem.limit_in_bytes")); 423 } 424 425 public long getKernelMemoryMaxUsage() { 426 return getLongValue(memory, "memory.kmem.max_usage_in_bytes"); 427 } 428 429 public long getKernelMemoryUsage() { 430 return getLongValue(memory, "memory.kmem.usage_in_bytes"); 431 } 432 433 public long getTcpMemoryFailCount() { 434 return getLongValue(memory, "memory.kmem.tcp.failcnt"); 435 } 436 437 public long getTcpMemoryLimit() { 438 return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.kmem.tcp.limit_in_bytes")); 439 } 440 441 public long getTcpMemoryMaxUsage() { 442 return getLongValue(memory, "memory.kmem.tcp.max_usage_in_bytes"); 443 } 444 445 public long getTcpMemoryUsage() { 446 return getLongValue(memory, "memory.kmem.tcp.usage_in_bytes"); 447 } 448 449 public long getMemoryAndSwapFailCount() { 450 return getLongValue(memory, "memory.memsw.failcnt"); 451 } 452 453 public long getMemoryAndSwapLimit() { 454 long retval = getLongValue(memory, "memory.memsw.limit_in_bytes"); 455 if (retval > CgroupV1SubsystemController.UNLIMITED_MIN) { 456 if (memory.isHierarchical()) { 457 // memory.memsw.limit_in_bytes returned unlimited, attempt 458 // hierarchical memory limit 459 String match = "hierarchical_memsw_limit"; 460 retval = CgroupSubsystemController.getLongValueMatchingLine(memory, 461 "memory.stat", 462 match, 463 CgroupV1Subsystem::convertHierachicalLimitLine); 464 } 465 } 466 return CgroupV1SubsystemController.longValOrUnlimited(retval); 467 } 468 469 public long getMemoryAndSwapMaxUsage() { 470 return getLongValue(memory, "memory.memsw.max_usage_in_bytes"); 471 } 472 473 public long getMemoryAndSwapUsage() { 474 return getLongValue(memory, "memory.memsw.usage_in_bytes"); 475 } 476 477 public Boolean isMemoryOOMKillEnabled() { 478 long val = CgroupSubsystemController.getLongEntry(memory, "memory.oom_control", "oom_kill_disable"); 479 return (val == 0); 480 } 481 482 public long getMemorySoftLimit() { 483 return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.soft_limit_in_bytes")); 484 } 485 486 487 /***************************************************************** 488 * BlKIO Subsystem 489 ****************************************************************/ 490 491 492 public long getBlkIOServiceCount() { 493 return CgroupSubsystemController.getLongEntry(blkio, "blkio.throttle.io_service_bytes", "Total"); 494 } 495 496 public long getBlkIOServiced() { 497 return CgroupSubsystemController.getLongEntry(blkio, "blkio.throttle.io_serviced", "Total"); 498 } 499 500 }