6523160: RuntimeMXBean.getUptime() returns negative values
authorjbachorik
Sun, 10 Nov 2013 20:05:03 +0100
changeset 21640 6bbdcc430310
parent 21639 39da287ca867
child 21641 c8114b367cf4
6523160: RuntimeMXBean.getUptime() returns negative values Summary: RuntimeMXBean.getUptime() should be based on HR timers rather than on the OS time Reviewed-by: dholmes, sla
jdk/make/java/management/mapfile-vers
jdk/makefiles/mapfiles/libmanagement/mapfile-vers
jdk/src/share/classes/sun/management/RuntimeImpl.java
jdk/src/share/classes/sun/management/VMManagement.java
jdk/src/share/classes/sun/management/VMManagementImpl.java
jdk/src/share/javavm/export/jmm.h
jdk/src/share/native/sun/management/VMManagementImpl.c
jdk/test/java/lang/management/RuntimeMXBean/UpTime.java
--- a/jdk/make/java/management/mapfile-vers	Sat Nov 09 14:30:03 2013 -0500
+++ b/jdk/make/java/management/mapfile-vers	Sun Nov 10 20:05:03 2013 +0100
@@ -103,6 +103,7 @@
 	    Java_sun_management_VMManagementImpl_getSafepointCount;
 	    Java_sun_management_VMManagementImpl_getSafepointSyncTime;
 	    Java_sun_management_VMManagementImpl_getStartupTime;
+	    Java_sun_management_VMManagementImpl_getUptime0;
 	    Java_sun_management_VMManagementImpl_getTotalApplicationNonStoppedTime;
 	    Java_sun_management_VMManagementImpl_getTotalClassCount;
 	    Java_sun_management_VMManagementImpl_getTotalCompileTime;
--- a/jdk/makefiles/mapfiles/libmanagement/mapfile-vers	Sat Nov 09 14:30:03 2013 -0500
+++ b/jdk/makefiles/mapfiles/libmanagement/mapfile-vers	Sun Nov 10 20:05:03 2013 +0100
@@ -103,6 +103,7 @@
 	    Java_sun_management_VMManagementImpl_getSafepointCount;
 	    Java_sun_management_VMManagementImpl_getSafepointSyncTime;
 	    Java_sun_management_VMManagementImpl_getStartupTime;
+	    Java_sun_management_VMManagementImpl_getUptime0;
 	    Java_sun_management_VMManagementImpl_getTotalApplicationNonStoppedTime;
 	    Java_sun_management_VMManagementImpl_getTotalClassCount;
 	    Java_sun_management_VMManagementImpl_getTotalCompileTime;
--- a/jdk/src/share/classes/sun/management/RuntimeImpl.java	Sat Nov 09 14:30:03 2013 -0500
+++ b/jdk/src/share/classes/sun/management/RuntimeImpl.java	Sun Nov 10 20:05:03 2013 +0100
@@ -110,12 +110,7 @@
     }
 
     public long getUptime() {
-        long current = System.currentTimeMillis();
-
-        // TODO: If called from client side when we support
-        // MBean proxy to read performance counters from shared memory,
-        // need to check if the monitored VM exitd.
-        return (current - vmStartupTime);
+        return jvm.getUptime();
     }
 
     public long getStartTime() {
--- a/jdk/src/share/classes/sun/management/VMManagement.java	Sat Nov 09 14:30:03 2013 -0500
+++ b/jdk/src/share/classes/sun/management/VMManagement.java	Sun Nov 10 20:05:03 2013 +0100
@@ -71,6 +71,7 @@
     public String  getBootClassPath();
     public List<String> getVmArguments();
     public long    getStartupTime();
+    public long    getUptime();
     public int     getAvailableProcessors();
 
     // Compilation Subsystem
--- a/jdk/src/share/classes/sun/management/VMManagementImpl.java	Sat Nov 09 14:30:03 2013 -0500
+++ b/jdk/src/share/classes/sun/management/VMManagementImpl.java	Sun Nov 10 20:05:03 2013 +0100
@@ -179,6 +179,10 @@
         return result;
     }
 
+    public long getUptime() {
+        return getUptime0();
+    }
+
     private List<String> vmArgs = null;
     public synchronized List<String> getVmArguments() {
         if (vmArgs == null) {
@@ -192,6 +196,7 @@
     public native String[] getVmArguments0();
 
     public native long getStartupTime();
+    private native long getUptime0();
     public native int getAvailableProcessors();
 
     // Compilation Subsystem
--- a/jdk/src/share/javavm/export/jmm.h	Sat Nov 09 14:30:03 2013 -0500
+++ b/jdk/src/share/javavm/export/jmm.h	Sun Nov 10 20:05:03 2013 +0100
@@ -78,6 +78,7 @@
   JMM_COMPILE_TOTAL_TIME_MS          = 8,    /* Total accumulated time spent in compilation */
   JMM_GC_TIME_MS                     = 9,    /* Total accumulated time spent in collection */
   JMM_GC_COUNT                       = 10,   /* Total number of collections */
+  JMM_JVM_UPTIME_MS                  = 11,   /* The JVM uptime in milliseconds */
 
   JMM_INTERNAL_ATTRIBUTE_INDEX       = 100,
   JMM_CLASS_LOADED_BYTES             = 101,  /* Number of bytes loaded instance classes */
--- a/jdk/src/share/native/sun/management/VMManagementImpl.c	Sat Nov 09 14:30:03 2013 -0500
+++ b/jdk/src/share/native/sun/management/VMManagementImpl.c	Sun Nov 10 20:05:03 2013 +0100
@@ -200,6 +200,13 @@
                                            JMM_JVM_INIT_DONE_TIME_MS);
 }
 
