changeset 4333:28269923b747

7028071: Add two attributes to the OperatingSystemMXBean to provide CPU Load info Summary: Add getProcessCpuLoad() and getSystemCpuLoad() to the OperatingSystemMXBean Reviewed-by: acorn, dholmes, mchung
author fparain
date Fri, 06 May 2011 18:09:33 +0200
parents 631c23c29000
children 0f4a9ce78cf9 edcd8209e0ff
files make/java/management/Makefile make/java/management/mapfile-vers src/share/classes/com/sun/management/OperatingSystemMXBean.java src/solaris/classes/com/sun/management/UnixOperatingSystem.java src/solaris/native/com/sun/management/LinuxOperatingSystem.c src/solaris/native/com/sun/management/SolarisOperatingSystem.c src/windows/classes/com/sun/management/OperatingSystem.java src/windows/native/com/sun/management/OperatingSystem_md.c test/com/sun/management/OperatingSystemMXBean/GetProcessCpuLoad.java test/com/sun/management/OperatingSystemMXBean/GetSystemCpuLoad.java
diffstat 10 files changed, 1541 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/make/java/management/Makefile	Thu May 05 14:02:17 2011 -0700
+++ b/make/java/management/Makefile	Fri May 06 18:09:33 2011 +0200
@@ -63,6 +63,20 @@
 FILES_c     +=  UnixOperatingSystem_md.c
 
 FILES_export +=	com/sun/management/UnixOperatingSystem.java
+
+ifeq ($(PLATFORM),solaris)
+
+FILES_c     += SolarisOperatingSystem.c
+OTHER_LDLIBS += -lkstat
+
+endif # PLATFORM solaris
+
+ifeq ($(PLATFORM),linux)
+
+FILES_c     += LinuxOperatingSystem.c
+
+endif # PLATFORM linux
+
 endif # PLATFORM
 
 #
--- a/make/java/management/mapfile-vers	Thu May 05 14:02:17 2011 -0700
+++ b/make/java/management/mapfile-vers	Fri May 06 18:09:33 2011 +0200
@@ -32,7 +32,9 @@
 	    Java_com_sun_management_UnixOperatingSystem_getFreeSwapSpaceSize;
 	    Java_com_sun_management_UnixOperatingSystem_getMaxFileDescriptorCount;
 	    Java_com_sun_management_UnixOperatingSystem_getOpenFileDescriptorCount;
+	    Java_com_sun_management_UnixOperatingSystem_getProcessCpuLoad;
 	    Java_com_sun_management_UnixOperatingSystem_getProcessCpuTime;
+	    Java_com_sun_management_UnixOperatingSystem_getSystemCpuLoad;
 	    Java_com_sun_management_UnixOperatingSystem_getTotalPhysicalMemorySize;
 	    Java_com_sun_management_UnixOperatingSystem_getTotalSwapSpaceSize;
 	    Java_com_sun_management_UnixOperatingSystem_initialize;
--- a/src/share/classes/com/sun/management/OperatingSystemMXBean.java	Thu May 05 14:02:17 2011 -0700
+++ b/src/share/classes/com/sun/management/OperatingSystemMXBean.java	Fri May 06 18:09:33 2011 +0200
@@ -92,4 +92,39 @@
      * @return the total amount of physical memory in  bytes.
      */
     public long getTotalPhysicalMemorySize();
+
+    /**
+     * Returns the "recent cpu usage" for the whole system. This value is a
+     * double in the [0.0,1.0] interval. A value of 0.0 means that all CPUs
+     * were idle during the recent period of time observed, while a value
+     * of 1.0 means that all CPUs were actively running 100% of the time
+     * during the recent period being observed. All values betweens 0.0 and
+     * 1.0 are possible depending of the activities going on in the system.
+     * If the system recent cpu usage is not available, the method returns a
+     * negative value.
+     *
+     * @return the "recent cpu usage" for the whole system; a negative
+     * value if not available.
+     * @since   1.7
+     */
+    public double getSystemCpuLoad();
+
+    /**
+     * Returns the "recent cpu usage" for the Java Virtual Machine process.
+     * This value is a double in the [0.0,1.0] interval. A value of 0.0 means
+     * that none of the CPUs were running threads from the JVM process during
+     * the recent period of time observed, while a value of 1.0 means that all
+     * CPUs were actively running threads from the JVM 100% of the time
+     * during the recent period being observed. Threads from the JVM include
+     * the application threads as well as the JVM internal threads. All values
+     * betweens 0.0 and 1.0 are possible depending of the activities going on
+     * in the JVM process and the whole system. If the Java Virtual Machine
+     * recent CPU usage is not available, the method returns a negative value.
+     *
+     * @return the "recent cpu usage" for the Java Virtual Machine process;
+     * a negative value if not available.
+     * @since   1.7
+     */
+    public double getProcessCpuLoad();
+
 }
--- a/src/solaris/classes/com/sun/management/UnixOperatingSystem.java	Thu May 05 14:02:17 2011 -0700
+++ b/src/solaris/classes/com/sun/management/UnixOperatingSystem.java	Fri May 06 18:09:33 2011 +0200
@@ -50,6 +50,8 @@
     public native long getTotalPhysicalMemorySize();
     public native long getOpenFileDescriptorCount();
     public native long getMaxFileDescriptorCount();
