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 CgroupSubsystem.LONG_RETVAL_UNLIMITED); 270 } 271 272 public String getProvider() { 273 return PROVIDER_NAME; 274 } 275 276 /***************************************************************** 277 * CPU Accounting Subsystem 278 ****************************************************************/ 279 280 281 public long getCpuUsage() { 282 return getLongValue(cpuacct, "cpuacct.usage"); 283 } 284 285 public long[] getPerCpuUsage() { 286 String usagelist = CgroupSubsystemController.getStringValue(cpuacct, "cpuacct.usage_percpu"); 287 if (usagelist == null) { 288 return null; 289 } 290 291 String list[] = usagelist.split(" "); 292 long percpu[] = new long[list.length]; 293 for (int i = 0; i < list.length; i++) { 294 percpu[i] = Long.parseLong(list[i]); 295 } 296 return percpu; 297 } 298 299 public long getCpuUserUsage() { 300 return CgroupV1SubsystemController.getLongEntry(cpuacct, "cpuacct.stat", "user"); 301 } 302 303 public long getCpuSystemUsage() { 304 return CgroupV1SubsystemController.getLongEntry(cpuacct, "cpuacct.stat", "system"); 305 } 306 307 308 /***************************************************************** 309 * CPU Subsystem 310 ****************************************************************/ 311 312 313 public long getCpuPeriod() { 314 return getLongValue(cpu, "cpu.cfs_period_us"); 315 } 316 317 public long getCpuQuota() { 318 return getLongValue(cpu, "cpu.cfs_quota_us"); 319 } 320 321 public long getCpuShares() { 322 long retval = getLongValue(cpu, "cpu.shares"); 323 if (retval == 0 || retval == 1024) 324 return CgroupSubsystem.LONG_RETVAL_UNLIMITED; 325 else 326 return retval; 327 } 328 329 public long getCpuNumPeriods() { 330 return CgroupV1SubsystemController.getLongEntry(cpu, "cpu.stat", "nr_periods"); 331 } 332 333 public long getCpuNumThrottled() { 334 return CgroupV1SubsystemController.getLongEntry(cpu, "cpu.stat", "nr_throttled"); 335 } 336 337 public long getCpuThrottledTime() { 338 return CgroupV1SubsystemController.getLongEntry(cpu, "cpu.stat", "throttled_time"); 339 } 340 341 public long getEffectiveCpuCount() { 342 return Runtime.getRuntime().availableProcessors(); 343 } 344 345 346 /***************************************************************** 347 * CPUSet Subsystem 348 ****************************************************************/ 349 350 public int[] getCpuSetCpus() { 351 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.cpus")); 352 } 353 354 public int[] getEffectiveCpuSetCpus() { 355 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.effective_cpus")); 356 } 357 358 public int[] getCpuSetMems() { 359 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.mems")); 360 } 361 362 public int[] getEffectiveCpuSetMems() { 363 return CgroupSubsystemController.stringRangeToIntArray(CgroupSubsystemController.getStringValue(cpuset, "cpuset.effective_mems")); 364 } 365 366 public double getCpuSetMemoryPressure() { 367 return CgroupV1SubsystemController.getDoubleValue(cpuset, "cpuset.memory_pressure"); 368 } 369 370 public Boolean isCpuSetMemoryPressureEnabled() { 371 long val = getLongValue(cpuset, "cpuset.memory_pressure_enabled"); 372 return (val == 1); 373 } 374 375 376 /***************************************************************** 377 * Memory Subsystem 378 ****************************************************************/ 379 380 381 public long getMemoryFailCount() { 382 return getLongValue(memory, "memory.failcnt"); 383 } 384 385 public long getMemoryLimit() { 386 long retval = getLongValue(memory, "memory.limit_in_bytes"); 387 if (retval > CgroupV1SubsystemController.UNLIMITED_MIN) { 388 if (memory.isHierarchical()) { 389 // memory.limit_in_bytes returned unlimited, attempt 390 // hierarchical memory limit 391 String match = "hierarchical_memory_limit"; 392 retval = CgroupV1SubsystemController.getLongValueMatchingLine(memory, 393 "memory.stat", 394 match); 395 } 396 } 397 return CgroupV1SubsystemController.longValOrUnlimited(retval); 398 } 399 400 public long getMemoryMaxUsage() { 401 return getLongValue(memory, "memory.max_usage_in_bytes"); 402 } 403 404 public long getMemoryUsage() { 405 return getLongValue(memory, "memory.usage_in_bytes"); 406 } 407 408 public long getKernelMemoryFailCount() { 409 return getLongValue(memory, "memory.kmem.failcnt"); 410 } 411 412 public long getKernelMemoryLimit() { 413 return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.kmem.limit_in_bytes")); 414 } 415 416 public long getKernelMemoryMaxUsage() { 417 return getLongValue(memory, "memory.kmem.max_usage_in_bytes"); 418 } 419 420 public long getKernelMemoryUsage() { 421 return getLongValue(memory, "memory.kmem.usage_in_bytes"); 422 } 423 424 public long getTcpMemoryFailCount() { 425 return getLongValue(memory, "memory.kmem.tcp.failcnt"); 426 } 427 428 public long getTcpMemoryLimit() { 429 return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.kmem.tcp.limit_in_bytes")); 430 } 431 432 public long getTcpMemoryMaxUsage() { 433 return getLongValue(memory, "memory.kmem.tcp.max_usage_in_bytes"); 434 } 435 436 public long getTcpMemoryUsage() { 437 return getLongValue(memory, "memory.kmem.tcp.usage_in_bytes"); 438 } 439 440 public long getMemoryAndSwapFailCount() { 441 return getLongValue(memory, "memory.memsw.failcnt"); 442 } 443 444 public long getMemoryAndSwapLimit() { 445 long retval = getLongValue(memory, "memory.memsw.limit_in_bytes"); 446 if (retval > CgroupV1SubsystemController.UNLIMITED_MIN) { 447 if (memory.isHierarchical()) { 448 // memory.memsw.limit_in_bytes returned unlimited, attempt 449 // hierarchical memory limit 450 String match = "hierarchical_memsw_limit"; 451 retval = CgroupV1SubsystemController.getLongValueMatchingLine(memory, 452 "memory.stat", 453 match); 454 } 455 } 456 return CgroupV1SubsystemController.longValOrUnlimited(retval); 457 } 458 459 public long getMemoryAndSwapMaxUsage() { 460 return getLongValue(memory, "memory.memsw.max_usage_in_bytes"); 461 } 462 463 public long getMemoryAndSwapUsage() { 464 return getLongValue(memory, "memory.memsw.usage_in_bytes"); 465 } 466 467 public Boolean isMemoryOOMKillEnabled() { 468 long val = CgroupV1SubsystemController.getLongEntry(memory, "memory.oom_control", "oom_kill_disable"); 469 return (val == 0); 470 } 471 472 public long getMemorySoftLimit() { 473 return CgroupV1SubsystemController.longValOrUnlimited(getLongValue(memory, "memory.soft_limit_in_bytes")); 474 } 475 476 477 /***************************************************************** 478 * BlKIO Subsystem 479 ****************************************************************/ 480 481 482 public long getBlkIOServiceCount() { 483 return CgroupV1SubsystemController.getLongEntry(blkio, "blkio.throttle.io_service_bytes", "Total"); 484 } 485 486 public long getBlkIOServiced() { 487 return CgroupV1SubsystemController.getLongEntry(blkio, "blkio.throttle.io_serviced", "Total"); 488 } 489 490 }