+JNIEXPORT jlong JNICALL
+Java_sun_management_VMManagementImpl_getUptime0
+  (JNIEnv *env, jobject dummy)
+{
+    return jmm_interface->GetLongAttribute(env, NULL, JMM_JVM_UPTIME_MS);
+}
+
 JNIEXPORT jboolean JNICALL
 Java_sun_management_VMManagementImpl_isThreadContentionMonitoringEnabled
   (JNIEnv *env, jobject dummy)
--- a/jdk/test/java/lang/management/RuntimeMXBean/UpTime.java	Sat Nov 09 14:30:03 2013 -0500
+++ b/jdk/test/java/lang/management/RuntimeMXBean/UpTime.java	Sun Nov 10 20:05:03 2013 +0100
@@ -33,30 +33,34 @@
 public class UpTime {
     final static long DELAY = 5; // Seconds
     final static long TIMEOUT = 30; // Minutes
-    private static RuntimeMXBean metrics
+    final static long MULTIPLIER = 1000; // millisecond ticks
+
+    private static final RuntimeMXBean metrics
         = ManagementFactory.getRuntimeMXBean();
 
     public static void main(String argv[]) throws Exception {
         long jvmStartTime = metrics.getStartTime();
-        long systemStartOuter = System.currentTimeMillis();
+        // this will get an aproximate JVM uptime before starting this test
+        long jvmUptime = System.currentTimeMillis() - jvmStartTime;
+        long systemStartOuter = System_milliTime();
         long metricsStart = metrics.getUptime();
-        long systemStartInner = System.currentTimeMillis();
+        long systemStartInner = System_milliTime();
 
         // This JVM might have been running for some time if this test runs
         // in samevm mode.  The sanity check should apply to the test uptime.
-        long testUptime = metricsStart - (systemStartOuter - jvmStartTime);
+        long testUptime = metricsStart - jvmUptime;
 
         // If uptime is more than 30 minutes then it looks like a bug in
         // the method
-        if (testUptime > TIMEOUT * 60 * 1000)
+        if (testUptime > TIMEOUT * 60 * MULTIPLIER)
             throw new RuntimeException("Uptime of the JVM is more than 30 "
                                      + "minutes ("
-                                     + (metricsStart / 60 / 1000)
+                                     + (metricsStart / 60 / MULTIPLIER)
                                      + " minutes).");
 
         // Wait for DELAY seconds
         Object o = new Object();
-        while (System.currentTimeMillis() < systemStartInner + DELAY * 1000) {
+        while (System_milliTime() < systemStartInner + DELAY * MULTIPLIER) {
             synchronized (o) {
                 try {
                     o.wait(DELAY * 1000);
@@ -67,23 +71,27 @@
             }
         }
 
-        long systemEndInner = System.currentTimeMillis();
+        long systemEndInner = System_milliTime();
         long metricsEnd = metrics.getUptime();
-        long systemEndOuter = System.currentTimeMillis();
+        long systemEndOuter = System_milliTime();
 
         long systemDifferenceInner = systemEndInner - systemStartInner;
         long systemDifferenceOuter = systemEndOuter - systemStartOuter;
         long metricsDifference = metricsEnd - metricsStart;
 
         // Check the flow of time in RuntimeMXBean.getUptime(). See the
-        // picture below
-        if (metricsDifference < systemDifferenceInner)
+        // picture below.
+        // The measured times can be off by 1 due to conversions from
+        // nanoseconds to milliseconds, using different channels to read the
+        // HR timer and rounding error. Bigger difference will make the test
+        // fail.
+        if (metricsDifference - systemDifferenceInner < -1)
             throw new RuntimeException("Flow of the time in "
                                      + "RuntimeMXBean.getUptime() ("
                                      + metricsDifference + ") is slower than "
                                      + " in system (" + systemDifferenceInner
                                      + ")");
-        if (metricsDifference > systemDifferenceOuter)
+        if (metricsDifference - systemDifferenceOuter > 1)
             throw new RuntimeException("Flow of the time in "
                                      + "RuntimeMXBean.getUptime() ("
                                      + metricsDifference + ") is faster than "
@@ -92,6 +100,10 @@
 
         System.out.println("Test passed.");
     }
+
+    private static long System_milliTime() {
+        return System.nanoTime() / 1000000; // nanoseconds / milliseconds;
+    }
 }
 
 /*