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