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