changeset 14123:26c1140d2613

8226575: OperatingSystemMXBean should be made container aware Reviewed-by: andrew
author sgehwolf
date Fri, 03 Jul 2020 15:09:27 +0200
parents 4c96e2f298dc
children b52035c5f3fa
files make/mapfiles/libmanagement/mapfile-vers src/aix/native/sun/management/AixOperatingSystem.c src/linux/classes/jdk/internal/platform/cgroupv1/Metrics.java src/linux/classes/jdk/internal/platform/cgroupv1/SubSystem.java src/share/classes/com/sun/management/OperatingSystemMXBean.java src/solaris/classes/sun/management/OperatingSystemImpl.java src/solaris/native/sun/management/LinuxOperatingSystem.c src/solaris/native/sun/management/MacosxOperatingSystem.c src/solaris/native/sun/management/OperatingSystemImpl.c src/solaris/native/sun/management/SolarisOperatingSystem.c
diffstat 10 files changed, 238 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
--- a/make/mapfiles/libmanagement/mapfile-vers	Wed Aug 26 03:54:58 2020 +0100
+++ b/make/mapfiles/libmanagement/mapfile-vers	Fri Jul 03 15:09:27 2020 +0200
@@ -28,15 +28,17 @@
 SUNWprivate_1.1 {
 	global:
 	    Java_sun_management_OperatingSystemImpl_getCommittedVirtualMemorySize;
-	    Java_sun_management_OperatingSystemImpl_getFreePhysicalMemorySize;
-	    Java_sun_management_OperatingSystemImpl_getFreeSwapSpaceSize;
+	    Java_sun_management_OperatingSystemImpl_getFreePhysicalMemorySize0;
+	    Java_sun_management_OperatingSystemImpl_getFreeSwapSpaceSize0;
 	    Java_sun_management_OperatingSystemImpl_getMaxFileDescriptorCount;
 	    Java_sun_management_OperatingSystemImpl_getOpenFileDescriptorCount;
 	    Java_sun_management_OperatingSystemImpl_getProcessCpuLoad;
 	    Java_sun_management_OperatingSystemImpl_getProcessCpuTime;
-	    Java_sun_management_OperatingSystemImpl_getSystemCpuLoad;
-	    Java_sun_management_OperatingSystemImpl_getTotalPhysicalMemorySize;
-	    Java_sun_management_OperatingSystemImpl_getTotalSwapSpaceSize;
+	    Java_sun_management_OperatingSystemImpl_getSystemCpuLoad0;
+	    Java_sun_management_OperatingSystemImpl_getTotalPhysicalMemorySize0;
+	    Java_sun_management_OperatingSystemImpl_getTotalSwapSpaceSize0;
+	    Java_sun_management_OperatingSystemImpl_getSingleCpuLoad0;
+	    Java_sun_management_OperatingSystemImpl_getHostConfiguredCpuCount0;
 	    Java_sun_management_OperatingSystemImpl_initialize;
 	    Java_sun_management_ClassLoadingImpl_setVerboseClass;
             Java_sun_management_DiagnosticCommandImpl_executeDiagnosticCommand;
--- a/src/aix/native/sun/management/AixOperatingSystem.c	Wed Aug 26 03:54:58 2020 +0100
+++ b/src/aix/native/sun/management/AixOperatingSystem.c	Fri Jul 03 15:09:27 2020 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2017 SAP SE. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
@@ -27,7 +27,7 @@
 #include "sun_management_OperatingSystemImpl.h"
 
 JNIEXPORT jdouble JNICALL
-Java_sun_management_OperatingSystemImpl_getSystemCpuLoad
+Java_sun_management_OperatingSystemImpl_getSystemCpuLoad0
 (JNIEnv *env, jobject dummy)
 {
   return -1.0;
--- a/src/linux/classes/jdk/internal/platform/cgroupv1/Metrics.java	Wed Aug 26 03:54:58 2020 +0100
+++ b/src/linux/classes/jdk/internal/platform/cgroupv1/Metrics.java	Fri Jul 03 15:09:27 2020 +0200
@@ -31,6 +31,9 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.util.stream.Stream;
 
 public class Metrics implements jdk.internal.platform.Metrics {
@@ -73,7 +76,7 @@
          * 34 28 0:29 / /sys/fs/cgroup/MemorySubSystem rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,MemorySubSystem
          */
         try (Stream<String> lines =
-             Files.lines(Paths.get("/proc/self/mountinfo"))) {
+             readFilePrivileged(Paths.get("/proc/self/mountinfo"))) {
 
             lines.filter(line -> line.contains(" - cgroup "))
                  .map(line -> line.split(" "))
@@ -107,7 +110,7 @@
          *
          */
         try (Stream<String> lines =
-             Files.lines(Paths.get("/proc/self/cgroup"))) {
+             readFilePrivileged(Paths.get("/proc/self/cgroup"))) {
 
             lines.map(line -> line.split(":"))
                  .filter(line -> (line.length >= 3))
@@ -125,6 +128,25 @@
         return null;
     }
 
+    static Stream<String> readFilePrivileged(Path path) throws IOException {
+        try {
+            PrivilegedExceptionAction<Stream<String>> pea = () -> Files.lines(path);
+            return AccessController.doPrivileged(pea);
+        } catch (PrivilegedActionException e) {
+            unwrapIOExceptionAndRethrow(e);
+            throw new InternalError(e.getCause());
+        }
+    }
+
+    static void unwrapIOExceptionAndRethrow(PrivilegedActionException pae) throws IOException {
+        Throwable x = pae.getCause();
+        if (x instanceof IOException)
+            throw (IOException) x;
+        if (x instanceof RuntimeException)
+            throw (RuntimeException) x;
+        if (x instanceof Error)
+            throw (Error) x;
+    }
     /**
      * createSubSystem objects and initialize mount points
      */
--- a/src/linux/classes/jdk/internal/platform/cgroupv1/SubSystem.java	Wed Aug 26 03:54:58 2020 +0100
+++ b/src/linux/classes/jdk/internal/platform/cgroupv1/SubSystem.java	Fri Jul 03 15:09:27 2020 +0200
@@ -30,6 +30,9 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.util.ArrayList;
 import java.util.Optional;
 import java.util.stream.Stream;
@@ -88,14 +91,24 @@
     public static String getStringValue(SubSystem subsystem, String parm) {
         if (subsystem == null) return null;
 
-        try(BufferedReader bufferedReader = Files.newBufferedReader(Paths.get(subsystem.path(), parm))) {
+        try {
+            return subsystem.readStringValue(parm);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    private String readStringValue(String param) throws IOException {
+        PrivilegedExceptionAction<BufferedReader> pea = () ->
+                Files.newBufferedReader(Paths.get(path(), param));
+        try (BufferedReader bufferedReader =
+                     AccessController.doPrivileged(pea)) {
             String line = bufferedReader.readLine();
             return line;
+        } catch (PrivilegedActionException e) {
+            Metrics.unwrapIOExceptionAndRethrow(e);
+            throw new InternalError(e.getCause());
         }
-        catch (IOException e) {
-            return null;
-        }
-
     }
 
     public static long getLongValue(SubSystem subsystem, String parm) {
@@ -136,7 +149,7 @@
 
         if (subsystem == null) return 0L;
 
-        try (Stream<String> lines = Files.lines(Paths.get(subsystem.path(), parm))) {
+        try (Stream<String> lines = Metrics.readFilePrivileged(Paths.get(subsystem.path(), parm))) {
 
             Optional<String> result = lines.map(line -> line.split(" "))
                                            .filter(line -> (line.length == 2 &&
--- a/src/share/classes/com/sun/management/OperatingSystemMXBean.java	Wed Aug 26 03:54:58 2020 +0100
+++ b/src/share/classes/com/sun/management/OperatingSystemMXBean.java	Fri Jul 03 15:09:27 2020 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -30,6 +30,12 @@
  * on which the Java virtual machine is running.
  *
  * <p>
+ * This interface provides information about the operating environment
+ * on which the Java virtual machine is running. That might be a native
+ * operating system, a virtualized operating system environment, or a
+ * container-managed environment.
+ *
+ * <p>
  * The <tt>OperatingSystemMXBean</tt> object returned by
  * {@link java.lang.management.ManagementFactory#getOperatingSystemMXBean()}
  * is an instance of the implementation class of this interface
--- a/src/solaris/classes/sun/management/OperatingSystemImpl.java	Wed Aug 26 03:54:58 2020 +0100
+++ b/src/solaris/classes/sun/management/OperatingSystemImpl.java	Fri Jul 03 15:09:27 2020 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,9 @@
 
 package sun.management;
 
+import jdk.internal.platform.Metrics;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Implementation class for the operating system.
  * Standard and committed hotspot-specific metrics if any.
@@ -35,20 +38,134 @@
 class OperatingSystemImpl extends BaseOperatingSystemImpl
     implements com.sun.management.UnixOperatingSystemMXBean {
 
+    private static final int MAX_ATTEMPTS_NUMBER = 10;
+    private final Metrics containerMetrics;
+
     OperatingSystemImpl(VMManagement vm) {
         super(vm);
+        this.containerMetrics = jdk.internal.platform.Container.metrics();
+    }
+
+    public long getTotalSwapSpaceSize() {
+        if (containerMetrics != null) {
+            long limit = containerMetrics.getMemoryAndSwapLimit();
+            // The memory limit metrics is not available if JVM runs on Linux host (not in a docker container)
+            // or if a docker container was started without specifying a memory limit (without '--memory='
+            // Docker option). In latter case there is no limit on how much memory the container can use and
+            // it can use as much memory as the host's OS allows.
+            long memLimit = containerMetrics.getMemoryLimit();
+            if (limit >= 0 && memLimit >= 0) {
+                return limit - memLimit;
+            }
+        }
+        return getTotalSwapSpaceSize0();
+    }
+
+    public long getFreeSwapSpaceSize() {
+        if (containerMetrics != null) {
+            long memSwapLimit = containerMetrics.getMemoryAndSwapLimit();
+            long memLimit = containerMetrics.getMemoryLimit();
+            if (memSwapLimit >= 0 && memLimit >= 0) {
+                for (int attempt = 0; attempt < MAX_ATTEMPTS_NUMBER; attempt++) {
+                    long memSwapUsage = containerMetrics.getMemoryAndSwapUsage();
+                    long memUsage = containerMetrics.getMemoryUsage();
+                    if (memSwapUsage > 0 && memUsage > 0) {
+                        // We read "memory usage" and "memory and swap usage" not atomically,
+                        // and it's possible to get the negative value when subtracting these two.
+                        // If this happens just retry the loop for a few iterations.
+                        if ((memSwapUsage - memUsage) >= 0) {
+                            return memSwapLimit - memLimit - (memSwapUsage - memUsage);
+                        }
+                    }
+                }
+            }
+        }
+        return getFreeSwapSpaceSize0();
+    }
+
+    public long getFreePhysicalMemorySize() {
+        if (containerMetrics != null) {
+            long usage = containerMetrics.getMemoryUsage();
+            long limit = containerMetrics.getMemoryLimit();
+            if (usage > 0 && limit >= 0) {
+                return limit - usage;
+            }
+        }
+        return getFreePhysicalMemorySize0();
+    }
+
+    public long getTotalPhysicalMemorySize() {
+        if (containerMetrics != null) {
+            long limit = containerMetrics.getMemoryLimit();
+            if (limit >= 0) {
+                return limit;
+            }
+        }
+        return getTotalPhysicalMemorySize0();
+    }
+
+    public double getSystemCpuLoad() {
+        if (containerMetrics != null) {
+            long quota = containerMetrics.getCpuQuota();
+            if (quota > 0) {
+                long periodLength = containerMetrics.getCpuPeriod();
+                long numPeriods = containerMetrics.getCpuNumPeriods();
+                long usageNanos = containerMetrics.getCpuUsage();
+                if (periodLength > 0 && numPeriods > 0 && usageNanos > 0) {
+                    long elapsedNanos = TimeUnit.MICROSECONDS.toNanos(periodLength * numPeriods);
+                    double systemLoad = (double) usageNanos / elapsedNanos;
+                    // Ensure the return value is in the range 0.0 -> 1.0
+                    systemLoad = Math.max(0.0, systemLoad);
+                    systemLoad = Math.min(1.0, systemLoad);
+                    return systemLoad;
+                }
+                return -1;
+            } else {
+                // If CPU quotas are not active then find the average system load for
+                // all online CPUs that are allowed to run this container.
+
+                // If the cpuset is the same as the host's one there is no need to iterate over each CPU
+                if (isCpuSetSameAsHostCpuSet()) {
+                    return getSystemCpuLoad0();
+                } else {
+                    int[] cpuSet = containerMetrics.getEffectiveCpuSetCpus();
+                    if (cpuSet != null && cpuSet.length > 0) {
+                        double systemLoad = 0.0;
+                        for (int cpu : cpuSet) {
+                            double cpuLoad = getSingleCpuLoad0(cpu);
+                            if (cpuLoad < 0) {
+                                return -1;
+                            }
+                            systemLoad += cpuLoad;
+                        }
+                        return systemLoad / cpuSet.length;
+                    }
+                    return -1;
+                }
+            }
+        }
+        return getSystemCpuLoad0();
+    }
+
+    private boolean isCpuSetSameAsHostCpuSet() {
+        if (containerMetrics != null) {
+            return containerMetrics.getCpuSetCpus().length == getHostConfiguredCpuCount0();
+        }
+        return false;
     }
 
     public native long getCommittedVirtualMemorySize();
-    public native long getTotalSwapSpaceSize();
-    public native long getFreeSwapSpaceSize();
+    private native long getTotalSwapSpaceSize0();
+    private native long getFreeSwapSpaceSize0();
     public native long getProcessCpuTime();
-    public native long getFreePhysicalMemorySize();
-    public native long getTotalPhysicalMemorySize();
+    private native long getFreePhysicalMemorySize0();
+    private native long getTotalPhysicalMemorySize0();
     public native long getOpenFileDescriptorCount();
     public native long getMaxFileDescriptorCount();
-    public native double getSystemCpuLoad();
+    private native double getSystemCpuLoad0();
     public native double getProcessCpuLoad();
+    private native double getSingleCpuLoad0(int cpuNum);
+    private native int getHostConfiguredCpuCount0();
 
     static {
         initialize();
--- a/src/solaris/native/sun/management/LinuxOperatingSystem.c	Wed Aug 26 03:54:58 2020 +0100
+++ b/src/solaris/native/sun/management/LinuxOperatingSystem.c	Fri Jul 03 15:09:27 2020 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -197,17 +197,20 @@
  * This method must be called first, before any data can be gathererd.
  */
 int perfInit() {
-    static int initialized=1;
+    static int initialized = 0;
 
     if (!initialized) {
         int  i;
 
-        int n = sysconf(_SC_NPROCESSORS_ONLN);
+        // We need to allocate counters for all CPUs, including ones that
+        // are currently offline as they could be turned online later.
+        int n = sysconf(_SC_NPROCESSORS_CONF);
         if (n <= 0) {
             n = 1;
         }
 
         counters.cpus = calloc(n,sizeof(ticks));
+        counters.nProcs = n;
         if (counters.cpus != NULL)  {
             // For the CPU load
             get_totalticks(-1, &counters.cpuTicks);
@@ -319,10 +322,10 @@
 }
 
 JNIEXPORT jdouble JNICALL
-Java_sun_management_OperatingSystemImpl_getSystemCpuLoad
+Java_sun_management_OperatingSystemImpl_getSystemCpuLoad0
 (JNIEnv *env, jobject dummy)
 {
-    if(perfInit() == 0) {
+    if (perfInit() == 0) {
         return get_cpu_load(-1);
     } else {
         return -1.0;
@@ -333,9 +336,31 @@
 Java_sun_management_OperatingSystemImpl_getProcessCpuLoad
 (JNIEnv *env, jobject dummy)
 {
-    if(perfInit() == 0) {
+    if (perfInit() == 0) {
         return get_process_load();
     } else {
         return -1.0;
     }
 }
+
+JNIEXPORT jdouble JNICALL
+Java_sun_management_OperatingSystemImpl_getSingleCpuLoad0
+(JNIEnv *env, jobject mbean, jint cpu_number)
+{
+    if (perfInit() == 0 && cpu_number >= 0 && cpu_number < counters.nProcs) {
+        return get_cpu_load(cpu_number);
+    } else {
+        return -1.0;
+    }
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_management_OperatingSystemImpl_getHostConfiguredCpuCount0
+(JNIEnv *env, jobject mbean)
+{
+    if (perfInit() == 0) {
+        return counters.nProcs;
+    } else {
+       return -1;
+    }
+}
--- a/src/solaris/native/sun/management/MacosxOperatingSystem.c	Wed Aug 26 03:54:58 2020 +0100
+++ b/src/solaris/native/sun/management/MacosxOperatingSystem.c	Fri Jul 03 15:09:27 2020 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,7 +31,7 @@
 
 
 JNIEXPORT jdouble JNICALL
-Java_sun_management_OperatingSystemImpl_getSystemCpuLoad
+Java_sun_management_OperatingSystemImpl_getSystemCpuLoad0
 (JNIEnv *env, jobject dummy)
 {
     // This code is influenced by the darwin top source
--- a/src/solaris/native/sun/management/OperatingSystemImpl.c	Wed Aug 26 03:54:58 2020 +0100
+++ b/src/solaris/native/sun/management/OperatingSystemImpl.c	Fri Jul 03 15:09:27 2020 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -253,14 +253,14 @@
 }
 
 JNIEXPORT jlong JNICALL
-Java_sun_management_OperatingSystemImpl_getTotalSwapSpaceSize
+Java_sun_management_OperatingSystemImpl_getTotalSwapSpaceSize0
   (JNIEnv *env, jobject mbean)
 {
     return get_total_or_available_swap_space_size(env, JNI_FALSE);
 }
 
 JNIEXPORT jlong JNICALL
-Java_sun_management_OperatingSystemImpl_getFreeSwapSpaceSize
+Java_sun_management_OperatingSystemImpl_getFreeSwapSpaceSize0
   (JNIEnv *env, jobject mbean)
 {
     return get_total_or_available_swap_space_size(env, JNI_TRUE);
@@ -309,7 +309,7 @@
 }
 
 JNIEXPORT jlong JNICALL
-Java_sun_management_OperatingSystemImpl_getFreePhysicalMemorySize
+Java_sun_management_OperatingSystemImpl_getFreePhysicalMemorySize0
   (JNIEnv *env, jobject mbean)
 {
 #ifdef __APPLE__
@@ -343,7 +343,7 @@
 }
 
 JNIEXPORT jlong JNICALL
-Java_sun_management_OperatingSystemImpl_getTotalPhysicalMemorySize
+Java_sun_management_OperatingSystemImpl_getTotalPhysicalMemorySize0
   (JNIEnv *env, jobject mbean)
 {
 #ifdef _ALLBSD_SOURCE
@@ -462,6 +462,22 @@
 #endif
 }
 
+#ifndef __linux__
+JNIEXPORT jdouble JNICALL
+Java_sun_management_OperatingSystemImpl_getSingleCpuLoad0
+  (JNIEnv *env, jobject mbean, jint cpu_number)
+{
+    return -1.0;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_management_OperatingSystemImpl_getHostConfiguredCpuCount0
+  (JNIEnv *env, jobject mbean)
+{
+    return -1;
+}
+#endif
+
 JNIEXPORT jlong JNICALL
 Java_sun_management_OperatingSystemImpl_getMaxFileDescriptorCount
   (JNIEnv *env, jobject mbean)
--- a/src/solaris/native/sun/management/SolarisOperatingSystem.c	Wed Aug 26 03:54:58 2020 +0100
+++ b/src/solaris/native/sun/management/SolarisOperatingSystem.c	Fri Jul 03 15:09:27 2020 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -226,7 +226,7 @@
 }
 
 JNIEXPORT jdouble JNICALL
-Java_sun_management_OperatingSystemImpl_getSystemCpuLoad
+Java_sun_management_OperatingSystemImpl_getSystemCpuLoad0
 (JNIEnv *env, jobject dummy)
 {
     return get_cpu_load(-1);