1 /* 2 * Copyright (c) 2018, 2019, 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.Files; 30 import java.nio.file.Path; 31 import java.nio.file.Paths; 32 import java.security.AccessController; 33 import java.security.PrivilegedActionException; 34 import java.security.PrivilegedExceptionAction; 35 import java.util.stream.Stream; 36 37 import jdk.internal.platform.cgroupv1.SubSystem.MemorySubSystem; 38 39 public class Metrics implements jdk.internal.platform.Metrics { 40 private MemorySubSystem memory; 41 private SubSystem cpu; 42 private SubSystem cpuacct; 43 private SubSystem cpuset; 44 private SubSystem blkio; 45 private boolean activeSubSystems; 46 47 // Values returned larger than this number are unlimited. 48 static long unlimited_minimum = 0x7FFFFFFFFF000000L; 49 50 private static final Metrics INSTANCE = initContainerSubSystems(); 51 52 private static final String PROVIDER_NAME = "cgroupv1"; 53 54 private Metrics() { 55 activeSubSystems = false; 56 } 57 58 public static Metrics getInstance() { 59 return INSTANCE; 60 } 61 62 private static Metrics initContainerSubSystems() { 63 Metrics metrics = new Metrics(); 64 65 /** 66 * Find the cgroup mount points for subsystems 67 * by reading /proc/self/mountinfo 68 * 69 * Example for docker MemorySubSystem subsystem: 70 * 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/MemorySubSystem ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,MemorySubSystem 71 * 72 * Example for host: 73 * 34 28 0:29 / /sys/fs/cgroup/MemorySubSystem rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,MemorySubSystem 74 */ 75 try (Stream<String> lines = 76 readFilePrivileged(Paths.get("/proc/self/mountinfo"))) { 77 78 lines.filter(line -> line.contains(" - cgroup ")) 79 .map(line -> line.split(" ")) 80 .forEach(entry -> createSubSystem(metrics, entry)); 81 82 } catch (IOException e) { 83 return null; 84 } 85 86 /** 87 * Read /proc/self/cgroup and map host mount point to 88 * local one via /proc/self/mountinfo content above 89 * 90 * Docker example: 91 * 5:memory:/docker/6558aed8fc662b194323ceab5b964f69cf36b3e8af877a14b80256e93aecb044 92 * 93 * Host example: 94 * 5:memory:/user.slice 95 * 96 * Construct a path to the process specific memory and cpuset 97 * cgroup directory. 98 * 99 * For a container running under Docker from memory example above 100 * the paths would be: 101 * 102 * /sys/fs/cgroup/memory 103 * 104 * For a Host from memory example above the path would be: 105 * 106 * /sys/fs/cgroup/memory/user.slice 107 * 108 */ 109 try (Stream<String> lines = 110 readFilePrivileged(Paths.get("/proc/self/cgroup"))) { 111 112 lines.map(line -> line.split(":")) 113 .filter(line -> (line.length >= 3)) 114 .forEach(line -> setSubSystemPath(metrics, line)); 115 116 } catch (IOException e) { 117 return null; 118 } 119 120 // Return Metrics object if we found any subsystems. 121 if (metrics.activeSubSystems()) { 122 return metrics; 123 } 124 125 return null; 126 } 127 128 static Stream<String> readFilePrivileged(Path path) throws IOException { 129 try { 130 PrivilegedExceptionAction<Stream<String>> pea = () -> Files.lines(path); 131 return AccessController.doPrivileged(pea); 132 } catch (PrivilegedActionException e) { 133 unwrapIOExceptionAndRethrow(e); 134 throw new InternalError(e.getCause()); 135 } 136 } 137 138 static void unwrapIOExceptionAndRethrow(PrivilegedActionException pae) throws IOException { 139 Throwable x = pae.getCause(); 140 if (x instanceof IOException) 141 throw (IOException) x; 142 if (x instanceof RuntimeException) 143 throw (RuntimeException) x; 144 if (x instanceof Error) 145 throw (Error) x; 146 } 147 /** 148 * createSubSystem objects and initialize mount points 149 */ 150 private static void createSubSystem(Metrics metric, String[] mountentry) { 151 if (mountentry.length < 5) return; 152 153 Path p = Paths.get(mountentry[4]); 154 String[] subsystemNames = p.getFileName().toString().split(","); 155 156 for (String subsystemName: subsystemNames) { 157 switch (subsystemName) { 158 case "memory": 159 metric.setMemorySubSystem(new MemorySubSystem(mountentry[3], mountentry[4])); 160 break; 161 case "cpuset": 162 metric.setCpuSetSubSystem(new SubSystem(mountentry[3], mountentry[4])); 163 break; 164 case "cpuacct": 165 metric.setCpuAcctSubSystem(new SubSystem(mountentry[3], mountentry[4])); 166 break; 167 case "cpu": 168 metric.setCpuSubSystem(new SubSystem(mountentry[3], mountentry[4])); 169 break; 170 case "blkio": 171 metric.setBlkIOSubSystem(new SubSystem(mountentry[3], mountentry[4])); 172 break; 173 default: 174 // Ignore subsystems that we don't support 175 break; 176 } 177 } 178 } 179 180 /** 181 * setSubSystemPath based on the contents of /proc/self/cgroup 182 */ 183 private static void setSubSystemPath(Metrics metric, String[] entry) { 184 String controller; 185 String base; 186 SubSystem subsystem = null; 187 SubSystem subsystem2 = null; 188 189 controller = entry[1]; 190 base = entry[2]; 191 if (controller != null && base != null) { 192 switch (controller) { 193 case "memory": 194 subsystem = metric.MemorySubSystem(); 195 break; 196 case "cpuset": 197 subsystem = metric.CpuSetSubSystem(); 198 break; 199 case "cpu,cpuacct": 200 case "cpuacct,cpu": 201 subsystem = metric.CpuSubSystem(); 202 subsystem2 = metric.CpuAcctSubSystem(); 203 break; 204 case "cpuacct": 205 subsystem = metric.CpuAcctSubSystem(); 206 break; 207 case "cpu": 208 subsystem = metric.CpuSubSystem(); 209 break; 210 case "blkio": 211 subsystem = metric.BlkIOSubSystem(); 212 break; 213 // Ignore subsystems that we don't support 214 default: 215 break; 216 } 217 } 218 219 if (subsystem != null) { 220 subsystem.setPath(base); 221 if (subsystem instanceof MemorySubSystem) { 222 MemorySubSystem memorySubSystem = (MemorySubSystem)subsystem; 223 boolean isHierarchial = getHierarchical(memorySubSystem); 224 memorySubSystem.setHierarchical(isHierarchial); 225 } 226 metric.setActiveSubSystems(); 227 } 228 if (subsystem2 != null) { 229 subsystem2.setPath(base); 230 } 231 } 232 233 234 private static boolean getHierarchical(MemorySubSystem subsystem) { 235 long hierarchical = SubSystem.getLongValue(subsystem, "memory.use_hierarchy"); 236 return hierarchical > 0; 237 } 238 239 private void setActiveSubSystems() { 240 activeSubSystems = true; 241 } 242 243 private boolean activeSubSystems() { 244 return activeSubSystems; 245 } 246 247 private void setMemorySubSystem(MemorySubSystem memory) { 248 this.memory = memory; 249 } 250 251 private void setCpuSubSystem(SubSystem cpu) { 252 this.cpu = cpu; 253 } 254 255 private void setCpuAcctSubSystem(SubSystem cpuacct) { 256 this.cpuacct = cpuacct; 257 } 258 259 private void setCpuSetSubSystem(SubSystem cpuset) { 260 this.cpuset = cpuset; 261 } 262 263 private void setBlkIOSubSystem(SubSystem blkio) { 264 this.blkio = blkio; 265 } 266 267 private SubSystem MemorySubSystem() { 268 return memory; 269 } 270 271 private SubSystem CpuSubSystem() { 272 return cpu; 273 } 274 275 private SubSystem CpuAcctSubSystem() { 276 return cpuacct; 277 } 278 279 private SubSystem CpuSetSubSystem() { 280 return cpuset; 281 } 282 283 private SubSystem BlkIOSubSystem() { 284 return blkio; 285 } 286 287 public String getProvider() { 288 return PROVIDER_NAME; 289 } 290 291 /***************************************************************** 292 * CPU Accounting Subsystem 293 ****************************************************************/ 294 295 296 public long getCpuUsage() { 297 return SubSystem.getLongValue(cpuacct, "cpuacct.usage"); 298 } 299 300 public long[] getPerCpuUsage() { 301 String usagelist = SubSystem.getStringValue(cpuacct, "cpuacct.usage_percpu"); 302 if (usagelist == null) { 303 return new long[0]; 304 } 305 306 String list[] = usagelist.split(" "); 307 long percpu[] = new long[list.length]; 308 for (int i = 0; i < list.length; i++) { 309 percpu[i] = Long.parseLong(list[i]); 310 } 311 return percpu; 312 } 313 314 public long getCpuUserUsage() { 315 return SubSystem.getLongEntry(cpuacct, "cpuacct.stat", "user"); 316 } 317 318 public long getCpuSystemUsage() { 319 return SubSystem.getLongEntry(cpuacct, "cpuacct.stat", "system"); 320 } 321 322 323 /***************************************************************** 324 * CPU Subsystem 325 ****************************************************************/ 326 327 328 public long getCpuPeriod() { 329 return SubSystem.getLongValue(cpu, "cpu.cfs_period_us"); 330 } 331 332 public long getCpuQuota() { 333 return SubSystem.getLongValue(cpu, "cpu.cfs_quota_us"); 334 } 335 336 public long getCpuShares() { 337 long retval = SubSystem.getLongValue(cpu, "cpu.shares"); 338 if (retval == 0 || retval == 1024) 339 return -1; 340 else 341 return retval; 342 } 343 344 public long getCpuNumPeriods() { 345 return SubSystem.getLongEntry(cpu, "cpu.stat", "nr_periods"); 346 } 347 348 public long getCpuNumThrottled() { 349 return SubSystem.getLongEntry(cpu, "cpu.stat", "nr_throttled"); 350 } 351 352 public long getCpuThrottledTime() { 353 return SubSystem.getLongEntry(cpu, "cpu.stat", "throttled_time"); 354 } 355 356 public long getEffectiveCpuCount() { 357 return Runtime.getRuntime().availableProcessors(); 358 } 359 360 361 /***************************************************************** 362 * CPUSet Subsystem 363 ****************************************************************/ 364 365 public int[] getCpuSetCpus() { 366 return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.cpus")); 367 } 368 369 public int[] getEffectiveCpuSetCpus() { 370 return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.effective_cpus")); 371 } 372 373 public int[] getCpuSetMems() { 374 return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.mems")); 375 } 376 377 public int[] getEffectiveCpuSetMems() { 378 return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.effective_mems")); 379 } 380 381 public double getCpuSetMemoryPressure() { 382 return SubSystem.getDoubleValue(cpuset, "cpuset.memory_pressure"); 383 } 384 385 public boolean isCpuSetMemoryPressureEnabled() { 386 long val = SubSystem.getLongValue(cpuset, "cpuset.memory_pressure_enabled"); 387 return (val == 1); 388 } 389 390 391 /***************************************************************** 392 * Memory Subsystem 393 ****************************************************************/ 394 395 396 public long getMemoryFailCount() { 397 return SubSystem.getLongValue(memory, "memory.failcnt"); 398 } 399 400 public long getMemoryLimit() { 401 long retval = SubSystem.getLongValue(memory, "memory.limit_in_bytes"); 402 if (retval > unlimited_minimum) { 403 if (memory.isHierarchical()) { 404 // memory.limit_in_bytes returned unlimited, attempt 405 // hierarchical memory limit 406 String match = "hierarchical_memory_limit"; 407 retval = SubSystem.getLongValueMatchingLine(memory, 408 "memory.stat", 409 match, 410 Metrics::convertHierachicalLimitLine); 411 } 412 } 413 return retval > unlimited_minimum ? -1L : retval; 414 } 415 416 public static long convertHierachicalLimitLine(String line) { 417 String[] tokens = line.split("\\s"); 418 if (tokens.length == 2) { 419 String strVal = tokens[1]; 420 return SubSystem.convertStringToLong(strVal); 421 } 422 return unlimited_minimum + 1; // unlimited 423 } 424 425 public long getMemoryMaxUsage() { 426 return SubSystem.getLongValue(memory, "memory.max_usage_in_bytes"); 427 } 428 429 public long getMemoryUsage() { 430 return SubSystem.getLongValue(memory, "memory.usage_in_bytes"); 431 } 432 433 public long getKernelMemoryFailCount() { 434 return SubSystem.getLongValue(memory, "memory.kmem.failcnt"); 435 } 436 437 public long getKernelMemoryLimit() { 438 long retval = SubSystem.getLongValue(memory, "memory.kmem.limit_in_bytes"); 439 return retval > unlimited_minimum ? -1L : retval; 440 } 441 442 public long getKernelMemoryMaxUsage() { 443 return SubSystem.getLongValue(memory, "memory.kmem.max_usage_in_bytes"); 444 } 445 446 public long getKernelMemoryUsage() { 447 return SubSystem.getLongValue(memory, "memory.kmem.usage_in_bytes"); 448 } 449 450 public long getTcpMemoryFailCount() { 451 return SubSystem.getLongValue(memory, "memory.kmem.tcp.failcnt"); 452 } 453 454 public long getTcpMemoryLimit() { 455 long retval = SubSystem.getLongValue(memory, "memory.kmem.tcp.limit_in_bytes"); 456 return retval > unlimited_minimum ? -1L : retval; 457 } 458 459 public long getTcpMemoryMaxUsage() { 460 return SubSystem.getLongValue(memory, "memory.kmem.tcp.max_usage_in_bytes"); 461 } 462 463 public long getTcpMemoryUsage() { 464 return SubSystem.getLongValue(memory, "memory.kmem.tcp.usage_in_bytes"); 465 } 466 467 public long getMemoryAndSwapFailCount() { 468 return SubSystem.getLongValue(memory, "memory.memsw.failcnt"); 469 } 470 471 public long getMemoryAndSwapLimit() { 472 long retval = SubSystem.getLongValue(memory, "memory.memsw.limit_in_bytes"); 473 if (retval > unlimited_minimum) { 474 if (memory.isHierarchical()) { 475 // memory.memsw.limit_in_bytes returned unlimited, attempt 476 // hierarchical memory limit 477 String match = "hierarchical_memsw_limit"; 478 retval = SubSystem.getLongValueMatchingLine(memory, 479 "memory.stat", 480 match, 481 Metrics::convertHierachicalLimitLine); 482 } 483 } 484 return retval > unlimited_minimum ? -1L : retval; 485 } 486 487 public long getMemoryAndSwapMaxUsage() { 488 return SubSystem.getLongValue(memory, "memory.memsw.max_usage_in_bytes"); 489 } 490 491 public long getMemoryAndSwapUsage() { 492 return SubSystem.getLongValue(memory, "memory.memsw.usage_in_bytes"); 493 } 494 495 public boolean isMemoryOOMKillEnabled() { 496 long val = SubSystem.getLongEntry(memory, "memory.oom_control", "oom_kill_disable"); 497 return (val == 0); 498 } 499 500 public long getMemorySoftLimit() { 501 long retval = SubSystem.getLongValue(memory, "memory.soft_limit_in_bytes"); 502 return retval > unlimited_minimum ? -1L : retval; 503 } 504 505 506 /***************************************************************** 507 * BlKIO Subsystem 508 ****************************************************************/ 509 510 511 public long getBlkIOServiceCount() { 512 return SubSystem.getLongEntry(blkio, "blkio.throttle.io_service_bytes", "Total"); 513 } 514 515 public long getBlkIOServiced() { 516 return SubSystem.getLongEntry(blkio, "blkio.throttle.io_serviced", "Total"); 517 } 518 519 }