--- a/jdk/make/java/management/Makefile Wed May 11 14:12:50 2011 -0700
+++ b/jdk/make/java/management/Makefile Wed May 11 18:52:46 2011 -0700
@@ -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/jdk/make/java/management/mapfile-vers Wed May 11 14:12:50 2011 -0700
+++ b/jdk/make/java/management/mapfile-vers Wed May 11 18:52:46 2011 -0700
@@ -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/jdk/src/share/classes/com/sun/management/OperatingSystemMXBean.java Wed May 11 14:12:50 2011 -0700
+++ b/jdk/src/share/classes/com/sun/management/OperatingSystemMXBean.java Wed May 11 18:52:46 2011 -0700
@@ -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/jdk/src/solaris/classes/com/sun/management/UnixOperatingSystem.java Wed May 11 14:12:50 2011 -0700
+++ b/jdk/src/solaris/classes/com/sun/management/UnixOperatingSystem.java Wed May 11 18:52:46 2011 -0700
@@ -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/jdk/src/solaris/native/com/sun/management/LinuxOperatingSystem.c Wed May 11 18:52:46 2011 -0700
@@ -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/jdk/src/solaris/native/com/sun/management/SolarisOperatingSystem.c Wed May 11 18:52:46 2011 -0700
@@ -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/jdk/src/windows/classes/com/sun/management/OperatingSystem.java Wed May 11 14:12:50 2011 -0700
+++ b/jdk/src/windows/classes/com/sun/management/OperatingSystem.java Wed May 11 18:52:46 2011 -0700
@@ -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/jdk/src/windows/native/com/sun/management/OperatingSystem_md.c Wed May 11 14:12:50 2011 -0700
+++ b/jdk/src/windows/native/com/sun/management/OperatingSystem_md.c Wed May 11 18:52:46 2011 -0700
@@ -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/jdk/test/com/sun/management/OperatingSystemMXBean/GetProcessCpuLoad.java Wed May 11 18:52:46 2011 -0700
@@ -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/jdk/test/com/sun/management/OperatingSystemMXBean/GetSystemCpuLoad.java Wed May 11 18:52:46 2011 -0700
@@ -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();
+ }
+ }
+ }
+}