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 }