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 }