+    public native double getSystemCpuLoad();
+    public native double getProcessCpuLoad();
 
     static {
         initialize();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/com/sun/management/LinuxOperatingSystem.c	Fri May 06 18:09:33 2011 +0200
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 2011, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <pthread.h>
+#include "com_sun_management_UnixOperatingSystem.h"
+
+struct ticks {
+    uint64_t  used;
+    uint64_t  usedKernel;
+    uint64_t  total;
+};
+
+typedef struct ticks ticks;
+
+typedef enum {
+    CPU_LOAD_VM_ONLY,
+    CPU_LOAD_GLOBAL,
+} CpuLoadTarget;
+
+static struct perfbuf {
+    int   nProcs;
+    ticks jvmTicks;
+    ticks cpuTicks;
+    ticks *cpus;
+} counters;
+
+#define DEC_64 "%lld"
+
+static void next_line(FILE *f) {
+    while (fgetc(f) != '\n');
+}
+
+/**
+ * Return the total number of ticks since the system was booted.
+ * If the usedTicks parameter is not NULL, it will be filled with
+ * the number of ticks spent on actual processes (user, system or
+ * nice processes) since system boot. Note that this is the total number
+ * of "executed" ticks on _all_ CPU:s, that is on a n-way system it is
+ * n times the number of ticks that has passed in clock time.
+ *
+ * Returns a negative value if the reading of the ticks failed.
+ */
+static int get_totalticks(int which, ticks *pticks) {
+    FILE         *fh;
+    uint64_t        userTicks, niceTicks, systemTicks, idleTicks;
+    int             n;
+
+    if((fh = fopen("/proc/stat", "r")) == NULL) {
+        return -1;
+    }
+
+    n = fscanf(fh, "cpu " DEC_64 " " DEC_64 " " DEC_64 " " DEC_64,
+           &userTicks, &niceTicks, &systemTicks, &idleTicks);
+
+    // Move to next line
+    next_line(fh);
+
+    //find the line for requested cpu faster to just iterate linefeeds?
+    if (which != -1) {
+        int i;
+        for (i = 0; i < which; i++) {
+            if (fscanf(fh, "cpu%*d " DEC_64 " " DEC_64 " " DEC_64 " " DEC_64, &userTicks, &niceTicks, &systemTicks, &idleTicks) != 4) {
+                fclose(fh);
+                return -2;
+            }
+            next_line(fh);
+        }
+        n = fscanf(fh, "cpu%*d " DEC_64 " " DEC_64 " " DEC_64 " " DEC_64 "\n",
+           &userTicks, &niceTicks, &systemTicks, &idleTicks);
+    }
+
+    fclose(fh);
+    if (n != 4) {
+        return -2;
+    }
+
+    pticks->used       = userTicks + niceTicks;
+    pticks->usedKernel = systemTicks;
+    pticks->total      = userTicks + niceTicks + systemTicks + idleTicks;
+
+    return 0;
+}
+
+static int vread_statdata(const char *procfile, const char *fmt, va_list args) {
+    FILE    *f;
+    int     n;
+    char     buf[2048];
+
+    if ((f = fopen(procfile, "r")) == NULL) {
+        return -1;
+    }
+
+    if ((n = fread(buf, 1, sizeof(buf), f)) != -1) {
+    char *tmp;
+
+    buf[n-1] = '\0';
+    /** skip through pid and exec name. the exec name _could be wacky_ (renamed) and
+     *  make scanf go mupp.
+     */
+    if ((tmp = strrchr(buf, ')')) != NULL) {
+        // skip the ')' and the following space but check that the buffer is long enough
+        tmp += 2;
+        if (tmp < buf + n) {
+        n = vsscanf(tmp, fmt, args);
+        }
+    }
+    }
+
+    fclose(f);
+
+    return n;
+}
+
+static int read_statdata(const char *procfile, const char *fmt, ...) {
+    int       n;
+    va_list args;
+
+    va_start(args, fmt);
+    n = vread_statdata(procfile, fmt, args);
+    va_end(args);
+    return n;
+}
+
+/** read user and system ticks from a named procfile, assumed to be in 'stat' format then. */
+static int read_ticks(const char *procfile, uint64_t *userTicks, uint64_t *systemTicks) {
+    return read_statdata(procfile, "%*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u "DEC_64" "DEC_64,
+             userTicks, systemTicks
+             );
+}
+
+/**
+ * Return the number of ticks spent in any of the processes belonging
+ * to the JVM on any CPU.
+ */
+static int get_jvmticks(ticks *pticks) {
+    uint64_t userTicks;
+    uint64_t systemTicks;
+
+    if (read_ticks("/proc/self/stat", &userTicks, &systemTicks) < 0) {
+        return -1;
+    }
+
+    // get the total
+    if (get_totalticks(-1, pticks) < 0) {
+        return -1;
+    }
+
+    pticks->used       = userTicks;
+    pticks->usedKernel = systemTicks;
+
+    return 0;
+}
+
+/**
+ * This method must be called first, before any data can be gathererd.
+ */
+int perfInit() {
+    static int initialized=1;
+
+    if (!initialized) {
+        int  i;
+
+        int n = sysconf(_SC_NPROCESSORS_ONLN);
+        if (n <= 0) {
+            n = 1;
+        }
+
+        counters.cpus = calloc(n,sizeof(ticks));
+        if (counters.cpus != NULL)  {
+            // For the CPU load
+            get_totalticks(-1, &counters.cpuTicks);
+
+            for (i = 0; i < n; i++) {
+                get_totalticks(i, &counters.cpus[i]);
+            }
+            // For JVM load
+            get_jvmticks(&counters.jvmTicks);
+            initialized = 1;
+        }
+    }
+
+    return initialized ? 0 : -1;
+}
+
+#define MAX(a,b) (a>b?a:b)
+#define MIN(a,b) (a<b?a:b)
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * Return the load of the CPU as a double. 1.0 means the CPU process uses all
+ * available time for user or system processes, 0.0 means the CPU uses all time
+ * being idle.
+ *
+ * Returns a negative value if there is a problem in determining the CPU load.
+ */
+
+static double get_cpuload_internal(int which, double *pkernelLoad, CpuLoadTarget target) {
+    uint64_t udiff, kdiff, tdiff;
+    ticks *pticks, tmp;
+    double user_load = -1.0;
+    int failed = 0;
+
+    *pkernelLoad = 0.0;
+
+    pthread_mutex_lock(&lock);
+
+    if(perfInit() == 0) {
+
+        if (target == CPU_LOAD_VM_ONLY) {
+            pticks = &counters.jvmTicks;
+        } else if (which == -1) {
+            pticks = &counters.cpuTicks;
+        } else {
+            pticks = &counters.cpus[which];
+        }
+
+        tmp = *pticks;
+
+        if (target == CPU_LOAD_VM_ONLY) {
+            if (get_jvmticks(pticks) != 0) {
+                failed = 1;
+            }
+        } else if (get_totalticks(which, pticks) < 0) {
+            failed = 1;
+        }
+
+        if(!failed) {
+            // seems like we sometimes end up with less kernel ticks when
+            // reading /proc/self/stat a second time, timing issue between cpus?
+            if (pticks->usedKernel < tmp.usedKernel) {
+                kdiff = 0;
+            } else {
+                kdiff = pticks->usedKernel - tmp.usedKernel;
+            }
+            tdiff = pticks->total - tmp.total;
+            udiff = pticks->used - tmp.used;
+
+            if (tdiff == 0) {
+                user_load = 0;
+            } else {
+                if (tdiff < (udiff + kdiff)) {
+                    tdiff = udiff + kdiff;
+                }
+                *pkernelLoad = (kdiff / (double)tdiff);
+                // BUG9044876, normalize return values to sane values
+                *pkernelLoad = MAX(*pkernelLoad, 0.0);
+                *pkernelLoad = MIN(*pkernelLoad, 1.0);
+
+                user_load = (udiff / (double)tdiff);
+                user_load = MAX(user_load, 0.0);
+                user_load = MIN(user_load, 1.0);
+            }
+        }
+    }
+    pthread_mutex_unlock(&lock);
+    return user_load;
+}
+
+double get_cpu_load(int which) {
+    double u, s;
+    u = get_cpuload_internal(which, &s, CPU_LOAD_GLOBAL);
+    if (u < 0) {
+        return -1.0;
+    }
+    // Cap total systemload to 1.0
+    return MIN((u + s), 1.0);
+}
+
+double get_process_load() {
+    double u, s;
+    u = get_cpuload_internal(-1, &s, CPU_LOAD_VM_ONLY);
+    if (u < 0) {
+        return -1.0;
+    }
+    return u + s;
+}
+
+JNIEXPORT jdouble JNICALL
+Java_com_sun_management_UnixOperatingSystem_getSystemCpuLoad
+(JNIEnv *env, jobject dummy)
+{
+    if(perfInit() == 0) {
+        return get_cpu_load(-1);
+    } else {
+        return -1.0;
+    }
+}
+
+JNIEXPORT jdouble JNICALL
+Java_com_sun_management_UnixOperatingSystem_getProcessCpuLoad
+(JNIEnv *env, jobject dummy)
+{
+    if(perfInit() == 0) {
+        return get_process_load();
+    } else {
+        return -1.0;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/com/sun/management/SolarisOperatingSystem.c	Fri May 06 18:09:33 2011 +0200
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2011, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <fcntl.h>
+#include <kstat.h>
+#include <procfs.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/sysinfo.h>
+#include <sys/lwp.h>
+#include <pthread.h>
+#include <utmpx.h>
+#include <dlfcn.h>
+#include <sys/loadavg.h>
+#include <jni.h>
+#include "jvm.h"
+#include "com_sun_management_UnixOperatingSystem.h"
+
+typedef struct {
+    kstat_t *kstat;
+    uint64_t  last_idle;
+    uint64_t  last_total;
+    double  last_ratio;
+} cpuload_t;
+
+static cpuload_t   *cpu_loads = NULL;
+static unsigned int num_cpus;
+static kstat_ctl_t *kstat_ctrl = NULL;
+
+static void map_cpu_kstat_counters() {
+    kstat_t     *kstat;
+    int          i;
+
+    // Get number of CPU(s)
+    if ((num_cpus = sysconf(_SC_NPROCESSORS_ONLN)) == -1) {
+        num_cpus = 1;
+    }
+
+    // Data structure for saving CPU load
+    if ((cpu_loads = calloc(num_cpus,sizeof(cpuload_t))) == NULL) {
+        return;
+    }
+
+    // Get kstat cpu_stat counters for every CPU
+    // (loop over kstat to find our cpu_stat(s)
+    i = 0;
+    for (kstat = kstat_ctrl->kc_chain; kstat != NULL; kstat = kstat->ks_next) {
+        if (strncmp(kstat->ks_module, "cpu_stat", 8) == 0) {
+
+            if (kstat_read(kstat_ctrl, kstat, NULL) == -1) {
+            // Failed to initialize kstat for this CPU so ignore it
+            continue;
+            }
+
+            if (i == num_cpus) {
+            // Found more cpu_stats than reported CPUs
+            break;
+            }
+
+            cpu_loads[i++].kstat = kstat;
+        }
+    }
+}
+
+static int init_cpu_kstat_counters() {
+    static int initialized = 0;
+
+    // Concurrence in this method is prevented by the lock in
+    // the calling method get_cpu_load();
+    if(!initialized) {
+        if ((kstat_ctrl = kstat_open()) != NULL) {
+            map_cpu_kstat_counters();
+            initialized = 1;
+        }
+    }
+    return initialized ? 0 : -1;
+}
+
+static void update_cpu_kstat_counters() {
+    if(kstat_chain_update(kstat_ctrl) != 0) {
+        free(cpu_loads);
+        map_cpu_kstat_counters();
+    }
+}
+
+int read_cpustat(cpuload_t *load, cpu_stat_t *cpu_stat) {
+    if (load->kstat == NULL) {
+        // no handle.
+        return -1;
+    }
+    if (kstat_read(kstat_ctrl, load->kstat, cpu_stat) == -1) {
+        //  disabling for now, a kstat chain update is likely to happen next time
+        load->kstat = NULL;
+        return -1;
+    }
+    return 0;
+}
+
+double get_single_cpu_load(unsigned int n) {
+    cpuload_t  *load;
+    cpu_stat_t  cpu_stat;
+    uint_t     *usage;
+    uint64_t          c_idle;
+    uint64_t          c_total;
+    uint64_t          d_idle;
+    uint64_t          d_total;
+    int           i;
+
+    if (n >= num_cpus) {
+        return -1.0;
+    }
+
+    load = &cpu_loads[n];
+    if (read_cpustat(load, &cpu_stat) < 0) {
+        return -1.0;
+    }
+
+    usage   = cpu_stat.cpu_sysinfo.cpu;
+    c_idle  = usage[CPU_IDLE];
+
+    for (c_total = 0, i = 0; i < CPU_STATES; i++) {
+        c_total += usage[i];
+    }
+
+    // Calculate diff against previous snapshot
+    d_idle  = c_idle - load->last_idle;
+    d_total = c_total - load->last_total;
+
+    /** update if weve moved */
+    if (d_total > 0) {
+        // Save current values for next time around
+        load->last_idle  = c_idle;
+        load->last_total = c_total;
+        load->last_ratio = (double) (d_total - d_idle) / d_total;
+    }
+
+    return load->last_ratio;
+}
+
+int get_info(const char *path, void *info, size_t s, off_t o) {
+    int fd;
+    int ret = 0;
+    if ((fd = open(path, O_RDONLY)) < 0) {
+        return -1;
+    }
+    if (pread(fd, info, s, o) != s) {
+        ret = -1;
+    }
+    close(fd);
+    return ret;
+}
+
+#define MIN(a, b)           ((a < b) ? a : b)
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * Return the cpu load (0-1) for proc number 'which' (or average all if which == -1)
+ */
+double  get_cpu_load(int which) {
+    double load =.0;
+
+    pthread_mutex_lock(&lock);
+    if(init_cpu_kstat_counters()==0) {
+
+        update_cpu_kstat_counters();
+
+        if (which == -1) {
+            unsigned int i;
+            double       t;
+
+            for (t = .0, i = 0; i < num_cpus; i++) {
+                t += get_single_cpu_load(i);
+            }
+
+            // Cap total systemload to 1.0
+            load = MIN((t / num_cpus), 1.0);
+        } else {
+            load = MIN(get_single_cpu_load(which), 1.0);
+        }
+    } else {
+        load = -1.0;
+    }
+    pthread_mutex_unlock(&lock);
+
+    return load;
+}
+
+/**
+ * Return the cpu load (0-1) for the current process (i.e the JVM)
+ * or -1.0 if the get_info() call failed
+ */
+double get_process_load(void) {
+    psinfo_t info;
+
+    // Get the percentage of "recent cpu usage" from all the lwp:s in the JVM:s
+    // process. This is returned as a value between 0.0 and 1.0 multiplied by 0x8000.
+    if (get_info("/proc/self/psinfo",&info.pr_pctcpu, sizeof(info.pr_pctcpu), offsetof(psinfo_t, pr_pctcpu)) == 0) {
+        return (double) info.pr_pctcpu / 0x8000;
+    }
+    return -1.0;
+}
+
+JNIEXPORT jdouble JNICALL
+Java_com_sun_management_UnixOperatingSystem_getSystemCpuLoad
+(JNIEnv *env, jobject dummy)
+{
+    return get_cpu_load(-1);
+}
+
+JNIEXPORT jdouble JNICALL
+Java_com_sun_management_UnixOperatingSystem_getProcessCpuLoad
+(JNIEnv *env, jobject dummy)
+{
+    return get_process_load();
+}
+
--- a/src/windows/classes/com/sun/management/OperatingSystem.java	Thu May 05 14:02:17 2011 -0700
+++ b/src/windows/classes/com/sun/management/OperatingSystem.java	Fri May 06 18:09:33 2011 +0200
@@ -58,6 +58,8 @@
     public native long getProcessCpuTime();
     public native long getFreePhysicalMemorySize();
     public native long getTotalPhysicalMemorySize();
+    public native double getSystemCpuLoad();
+    public native double getProcessCpuLoad();
 
     static {
         initialize();
--- a/src/windows/native/com/sun/management/OperatingSystem_md.c	Thu May 05 14:02:17 2011 -0700
+++ b/src/windows/native/com/sun/management/OperatingSystem_md.c	Fri May 06 18:09:33 2011 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2011, 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
@@ -34,9 +34,27 @@
 #include <errno.h>
 #include <stdlib.h>
 
+#include <malloc.h>
+#pragma warning (push,0)
+#include <windows.h>
+#pragma warning (pop)
+#include <stdio.h>
+#include <time.h>
+#include <stdint.h>
+#include <assert.h>
+
+/* Disable warnings due to broken header files from Microsoft... */
+#pragma warning(push, 3)
+#include <pdh.h>
+#include <pdhmsg.h>
+#include <process.h>
+#pragma warning(pop)
+
 typedef unsigned __int32 juint;
 typedef unsigned __int64 julong;
 
+typedef enum boolean_values { false=0, true=1};
+
 static void set_low(jlong* value, jint low) {
     *value &= (jlong)0xffffffff << 32;
     *value |= (jlong)(julong)(juint)low;
@@ -56,11 +74,14 @@
 
 static HANDLE main_process;
 
+int perfiInit(void);
+
 JNIEXPORT void JNICALL
 Java_com_sun_management_OperatingSystem_initialize
   (JNIEnv *env, jclass cls)
 {
     main_process = GetCurrentProcess();
+     perfiInit();
 }
 
 JNIEXPORT jlong JNICALL
@@ -132,3 +153,788 @@
     GlobalMemoryStatus(&ms);
     return ms.dwTotalPhys;
 }
+
+// Seems WinXP PDH returns PDH_MORE_DATA whenever we send in a NULL buffer.
+// Let's just ignore it, since we make sure we have enough buffer anyway.
+static int
+pdh_fail(PDH_STATUS pdhStat) {
+    return pdhStat != ERROR_SUCCESS && pdhStat != PDH_MORE_DATA;
+}
+
+// INFO: Using PDH APIs Correctly in a Localized Language (Q287159)
+//       http://support.microsoft.com/default.aspx?scid=kb;EN-US;q287159
+// The index value for the base system counters and objects like processor,
+// process, thread, memory, and so forth are always the same irrespective
+// of the localized version of the operating system or service pack installed.
+#define PDH_PROCESSOR_IDX        ((DWORD) 238)
+#define PDH_PROCESSOR_TIME_IDX        ((DWORD)   6)
+#define PDH_PRIV_PROCESSOR_TIME_IDX ((DWORD) 144)
+#define PDH_PROCESS_IDX            ((DWORD) 230)
+#define PDH_ID_PROCESS_IDX        ((DWORD) 784)
+#define PDH_CONTEXT_SWITCH_RATE_IDX ((DWORD) 146)
+#define PDH_SYSTEM_IDX            ((DWORD)   2)
+#define PDH_VIRTUAL_BYTES_IDX        ((DWORD) 174)
+
+typedef PDH_STATUS (WINAPI *PdhAddCounterFunc)(
+                           HQUERY      hQuery,
+                           LPCSTR      szFullCounterPath,
+                           DWORD       dwUserData,
+                           HCOUNTER    *phCounter
+                           );
+typedef PDH_STATUS (WINAPI *PdhOpenQueryFunc)(
+                          LPCWSTR     szDataSource,
+                          DWORD       dwUserData,
+                          HQUERY      *phQuery
+                          );
+typedef DWORD (WINAPI *PdhCloseQueryFunc)(
+                      HQUERY      hQuery
+                      );
+typedef PDH_STATUS (WINAPI *PdhCollectQueryDataFunc)(
+                             HQUERY      hQuery
+                             );
+typedef DWORD (WINAPI *PdhGetFormattedCounterValueFunc)(
+                            HCOUNTER                hCounter,
+                            DWORD                   dwFormat,
+                            LPDWORD                 lpdwType,
+                            PPDH_FMT_COUNTERVALUE   pValue
+                            );
+typedef PDH_STATUS (WINAPI *PdhEnumObjectItemsFunc)(
+                            LPCTSTR    szDataSource,
+                            LPCTSTR    szMachineName,
+                            LPCTSTR    szObjectName,
+                            LPTSTR     mszCounterList,
+                            LPDWORD    pcchCounterListLength,
+                            LPTSTR     mszInstanceList,
+                            LPDWORD    pcchInstanceListLength,
+                            DWORD      dwDetailLevel,
+                            DWORD      dwFlags
+                            );
+typedef PDH_STATUS (WINAPI *PdhRemoveCounterFunc)(
+                          HCOUNTER  hCounter
+                          );
+typedef PDH_STATUS (WINAPI *PdhLookupPerfNameByIndexFunc)(
+                              LPCSTR  szMachineName,
+                              DWORD   dwNameIndex,
+                              LPSTR   szNameBuffer,
+                              LPDWORD pcchNameBufferSize
+                              );
+typedef PDH_STATUS (WINAPI *PdhMakeCounterPathFunc)(
+                            PDH_COUNTER_PATH_ELEMENTS *pCounterPathElements,
+                            LPTSTR szFullPathBuffer,
+                            LPDWORD pcchBufferSize,
+                            DWORD dwFlags
+                            );
+
+static PdhAddCounterFunc PdhAddCounter_i;
+static PdhOpenQueryFunc PdhOpenQuery_i;
+static PdhCloseQueryFunc PdhCloseQuery_i;
+static PdhCollectQueryDataFunc PdhCollectQueryData_i;
+static PdhGetFormattedCounterValueFunc PdhGetFormattedCounterValue_i;
+static PdhEnumObjectItemsFunc PdhEnumObjectItems_i;
+static PdhRemoveCounterFunc PdhRemoveCounter_i;
+static PdhLookupPerfNameByIndexFunc PdhLookupPerfNameByIndex_i;
+static PdhMakeCounterPathFunc PdhMakeCounterPath_i;
+
+static HANDLE thisProcess;
+static double cpuFactor;
+static DWORD  num_cpus;
+
+#define FT2JLONG(X)  ((((jlong)X.dwHighDateTime) << 32) | ((jlong)X.dwLowDateTime))
+#define COUNTER_BUF_SIZE 256
+// Min time between query updates.
+#define MIN_UPDATE_INTERVAL 500
+#define CONFIG_SUCCESSFUL 0
+
+/**
+ * Struct for PDH queries.
+ */
+typedef struct {
+    HQUERY      query;
+    uint64_t      lastUpdate; // Last time query was updated (current millis).
+} UpdateQueryS, *UpdateQueryP;
+
+/**
+ * Struct for the processor load counters.
+ */
+typedef struct {
+    UpdateQueryS      query;
+    HCOUNTER*      counters;
+    int          noOfCounters;
+} MultipleCounterQueryS, *MultipleCounterQueryP;
+
+/**
+ * Struct for the jvm process load counter.
+ */
+typedef struct {
+    UpdateQueryS      query;
+    HCOUNTER      counter;
+} SingleCounterQueryS, *SingleCounterQueryP;
+
+static char* getProcessPDHHeader(void);
+
+/**
+ * Currently available counters.
+ */
+static SingleCounterQueryS cntCtxtSwitchRate;
+static SingleCounterQueryS cntVirtualSize;
+static SingleCounterQueryS cntProcLoad;
+static SingleCounterQueryS cntProcSystemLoad;
+static MultipleCounterQueryS multiCounterCPULoad;
+
+static CRITICAL_SECTION processHeaderLock;
+static CRITICAL_SECTION initializationLock;
+
+/**
+ * Initialize the perf module at startup.
+ */
+int
+perfiInit(void)
+{
+    InitializeCriticalSection(&processHeaderLock);
+    InitializeCriticalSection(&initializationLock);
+    return 0;
+}
+
+/**
+ * Dynamically sets up function pointers to the PDH library.
+ *
+ * @return CONFIG_SUCCESSFUL on success, negative on failure.
+ */
+static int
+get_functions(HMODULE h, char *ebuf, size_t elen) {
+    // The 'A' at the end means the ANSI (not the UNICODE) vesions of the methods
+    PdhAddCounter_i         = (PdhAddCounterFunc)GetProcAddress(h, "PdhAddCounterA");
+    PdhOpenQuery_i         = (PdhOpenQueryFunc)GetProcAddress(h, "PdhOpenQueryA");
+    PdhCloseQuery_i         = (PdhCloseQueryFunc)GetProcAddress(h, "PdhCloseQuery");
+    PdhCollectQueryData_i     = (PdhCollectQueryDataFunc)GetProcAddress(h, "PdhCollectQueryData");
+    PdhGetFormattedCounterValue_i = (PdhGetFormattedCounterValueFunc)GetProcAddress(h, "PdhGetFormattedCounterValue");
+    PdhEnumObjectItems_i         = (PdhEnumObjectItemsFunc)GetProcAddress(h, "PdhEnumObjectItemsA");
+    PdhRemoveCounter_i         = (PdhRemoveCounterFunc)GetProcAddress(h, "PdhRemoveCounter");
+    PdhLookupPerfNameByIndex_i     = (PdhLookupPerfNameByIndexFunc)GetProcAddress(h, "PdhLookupPerfNameByIndexA");
+    PdhMakeCounterPath_i         = (PdhMakeCounterPathFunc)GetProcAddress(h, "PdhMakeCounterPathA");
+
+    if (PdhAddCounter_i == NULL || PdhOpenQuery_i == NULL ||
+    PdhCloseQuery_i == NULL || PdhCollectQueryData_i == NULL ||
+    PdhGetFormattedCounterValue_i == NULL || PdhEnumObjectItems_i == NULL ||
+    PdhRemoveCounter_i == NULL || PdhLookupPerfNameByIndex_i == NULL || PdhMakeCounterPath_i == NULL)
+    {
+        _snprintf(ebuf, elen, "Required method could not be found.");
+        return -1;
+    }
+    return CONFIG_SUCCESSFUL;
+}
+
+/**
+ * Returns the counter value as a double for the specified query.
+ * Will collect the query data and update the counter values as necessary.
+ *
+ * @param query       the query to update (if needed).
+ * @param c          the counter to read.
+ * @param value       where to store the formatted value.
+ * @param format      the format to use (i.e. PDH_FMT_DOUBLE, PDH_FMT_LONG etc)
+ * @return            CONFIG_SUCCESSFUL if no error
+ *                    -1 if PdhCollectQueryData fails
+ *                    -2 if PdhGetFormattedCounterValue fails
+ */
+static int
+getPerformanceData(UpdateQueryP query, HCOUNTER c, PDH_FMT_COUNTERVALUE* value, DWORD format) {
+    clock_t now;
+    now = clock();
+
+    // Need to limit how often we update the query
+    // to mimise the heisenberg effect.
+    // (PDH behaves erratically if the counters are
+    // queried too often, especially counters that
+    // store and use values from two consecutive updates,
+    // like cpu load.)
+    if (now - query->lastUpdate > MIN_UPDATE_INTERVAL) {
+        if (PdhCollectQueryData_i(query->query) != ERROR_SUCCESS) {
+            return -1;
+        }
+        query->lastUpdate = now;
+    }
+
+    if (PdhGetFormattedCounterValue_i(c, format, NULL, value) != ERROR_SUCCESS) {
+        return -2;
+    }
+    return CONFIG_SUCCESSFUL;
+}
+
+/**
+ * Places the resolved counter name of the counter at the specified index in the
+ * supplied buffer. There must be enough space in the buffer to hold the counter name.
+ *
+ * @param index   the counter index as specified in the registry.
+ * @param buf     the buffer in which to place the counter name.
+ * @param size      the size of the counter name buffer.
+ * @param ebuf    the error message buffer.
+ * @param elen    the length of the error buffer.
+ * @return        CONFIG_SUCCESSFUL if successful, negative on failure.
+ */
+static int
+find_name(DWORD index, char *buf, DWORD size) {
+    PDH_STATUS res;
+
+    if ((res = PdhLookupPerfNameByIndex_i(NULL, index, buf, &size)) != ERROR_SUCCESS) {
+
+        /* printf("Could not open counter %d: error=0x%08x", index, res); */
+        /* if (res == PDH_CSTATUS_NO_MACHINE) { */
+        /*      printf("User probably does not have sufficient privileges to use"); */
+        /*      printf("performance counters. If you are running on Windows 2003"); */
+        /*      printf("or Windows Vista, make sure the user is in the"); */
+        /*      printf("Performance Logs user group."); */
+        /* } */
+        return -1;
+    }
+
+    if (size == 0) {
+        /* printf("Failed to get counter name for %d: empty string", index); */
+        return -1;
+    }
+
+    // windows vista does not null-terminate the string (allthough the docs says it will)
+    buf[size - 1] = '\0';
+    return CONFIG_SUCCESSFUL;
+}
+
+/**
+ * Sets up the supplied SingleCounterQuery to listen for the specified counter.
+ * initPDH() must have been run prior to calling this function!
+ *
+ * @param counterQuery   the counter query to set up.
+ * @param counterString  the string specifying the path to the counter.
+ * @param ebuf           the error buffer.
+ * @param elen           the length of the error buffer.
+ * @returns              CONFIG_SUCCESSFUL if successful, negative on failure.
+ */
+static int
+initSingleCounterQuery(SingleCounterQueryP counterQuery, char *counterString) {
+    if (PdhOpenQuery_i(NULL, 0, &counterQuery->query.query) != ERROR_SUCCESS) {
+        /* printf("Could not open query for %s", counterString); */
+        return -1;
+    }
+    if (PdhAddCounter_i(counterQuery->query.query, counterString, 0, &counterQuery->counter) != ERROR_SUCCESS) {
+        /* printf("Could not add counter %s for query", counterString); */
+        if (counterQuery->counter != NULL) {
+            PdhRemoveCounter_i(counterQuery->counter);
+        }
+        if (counterQuery->query.query != NULL) {
+            PdhCloseQuery_i(counterQuery->query.query);
+        }
+        memset(counterQuery, 0, sizeof(SingleCounterQueryS));
+        return -1;
+    }
+    return CONFIG_SUCCESSFUL;
+}
+
+/**
+ * Sets up the supplied SingleCounterQuery to listen for the time spent
+ * by the HotSpot process.
+ *
+ * @param counterQuery   the counter query to set up as a process counter.
+ * @param ebuf           the error buffer.
+ * @param elen           the length of the error buffer.
+ * @returns              CONFIG_SUCCESSFUL if successful, negative on failure.
+ */
+static int
+initProcLoadCounter(void) {
+    char time[COUNTER_BUF_SIZE];
+    char counter[COUNTER_BUF_SIZE*2];
+
+    if (find_name(PDH_PROCESSOR_TIME_IDX, time, sizeof(time)-1) < 0) {
+        return -1;
+    }
+    _snprintf(counter, sizeof(counter)-1, "%s\\%s", getProcessPDHHeader(), time);
+    return initSingleCounterQuery(&cntProcLoad, counter);
+}
+
+static int
+initProcSystemLoadCounter(void) {
+    char time[COUNTER_BUF_SIZE];
+    char counter[COUNTER_BUF_SIZE*2];
+
+    if (find_name(PDH_PRIV_PROCESSOR_TIME_IDX, time, sizeof(time)-1) < 0) {
+        return -1;
+    }
+    _snprintf(counter, sizeof(counter)-1, "%s\\%s", getProcessPDHHeader(), time);
+    return initSingleCounterQuery(&cntProcSystemLoad, counter);
+}
+
+/**
+ * Sets up the supplied MultipleCounterQuery to check on the processors.
+ * (Comment: Refactor and prettify as with the the SingleCounter queries
+ * if more MultipleCounterQueries are discovered.)
+ *
+ * initPDH() must have been run prior to calling this function.
+ *
+ * @param multiQuery  a pointer to a MultipleCounterQueryS, will be filled in with
+ *                    the necessary info to check the PDH processor counters.
+ * @return            CONFIG_SUCCESSFUL if successful, negative on failure.
+ */
+static int
+initProcessorCounters(void) {
+    char          processor[COUNTER_BUF_SIZE]; //'Processor' == #238
+    char          time[COUNTER_BUF_SIZE];      //'Time' == 6
+    DWORD      c_size, i_size;
+    HQUERY     tmpQuery;
+    DWORD      i, p_count;
+    BOOL          error;
+    char         *instances, *tmp;
+    PDH_STATUS pdhStat;
+
+    c_size   = i_size = 0;
+    tmpQuery = NULL;
+    error    = false;
+
+    // This __try / __except stuff is there since Windows 2000 beta (or so) sometimes triggered
+    // an access violation when the user had insufficient privileges to use the performance
+    // counters. This was previously guarded by a very ugly piece of code which disabled the
+    // global trap handling in JRockit. Don't know if this really is needed anymore, but otoh,
+    // if we keep it we don't crash on Win2k beta. /Ihse, 2005-05-30
+    __try {
+        if (find_name(PDH_PROCESSOR_IDX, processor, sizeof(processor)-1) < 0) {
+            return -1;
+        }
+    } __except (EXCEPTION_EXECUTE_HANDLER) { // We'll catch all exceptions here.
+        /* printf("User does not have sufficient privileges to use performance counters"); */
+        return -1;
+    }
+
+    if (find_name(PDH_PROCESSOR_TIME_IDX, time, sizeof(time)-1) < 0) {
+        return -1;
+    }
+    //ok, now we have enough to enumerate all processors.
+    pdhStat = PdhEnumObjectItems_i (
+                    NULL,                   // reserved
+                    NULL,                   // local machine
+                    processor,          // object to enumerate
+                    NULL,              // pass in NULL buffers
+                    &c_size,              // and 0 length to get
+                    NULL,              // required size
+                    &i_size,              // of the buffers in chars
+                    PERF_DETAIL_WIZARD,     // counter detail level
+                    0);
+    if (pdh_fail(pdhStat)) {
+        /* printf("could not enumerate processors (1) error=%d", pdhStat); */
+        return -1;
+    }
+
+    // use calloc because windows vista does not null terminate the instance names (allthough the docs says it will)
+    instances = calloc(i_size, 1);
+    if (instances == NULL) {
+        /* printf("could not allocate memory (1) %d bytes", i_size); */
+        error = true;
+        goto end;
+    }
+
+    c_size  = 0;
+    pdhStat = PdhEnumObjectItems_i (
+                    NULL,                   // reserved
+                    NULL,                   // local machine
+                    processor,              // object to enumerate
+                    NULL,              // pass in NULL buffers
+                    &c_size,              // and 0 length to get
+                    instances,          // required size
+                    &i_size,              // of the buffers in chars
+                    PERF_DETAIL_WIZARD,     // counter detail level
+                    0);
+
+    if (pdh_fail(pdhStat)) {
+        /* printf("could not enumerate processors (2) error=%d", pdhStat); */
+        error = true;
+        goto end;
+    }
+    //count perf count instances.
+    for (p_count = 0, tmp = instances; *tmp != 0; tmp = &tmp[lstrlen(tmp)+1], p_count++);
+
+    //is this correct for HT?
+    assert(p_count == num_cpus+1);
+
+    //ok, have number of perf counters.
+    multiCounterCPULoad.counters = calloc(p_count, sizeof(HCOUNTER));
+    if (multiCounterCPULoad.counters == NULL) {
+        /* printf("could not allocate memory (2) count=%d", p_count); */
+        error = true;
+        goto end;
+    }
+
+    multiCounterCPULoad.noOfCounters = p_count;
+
+    if (PdhOpenQuery_i(NULL, 0, &multiCounterCPULoad.query.query) != ERROR_SUCCESS) {
+        /* printf("could not create query"); */
+        error = true;
+        goto end;
+    }
+    //now, fetch the counters.
+    for (i = 0, tmp = instances; *tmp != '\0'; tmp = &tmp[lstrlen(tmp)+1], i++) {
+    char counter[2*COUNTER_BUF_SIZE];
+
+    _snprintf(counter, sizeof(counter)-1, "\\%s(%s)\\%s", processor, tmp, time);
+
+    if (PdhAddCounter_i(multiCounterCPULoad.query.query, counter, 0, &multiCounterCPULoad.counters[i]) != ERROR_SUCCESS) {
+            /* printf("error adding processor counter %s", counter); */
+            error = true;
+            goto end;
+        }
+    }
+
+    free(instances);
+    instances = NULL;
+
+    // Query once to initialize the counters needing at least two queries
+    // (like the % CPU usage) to calculate correctly.
+    if (PdhCollectQueryData_i(multiCounterCPULoad.query.query) != ERROR_SUCCESS)
+        error = true;
+
+ end:
+    if (instances != NULL) {
+        free(instances);
+    }
+    if (tmpQuery != NULL) {
+        PdhCloseQuery_i(tmpQuery);
+    }
+    if (error) {
+        int i;
+
+        if (multiCounterCPULoad.counters != NULL) {
+            for (i = 0; i < multiCounterCPULoad.noOfCounters; i++) {
+                if (multiCounterCPULoad.counters[i] != NULL) {
+                    PdhRemoveCounter_i(multiCounterCPULoad.counters[i]);
+                }
+            }
+            free(multiCounterCPULoad.counters[i]);
+        }
+        if (multiCounterCPULoad.query.query != NULL) {
+            PdhCloseQuery_i(multiCounterCPULoad.query.query);
+        }
+        memset(&multiCounterCPULoad, 0, sizeof(MultipleCounterQueryS));
+        return -1;
+    }
+    return CONFIG_SUCCESSFUL;
+}
+
+/**
+ * Help function that initializes the PDH process header for the JRockit process.
+ * (You should probably use getProcessPDHHeader() instead!)
+ *
+ * initPDH() must have been run prior to calling this function.
+ *
+ * @param ebuf the error buffer.
+ * @param elen the length of the error buffer.
+ *
+ * @return the PDH instance description corresponding to the JVM process.
+ */
+static char*
+initProcessPDHHeader(void) {
+    static char hotspotheader[2*COUNTER_BUF_SIZE];
+
+    char           counter[2*COUNTER_BUF_SIZE];
+    char           processes[COUNTER_BUF_SIZE];   //'Process' == #230
+    char           pid[COUNTER_BUF_SIZE];           //'ID Process' == 784
+    char           module_name[MAX_PATH];
+    PDH_STATUS  pdhStat;
+    DWORD       c_size = 0, i_size = 0;
+    HQUERY      tmpQuery = NULL;
+    int           i, myPid = _getpid();
+    BOOL           error = false;
+    char          *instances, *tmp, *instance_name, *dot_pos;
+
+    tmpQuery = NULL;
+    myPid    = _getpid();
+    error    = false;
+
+    if (find_name(PDH_PROCESS_IDX, processes, sizeof(processes) - 1) < 0) {
+        return NULL;
+    }
+
+    if (find_name(PDH_ID_PROCESS_IDX, pid, sizeof(pid) - 1) < 0) {
+        return NULL;
+    }
+    //time is same.
+
+    c_size = 0;
+    i_size = 0;
+
+    pdhStat = PdhEnumObjectItems_i (
+                    NULL,                   // reserved
+                    NULL,                   // local machine
+                    processes,              // object to enumerate
+                    NULL,                   // pass in NULL buffers
+                    &c_size,              // and 0 length to get
+                    NULL,              // required size
+                    &i_size,              // of the buffers in chars
+                    PERF_DETAIL_WIZARD,     // counter detail level
+                    0);
+
+    //ok, now we have enough to enumerate all processes
+    if (pdh_fail(pdhStat)) {
+        /* printf("Could not enumerate processes (1) error=%d", pdhStat); */
+        return NULL;
+    }
+
+    // use calloc because windows vista does not null terminate the instance names (allthough the docs says it will)
+    if ((instances = calloc(i_size, 1)) == NULL) {
+        /* printf("Could not allocate memory %d bytes", i_size); */
+        error = true;
+        goto end;
+    }
+
+    c_size = 0;
+
+    pdhStat = PdhEnumObjectItems_i (
+                    NULL,                   // reserved
+                    NULL,                   // local machine
+                    processes,              // object to enumerate
+                    NULL,              // pass in NULL buffers
+                    &c_size,              // and 0 length to get
+                    instances,          // required size
+                    &i_size,              // of the buffers in chars
+                    PERF_DETAIL_WIZARD,     // counter detail level
+                    0);
+
+    // ok, now we have enough to enumerate all processes
+    if (pdh_fail(pdhStat)) {
+        /* printf("Could not enumerate processes (2) error=%d", pdhStat); */
+        error = true;
+        goto end;
+    }
+
+    if (PdhOpenQuery_i(NULL, 0, &tmpQuery) != ERROR_SUCCESS) {
+        /* printf("Could not create temporary query"); */
+        error = true;
+        goto end;
+    }
+
+    // Find our module name and use it to extract the instance name used by PDH
+    if (GetModuleFileName(NULL, module_name, MAX_PATH) >= MAX_PATH-1) {
+        /* printf("Module name truncated"); */
+        error = true;
+        goto end;
+    }
+    instance_name = strrchr(module_name, '\\'); //drop path
+    instance_name++;                            //skip slash
+    dot_pos = strchr(instance_name, '.');       //drop .exe
+    dot_pos[0] = '\0';
+
+    //now, fetch the counters.
+    for (tmp = instances; *tmp != 0 && !error; tmp = &tmp[lstrlen(tmp)+1]) {
+        HCOUNTER  hc = NULL;
+        BOOL done = false;
+
+        // Skip until we find our own process name
+        if (strcmp(tmp, instance_name) != 0) {
+            continue;
+        }
+
+        // iterate over all instance indexes and try to find our own pid
+        for (i = 0; !done && !error; i++){
+            PDH_STATUS res;
+            _snprintf(counter, sizeof(counter)-1, "\\%s(%s#%d)\\%s", processes, tmp, i, pid);
+
+            if (PdhAddCounter_i(tmpQuery, counter, 0, &hc) != ERROR_SUCCESS) {
+                /* printf("Failed to create process id query"); */
+                error = true;
+                goto end;
+            }
+
+            res = PdhCollectQueryData_i(tmpQuery);
+
+            if (res == PDH_INVALID_HANDLE) {
+                /* printf("Failed to query process id"); */
+                res = -1;
+                done = true;
+            } else if (res == PDH_NO_DATA) {
+                done = true;
+            } else {
+                PDH_FMT_COUNTERVALUE cv;
+
+                PdhGetFormattedCounterValue_i(hc, PDH_FMT_LONG, NULL, &cv);
+               /*
+                 * This check seems to be needed for Win2k SMP boxes, since
+                 * they for some reason don't return PDH_NO_DATA for non existing
+                 * counters.
+                 */
+                if (cv.CStatus != PDH_CSTATUS_VALID_DATA) {
+                    done = true;
+                } else if (cv.longValue == myPid) {
+                    _snprintf(hotspotheader, sizeof(hotspotheader)-1, "\\%s(%s#%d)\0", processes, tmp, i);
+                    PdhRemoveCounter_i(hc);
+                    goto end;
+                }
+            }
+            PdhRemoveCounter_i(hc);
+        }
+    }
+ end:
+    if (instances != NULL) {
+        free(instances);
+    }
+    if (tmpQuery != NULL) {
+        PdhCloseQuery_i(tmpQuery);
+    }
+    if (error) {
+        return NULL;
+    }
+    return hotspotheader;
+}
+
+/**
+ * Returns the PDH string prefix identifying the HotSpot process. Use this prefix when getting
+ * counters from the PDH process object representing HotSpot.
+ *
+ * Note: this call may take some time to complete.
+ *
+ * @param ebuf error buffer.
+ * @param elen error buffer length.
+ *
+ * @return the header to be used when retrieving PDH counters from the HotSpot process.
+ * Will return NULL if the call failed.
+ */
+static char *
+getProcessPDHHeader(void) {
+    static char *processHeader = NULL;
+
+    EnterCriticalSection(&processHeaderLock); {
+        if (processHeader == NULL) {
+            processHeader = initProcessPDHHeader();
+        }
+    } LeaveCriticalSection(&processHeaderLock);
+    return processHeader;
+}
+
+int perfInit(void);
+
+double
+perfGetCPULoad(int which)
+{
+    PDH_FMT_COUNTERVALUE cv;
+    HCOUNTER            c;
+
+    if (perfInit() < 0) {
+        // warn?
+        return -1.0;
+    }
+
+    if (multiCounterCPULoad.query.query == NULL) {
+        // warn?
+        return -1.0;
+    }
+
+    if (which == -1) {
+        c = multiCounterCPULoad.counters[multiCounterCPULoad.noOfCounters - 1];
+    } else {
+        if (which < multiCounterCPULoad.noOfCounters) {
+            c = multiCounterCPULoad.counters[which];
+        } else {
+            return -1.0;
+        }
+    }
+    if (getPerformanceData(&multiCounterCPULoad.query, c, &cv, PDH_FMT_DOUBLE ) == CONFIG_SUCCESSFUL) {
+        return cv.doubleValue / 100;
+    }
+    return -1.0;
+}
+
+double
+perfGetProcessLoad(void)
+{
+    PDH_FMT_COUNTERVALUE cv;
+
+    if (perfInit() < 0) {
+        // warn?
+        return -1.0;
+    }
+
+    if (cntProcLoad.query.query == NULL) {
+        // warn?
+        return -1.0;
+    }
+
+    if (getPerformanceData(&cntProcLoad.query, cntProcLoad.counter, &cv, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100) == CONFIG_SUCCESSFUL) {
+        double d = cv.doubleValue / cpuFactor;
+        d = min(1, d);
+        d = max(0, d);
+        return d;
+    }
+    return -1.0;
+}
+
+/**
+ * Helper to initialize the PDH library. Loads the library and sets up the functions.
+ * Note that once loaded, we will never unload the PDH library.
+ *
+ * @return  CONFIG_SUCCESSFUL if successful, negative on failure.
+ */
+int
+perfInit(void) {
+    static HMODULE    h;
+    static BOOL        running, inited;
+
+    int error;
+
+    if (running) {
+        return CONFIG_SUCCESSFUL;
+    }
+
+    error = CONFIG_SUCCESSFUL;
+
+    // this is double checked locking again, but we try to bypass the worst by
+    // implicit membar at end of lock.
+    EnterCriticalSection(&initializationLock); {
+        if (!inited) {
+            char         buf[64] = "";
+            SYSTEM_INFO si;
+
+            // CMH. But windows will not care about our affinity when giving
+            // us measurements. Need the real, raw num cpus.
+
+            GetSystemInfo(&si);
+            num_cpus  = si.dwNumberOfProcessors;
+            // Initialize the denominator for the jvm load calculations
+            cpuFactor = num_cpus * 100;
+
+            /**
+             * Do this dynamically, so we don't fail to start on systems without pdh.
+             */
+            if ((h = LoadLibrary("pdh.dll")) == NULL) {
+                /* printf("Could not load pdh.dll (%d)", GetLastError()); */
+                error = -2;
+            } else if (get_functions(h, buf, sizeof(buf)) < 0) {
+                FreeLibrary(h);
+                h = NULL;
+                error = -2;
+               /* printf("Failed to init pdh functions: %s.\n", buf); */
+            } else {
+                if (initProcessorCounters() != 0) {
+                    /* printf("Failed to init system load counters.\n"); */
+                } else if (initProcLoadCounter() != 0) {
+                    /* printf("Failed to init process load counter.\n"); */
+                } else if (initProcSystemLoadCounter() != 0) {
+                    /* printf("Failed to init process system load counter.\n"); */
+                } else {
+                    inited = true;
+                }
+            }
+        }
+    } LeaveCriticalSection(&initializationLock);
+
+    if (inited && error == CONFIG_SUCCESSFUL) {
+        running = true;
+    }
+
+    return error;
+}
+
+JNIEXPORT jdouble JNICALL
+Java_com_sun_management_OperatingSystem_getSystemCpuLoad
+(JNIEnv *env, jobject dummy)
+{
+    return perfGetCPULoad(-1);
+}
+
+JNIEXPORT jdouble JNICALL
+Java_com_sun_management_OperatingSystem_getProcessCpuLoad
+(JNIEnv *env, jobject dummy)
+{
+    return perfGetProcessLoad();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/com/sun/management/OperatingSystemMXBean/GetProcessCpuLoad.java	Fri May 06 18:09:33 2011 +0200
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug     7028071
+ * @summary Basic unit test of OperatingSystemMXBean.getProcessCpuLoad()
+ *
+ * @run main GetProcessCpuLoad
+ */
+
+import java.lang.management.*;
+import com.sun.management.OperatingSystemMXBean;
+
+public class GetProcessCpuLoad {
+    public static void main(String[] argv) throws Exception {
+        OperatingSystemMXBean mbean = (com.sun.management.OperatingSystemMXBean)
+            ManagementFactory.getOperatingSystemMXBean();
+        double load;
+        for(int i=0; i<10; i++) {
+            load = mbean.getProcessCpuLoad();
+            if((load<0.0 || load>1.0) && load != -1.0) {
+                throw new RuntimeException("getProcessCpuLoad() returns " + load
+                       +   " which is not in the [0.0,1.0] interval");
+            }
+            try {
+                Thread.sleep(200);
+            } catch(InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/com/sun/management/OperatingSystemMXBean/GetSystemCpuLoad.java	Fri May 06 18:09:33 2011 +0200
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug     7028071
+ * @summary Basic unit test of OperatingSystemMXBean.getProcessCpuLoad()
+ *
+ * @run main GetSystemCpuLoad
+ */
+
+import java.lang.management.*;
+import com.sun.management.OperatingSystemMXBean;
+
+public class GetSystemCpuLoad {
+    public static void main(String[] argv) throws Exception {
+        OperatingSystemMXBean mbean = (com.sun.management.OperatingSystemMXBean)
+            ManagementFactory.getOperatingSystemMXBean();
+        double load;
+        for(int i=0; i<10; i++) {
+            load = mbean.getSystemCpuLoad();
+            if((load<0.0 || load>1.0) && load != -1.0) {
+                throw new RuntimeException("getSystemCpuLoad() returns " + load
+                       +  " which is not in the [0.0,1.0] interval");
+            }
+            try {
+                Thread.sleep(200);
+            } catch(InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+}