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.BufferedReader; 29 import java.io.IOException; 30 import java.nio.file.Files; 31 import java.nio.file.Path; 32 import java.nio.file.Paths; 33 import java.util.stream.Stream; 34 35 public class Metrics implements jdk.internal.platform.Metrics { 36 private SubSystem memory; 37 private SubSystem cpu; 38 private SubSystem cpuacct; 39 private SubSystem cpuset; 40 private SubSystem blkio; 41 private boolean activeSubSystems; 42 43 // Values returned larger than this number are unlimited. 44 static long unlimited_minimum = 0x7FFFFFFFFF000000L; 45 46 private static final Metrics INSTANCE = initContainerSubSystems(); 47 48 private static final String PROVIDER_NAME = "cgroupv1"; 49 50 private Metrics() { 51 activeSubSystems = false; 52 } 53 54 public static Metrics getInstance() { 55 return INSTANCE; 56 } 57 58 private static Metrics initContainerSubSystems() { 59 Metrics metrics = new Metrics(); 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 Files.lines(Paths.get("/proc/self/mountinfo"))) { 73 74 lines.filter(line -> line.contains(" - cgroup ")) 75 .map(line -> line.split(" ")) 76 .forEach(entry -> createSubSystem(metrics, 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 Files.lines(Paths.get("/proc/self/cgroup"))) { 107 108 lines.map(line -> line.split(":")) 109 .filter(line -> (line.length >= 3)) 110 .forEach(line -> setSubSystemPath(metrics, line)); 111 112 } catch (IOException e) { 113 return null; 114 } 115 116 // Return Metrics object if we found any subsystems. 117 if (metrics.activeSubSystems()) { 118 return metrics; 119 } 120 121 return null; 122 } 123 124 /** 125 * createSubSystem objects and initialize mount points 126 */ 127 private static void createSubSystem(Metrics metric, 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 metric.setMemorySubSystem(new SubSystem(mountentry[3], mountentry[4])); 137 break; 138 case "cpuset": 139 metric.setCpuSetSubSystem(new SubSystem(mountentry[3], mountentry[4])); 140 break; 141 case "cpuacct": 142 metric.setCpuAcctSubSystem(new SubSystem(mountentry[3], mountentry[4])); 143 break; 144 case "cpu": 145 metric.setCpuSubSystem(new SubSystem(mountentry[3], mountentry[4])); 146 break; 147 case "blkio": 148 metric.setBlkIOSubSystem(new SubSystem(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 setSubSystemPath(Metrics metric, String[] entry) { 161 String controller; 162 String base; 163 SubSystem subsystem = null; 164 SubSystem subsystem2 = null; 165 166 controller = entry[1]; 167 base = entry[2]; 168 if (controller != null && base != null) { 169 switch (controller) { 170 case "memory": 171 subsystem = metric.MemorySubSystem(); 172 break; 173 case "cpuset": 174 subsystem = metric.CpuSetSubSystem(); 175 break; 176 case "cpu,cpuacct": 177 case "cpuacct,cpu": 178 subsystem = metric.CpuSubSystem(); 179 subsystem2 = metric.CpuAcctSubSystem(); 180 break; 181 case "cpuacct": 182 subsystem = metric.CpuAcctSubSystem(); 183 break; 184 case "cpu": 185 subsystem = metric.CpuSubSystem(); 186 break; 187 case "blkio": 188 subsystem = metric.BlkIOSubSystem(); 189 break; 190 // Ignore subsystems that we don't support 191 default: 192 break; 193 } 194 } 195 196 if (subsystem != null) { 197 subsystem.setPath(base); 198 metric.setActiveSubSystems(); 199 } 200 if (subsystem2 != null) { 201 subsystem2.setPath(base); 202 } 203 } 204 205 206 private void setActiveSubSystems() { 207 activeSubSystems = true; 208 } 209 210 private boolean activeSubSystems() { 211 return activeSubSystems; 212 } 213 214 private void setMemorySubSystem(SubSystem memory) { 215 this.memory = memory; 216 } 217 218 private void setCpuSubSystem(SubSystem cpu) { 219 this.cpu = cpu; 220 } 221 222 private void setCpuAcctSubSystem(SubSystem cpuacct) { 223 this.cpuacct = cpuacct; 224 } 225 226 private void setCpuSetSubSystem(SubSystem cpuset) { 227 this.cpuset = cpuset; 228 } 229 230 private void setBlkIOSubSystem(SubSystem blkio) { 231 this.blkio = blkio; 232 } 233 234 private SubSystem MemorySubSystem() { 235 return memory; 236 } 237 238 private SubSystem CpuSubSystem() { 239 return cpu; 240 } 241 242 private SubSystem CpuAcctSubSystem() { 243 return cpuacct; 244 } 245 246 private SubSystem CpuSetSubSystem() { 247 return cpuset; 248 } 249 250 private SubSystem BlkIOSubSystem() { 251 return blkio; 252 } 253 254 public String getProvider() { 255 return PROVIDER_NAME; 256 } 257 258 /***************************************************************** 259 * CPU Accounting Subsystem 260 ****************************************************************/ 261 262 263 public long getCpuUsage() { 264 return SubSystem.getLongValue(cpuacct, "cpuacct.usage"); 265 } 266 267 public long[] getPerCpuUsage() { 268 String usagelist = SubSystem.getStringValue(cpuacct, "cpuacct.usage_percpu"); 269 if (usagelist == null) { 270 return new long[0]; 271 } 272 273 String list[] = usagelist.split(" "); 274 long percpu[] = new long[list.length]; 275 for (int i = 0; i < list.length; i++) { 276 percpu[i] = Long.parseLong(list[i]); 277 } 278 return percpu; 279 } 280 281 public long getCpuUserUsage() { 282 return SubSystem.getLongEntry(cpuacct, "cpuacct.stat", "user"); 283 } 284 285 public long getCpuSystemUsage() { 286 return SubSystem.getLongEntry(cpuacct, "cpuacct.stat", "system"); 287 } 288 289 290 /***************************************************************** 291 * CPU Subsystem 292 ****************************************************************/ 293 294 295 public long getCpuPeriod() { 296 return SubSystem.getLongValue(cpu, "cpu.cfs_period_us"); 297 } 298 299 public long getCpuQuota() { 300 return SubSystem.getLongValue(cpu, "cpu.cfs_quota_us"); 301 } 302 303 public long getCpuShares() { 304 long retval = SubSystem.getLongValue(cpu, "cpu.shares"); 305 if (retval == 0 || retval == 1024) 306 return -1; 307 else 308 return retval; 309 } 310 311 public long getCpuNumPeriods() { 312 return SubSystem.getLongEntry(cpu, "cpu.stat", "nr_periods"); 313 } 314 315 public long getCpuNumThrottled() { 316 return SubSystem.getLongEntry(cpu, "cpu.stat", "nr_throttled"); 317 } 318 319 public long getCpuThrottledTime() { 320 return SubSystem.getLongEntry(cpu, "cpu.stat", "throttled_time"); 321 } 322 323 public long getEffectiveCpuCount() { 324 return Runtime.getRuntime().availableProcessors(); 325 } 326 327 328 /***************************************************************** 329 * CPUSet Subsystem 330 ****************************************************************/ 331 332 public int[] getCpuSetCpus() { 333 return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.cpus")); 334 } 335 336 public int[] getEffectiveCpuSetCpus() { 337 return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.effective_cpus")); 338 } 339 340 public int[] getCpuSetMems() { 341 return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.mems")); 342 } 343 344 public int[] getEffectiveCpuSetMems() { 345 return SubSystem.StringRangeToIntArray(SubSystem.getStringValue(cpuset, "cpuset.effective_mems")); 346 } 347 348 public double getCpuSetMemoryPressure() { 349 return SubSystem.getDoubleValue(cpuset, "cpuset.memory_pressure"); 350 } 351 352 public boolean isCpuSetMemoryPressureEnabled() { 353 long val = SubSystem.getLongValue(cpuset, "cpuset.memory_pressure_enabled"); 354 return (val == 1); 355 } 356 357 358 /***************************************************************** 359 * Memory Subsystem 360 ****************************************************************/ 361 362 363 public long getMemoryFailCount() { 364 return SubSystem.getLongValue(memory, "memory.failcnt"); 365 } 366 367 public long getMemoryLimit() { 368 long retval = SubSystem.getLongValue(memory, "memory.limit_in_bytes"); 369 return retval > unlimited_minimum ? -1L : retval; 370 } 371 372 public long getMemoryMaxUsage() { 373 return SubSystem.getLongValue(memory, "memory.max_usage_in_bytes"); 374 } 375 376 public long getMemoryUsage() { 377 return SubSystem.getLongValue(memory, "memory.usage_in_bytes"); 378 } 379 380 public long getKernelMemoryFailCount() { 381 return SubSystem.getLongValue(memory, "memory.kmem.failcnt"); 382 } 383 384 public long getKernelMemoryLimit() { 385 long retval = SubSystem.getLongValue(memory, "memory.kmem.limit_in_bytes"); 386 return retval > unlimited_minimum ? -1L : retval; 387 } 388 389 public long getKernelMemoryMaxUsage() { 390 return SubSystem.getLongValue(memory, "memory.kmem.max_usage_in_bytes"); 391 } 392 393 public long getKernelMemoryUsage() { 394 return SubSystem.getLongValue(memory, "memory.kmem.usage_in_bytes"); 395 } 396 397 public long getTcpMemoryFailCount() { 398 return SubSystem.getLongValue(memory, "memory.kmem.tcp.failcnt"); 399 } 400 401 public long getTcpMemoryLimit() { 402 long retval = SubSystem.getLongValue(memory, "memory.kmem.tcp.limit_in_bytes"); 403 return retval > unlimited_minimum ? -1L : retval; 404 } 405 406 public long getTcpMemoryMaxUsage() { 407 return SubSystem.getLongValue(memory, "memory.kmem.tcp.max_usage_in_bytes"); 408 } 409 410 public long getTcpMemoryUsage() { 411 return SubSystem.getLongValue(memory, "memory.kmem.tcp.usage_in_bytes"); 412 } 413 414 public long getMemoryAndSwapFailCount() { 415 return SubSystem.getLongValue(memory, "memory.memsw.failcnt"); 416 } 417 418 public long getMemoryAndSwapLimit() { 419 long retval = SubSystem.getLongValue(memory, "memory.memsw.limit_in_bytes"); 420 return retval > unlimited_minimum ? -1L : retval; 421 } 422 423 public long getMemoryAndSwapMaxUsage() { 424 return SubSystem.getLongValue(memory, "memory.memsw.max_usage_in_bytes"); 425 } 426 427 public long getMemoryAndSwapUsage() { 428 return SubSystem.getLongValue(memory, "memory.memsw.usage_in_bytes"); 429 } 430 431 public boolean isMemoryOOMKillEnabled() { 432 long val = SubSystem.getLongEntry(memory, "memory.oom_control", "oom_kill_disable"); 433 return (val == 0); 434 } 435 436 public long getMemorySoftLimit() { 437 long retval = SubSystem.getLongValue(memory, "memory.soft_limit_in_bytes"); 438 return retval > unlimited_minimum ? -1L : retval; 439 } 440 441 442 /***************************************************************** 443 * BlKIO Subsystem 444 ****************************************************************/ 445 446 447 public long getBlkIOServiceCount() { 448 return SubSystem.getLongEntry(blkio, "blkio.throttle.io_service_bytes", "Total"); 449 } 450 451 public long getBlkIOServiced() { 452 return SubSystem.getLongEntry(blkio, "blkio.throttle.io_serviced", "Total"); 453 } 454 455 }