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