--- old/jdk/src/java.management/windows/native/libmanagement/OperatingSystemImpl.c 2015-04-02 17:38:03.000000000 +0200 +++ /dev/null 2015-04-02 17:38:03.000000000 +0200 @@ -1,1363 +0,0 @@ -/* - * Copyright (c) 2003, 2013, 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 "jni.h" -#include "jni_util.h" -#include "jlong.h" -#include "jvm.h" -#include "management.h" -#include "sun_management_OperatingSystemImpl.h" - -#include -#include -#include - -#include -#pragma warning (push,0) -#include -#pragma warning (pop) -#include -#include -#include -#include - -/* Disable warnings due to broken header files from Microsoft... */ -#pragma warning(push, 3) -#include -#include -#include -#pragma warning(pop) - -typedef unsigned __int32 juint; -typedef unsigned __int64 julong; - -static void set_low(jlong* value, jint low) { - *value &= (jlong)0xffffffff << 32; - *value |= (jlong)(julong)(juint)low; -} - -static void set_high(jlong* value, jint high) { - *value &= (jlong)(julong)(juint)0xffffffff; - *value |= (jlong)high << 32; -} - -static jlong jlong_from(jint h, jint l) { - jlong result = 0; // initialization to avoid warning - set_high(&result, h); - set_low(&result, l); - return result; -} - -static HANDLE main_process; - -static void perfInit(void); - -JNIEXPORT void JNICALL -Java_sun_management_OperatingSystemImpl_initialize0 - (JNIEnv *env, jclass cls) -{ - main_process = GetCurrentProcess(); - perfInit(); -} - -JNIEXPORT jlong JNICALL -Java_sun_management_OperatingSystemImpl_getCommittedVirtualMemorySize0 - (JNIEnv *env, jobject mbean) -{ - PROCESS_MEMORY_COUNTERS pmc; - if (GetProcessMemoryInfo(main_process, &pmc, sizeof(PROCESS_MEMORY_COUNTERS)) == 0) { - return (jlong)-1L; - } else { - return (jlong) pmc.PagefileUsage; - } -} - -JNIEXPORT jlong JNICALL -Java_sun_management_OperatingSystemImpl_getTotalSwapSpaceSize0 - (JNIEnv *env, jobject mbean) -{ - MEMORYSTATUSEX ms; - ms.dwLength = sizeof(ms); - GlobalMemoryStatusEx(&ms); - return (jlong) ms.ullTotalPageFile; -} - -JNIEXPORT jlong JNICALL -Java_sun_management_OperatingSystemImpl_getFreeSwapSpaceSize0 - (JNIEnv *env, jobject mbean) -{ - MEMORYSTATUSEX ms; - ms.dwLength = sizeof(ms); - GlobalMemoryStatusEx(&ms); - return (jlong) ms.ullAvailPageFile; -} - -JNIEXPORT jlong JNICALL -Java_sun_management_OperatingSystemImpl_getProcessCpuTime0 - (JNIEnv *env, jobject mbean) -{ - - FILETIME process_creation_time, process_exit_time, - process_user_time, process_kernel_time; - - // Using static variables declared above - // Units are 100-ns intervals. Convert to ns. - GetProcessTimes(main_process, &process_creation_time, - &process_exit_time, - &process_kernel_time, &process_user_time); - return (jlong_from(process_user_time.dwHighDateTime, - process_user_time.dwLowDateTime) + - jlong_from(process_kernel_time.dwHighDateTime, - process_kernel_time.dwLowDateTime)) * 100; -} - -JNIEXPORT jlong JNICALL -Java_sun_management_OperatingSystemImpl_getFreePhysicalMemorySize0 - (JNIEnv *env, jobject mbean) -{ - MEMORYSTATUSEX ms; - ms.dwLength = sizeof(ms); - GlobalMemoryStatusEx(&ms); - return (jlong) ms.ullAvailPhys; -} - -JNIEXPORT jlong JNICALL -Java_sun_management_OperatingSystemImpl_getTotalPhysicalMemorySize0 - (JNIEnv *env, jobject mbean) -{ - MEMORYSTATUSEX ms; - ms.dwLength = sizeof(ms); - GlobalMemoryStatusEx(&ms); - return (jlong) ms.ullTotalPhys; -} - -/* Performance Data Helper API (PDH) support */ - -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 PDH_STATUS (WINAPI *PdhCollectQueryDataFunc)( - HQUERY hQuery - ); - -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 DWORD (WINAPI *PdhCloseQueryFunc)( - HQUERY hQuery - ); - -typedef DWORD (WINAPI *PdhGetFormattedCounterValueFunc)( - HCOUNTER hCounter, - DWORD dwFormat, - LPDWORD lpdwType, - PPDH_FMT_COUNTERVALUE pValue - ); - -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; - -/* - * Struct for PDH queries. - */ -typedef struct { - HQUERY query; - uint64_t lastUpdate; // Last time query was updated (ticks) -} UpdateQueryS, *UpdateQueryP; - -// Min time between query updates (ticks) -static const int MIN_UPDATE_INTERVAL = 500; - -/* - * Struct for a PDH query with multiple counters. - */ -typedef struct { - UpdateQueryS query; - HCOUNTER* counters; - int noOfCounters; -} MultipleCounterQueryS, *MultipleCounterQueryP; - -/* - * Struct for a PDH query with a single counter. - */ -typedef struct { - UpdateQueryS query; - HCOUNTER counter; -} SingleCounterQueryS, *SingleCounterQueryP; - - -typedef struct { - CRITICAL_SECTION cs; - DWORD owningThread; - DWORD recursionCount; -} PdhCriticalSectionS, *PdhCriticalSectionP; - -static PdhCriticalSectionS initializationLock; - -static void InitializePdhCriticalSection(PdhCriticalSectionP criticalSection) { - assert(criticalSection); - - InitializeCriticalSection(&criticalSection->cs); - criticalSection->owningThread = 0; - criticalSection->recursionCount = 0; -} - -static void EnterPdhCriticalSection(PdhCriticalSectionP criticalSection) { - assert(criticalSection); - - EnterCriticalSection(&criticalSection->cs); - criticalSection->recursionCount++; - if (!criticalSection->owningThread) { - criticalSection->owningThread = GetCurrentThreadId(); - } -} - -static void LeavePdhCriticalSection(PdhCriticalSectionP criticalSection) { - assert(criticalSection); - assert(GetCurrentThreadId() == criticalSection->owningThread); - assert(criticalSection->recursionCount >= 1); - - criticalSection->recursionCount--; - if (!criticalSection->recursionCount) { - criticalSection->owningThread = 0; - } - LeaveCriticalSection(&criticalSection->cs); -} - -/* - * 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. - * To find the correct index for an object or counter, inspect the registry key/value: - * [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009\Counter] - */ -static const DWORD PDH_PROCESSOR_IDX = 238; -static const DWORD PDH_PROCESSOR_TIME_IDX = 6; -static const DWORD PDH_PROCESS_IDX = 230; -static const DWORD PDH_ID_PROCESS_IDX = 784; - -/* useful pdh fmt's */ -static const char* const OBJECT_COUNTER_FMT = "\\%s\\%s"; -static const size_t OBJECT_COUNTER_FMT_LEN = 2; -static const char* const OBJECT_WITH_INSTANCES_COUNTER_FMT = "\\%s(%s)\\%s"; -static const size_t OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN = 4; -static const char* const PROCESS_OBJECT_INSTANCE_COUNTER_FMT = "\\%s(%s#%s)\\%s"; -static const size_t PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN = 5; - -static const char* pdhProcessImageName = NULL; /* "java" */ -static char* pdhIDProcessCounterFmt = NULL; /* "\Process(java#%d)\ID Process" */ - -static int numberOfJavaProcessesAtInitialization = 0; - -/* - * Currently used CPU queries/counters and variables - */ -static SingleCounterQueryP processTotalCPULoad = NULL; -static MultipleCounterQueryP multiCounterCPULoad = NULL; -static double cpuFactor = .0; -static DWORD numCpus = 0; - -/* - * 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 -pdhFail(PDH_STATUS pdhStat) { - return pdhStat != ERROR_SUCCESS && pdhStat != PDH_MORE_DATA; -} - -static const char* -allocateAndCopy(const char* const originalString) { - size_t len; - char* allocatedString; - - assert(originalString); - - len = strlen(originalString); - - allocatedString = malloc(len + 1); - - if (!allocatedString) { - return NULL; - } - - strncpy(allocatedString, originalString, len); - allocatedString[len] = '\0'; - - return allocatedString; -} - -/* - * Allocates memory into the supplied pointer and - * fills it with the localized PDH artifact description, if indexed correctly. - * Caller owns the memory from the point of returning from this function. - * - * @param index the PDH counter index as specified in the registry - * @param ppBuffer pointer to a char*. - * @return 0 if successful, negative on failure. - */ -static int -lookupNameByIndex(DWORD index, char** ppBuffer) { - DWORD size; - - assert(ppBuffer); - - /* determine size needed */ - if (PdhLookupPerfNameByIndex_i(NULL, index, NULL, &size) != PDH_MORE_DATA) { - /* invalid index? */ - return -1; - } - - *ppBuffer = malloc((size_t)size); - - if (!*ppBuffer) { - return -1; - } - - if (PdhLookupPerfNameByIndex_i(NULL, index, *ppBuffer, &size) != ERROR_SUCCESS) { - free(*ppBuffer); - *ppBuffer = NULL; - return -1; - } - - /* windows vista does not null-terminate the string - * (although the docs says it will) */ - (*ppBuffer)[size - 1] = '\0'; - - return 0; -} - -/* -* Construct a fully qualified PDH path -* -* @param objectName a PDH Object string representation (required) -* @param counterName a PDH Counter string representation (required) -* @param imageName a process image name string, ex. "java" (opt) -* @param instance an instance string, ex. "0", "1", ... (opt) -* @return the fully qualified PDH path. -* -* Caller will own the returned malloc:ed string -*/ -static const char* -makeFullCounterPath(const char* const objectName, - const char* const counterName, - const char* const imageName, - const char* const instance) { - - size_t fullCounterPathLen; - char* fullCounterPath; - - assert(objectName); - assert(counterName); - - fullCounterPathLen = strlen(objectName); - fullCounterPathLen += strlen(counterName); - - if (imageName) { - /* - * For paths using the "Process" Object. - * - * Examples: - * abstract: "\Process(imageName#instance)\Counter" - * actual: "\Process(java#2)\ID Process" - */ - fullCounterPathLen += PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN; - fullCounterPathLen += strlen(imageName); - - /* - * imageName must be passed together with an associated - * instance "number" ("0", "1", "2", ...). - * This is required in order to create valid "Process" Object paths. - * - * Examples: "\Process(java#0)", \Process(java#1"), ... - */ - assert(instance); - - fullCounterPathLen += strlen(instance); - - fullCounterPath = malloc(fullCounterPathLen + 1); - - if (!fullCounterPath) { - return NULL; - } - - _snprintf(fullCounterPath, - fullCounterPathLen, - PROCESS_OBJECT_INSTANCE_COUNTER_FMT, - objectName, - imageName, - instance, - counterName); - } else { - if (instance) { - /* - * For paths where the Object has multiple instances. - * - * Examples: - * abstract: "\Object(instance)\Counter" - * actual: "\Processor(0)\% Privileged Time" - */ - fullCounterPathLen += strlen(instance); - fullCounterPathLen += OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN; - } else { - /* - * For "normal" paths. - * - * Examples: - * abstract: "\Object\Counter" - * actual: "\Memory\Available Mbytes" - */ - fullCounterPathLen += OBJECT_COUNTER_FMT_LEN; - } - - fullCounterPath = malloc(fullCounterPathLen + 1); - - if (!fullCounterPath) { - return NULL; - } - - if (instance) { - _snprintf(fullCounterPath, - fullCounterPathLen, - OBJECT_WITH_INSTANCES_COUNTER_FMT, - objectName, - instance, - counterName); - } else { - _snprintf(fullCounterPath, - fullCounterPathLen, - OBJECT_COUNTER_FMT, - objectName, - counterName); - } - } - - fullCounterPath[fullCounterPathLen] = '\0'; - - return fullCounterPath; -} - -/* - * Resolves an index for a PDH artifact to - * a localized, malloc:ed string representation. - * Caller will own the returned malloc:ed string. - * - * @param pdhArtifactIndex PDH index - * @return malloc:ed string representation - * of the requested pdh artifact (localized). - * NULL on failure. - */ -static const char* -getPdhLocalizedArtifact(DWORD pdhArtifactIndex) { - char* pdhLocalizedArtifactString; - - if (lookupNameByIndex(pdhArtifactIndex, - &pdhLocalizedArtifactString) != 0) { - return NULL; - } - - return pdhLocalizedArtifactString; -} - -static void -pdhCleanup(HQUERY* const query, HCOUNTER* const counter) { - if (counter && *counter) { - PdhRemoveCounter_i(*counter); - *counter = NULL; - } - if (query && *query) { - PdhCloseQuery_i(*query); - *query = NULL; - } -} - -static void -destroySingleCounter(SingleCounterQueryP counterQuery) { - if (counterQuery) { - pdhCleanup(&counterQuery->query.query, &counterQuery->counter); - } -} - -static void -destroyMultiCounter(MultipleCounterQueryP multiCounterQuery) { - int i; - if (multiCounterQuery) { - if (multiCounterQuery->counters) { - for (i = 0; i < multiCounterQuery->noOfCounters; i++) { - pdhCleanup(NULL, &multiCounterQuery->counters[i]); - } - free(multiCounterQuery->counters); - multiCounterQuery->counters = NULL; - } - pdhCleanup(&multiCounterQuery->query.query, NULL); - } -} - -static int -openQuery(HQUERY* const query) { - assert(query); - - if (PdhOpenQuery_i(NULL, 0, query) != ERROR_SUCCESS) { - return -1; - } - - return 0; -} - -static int -addCounter(HQUERY query, - const char* const fullCounterPath, - HCOUNTER* const counter) { - - assert(fullCounterPath); - assert(counter); - - if (PdhAddCounter_i(query, - fullCounterPath, - 0, - counter) != ERROR_SUCCESS) { - return -1; - } - - return 0; -} - -/* - * Sets up the supplied SingleCounterQuery to listen for the specified counter. - * - * @param counterQuery the counter query to set up. - * @param fullCounterPath the string specifying the full path to the counter. - * @returns 0 if successful, negative on failure. - */ -static int -initializeSingleCounterQuery(SingleCounterQueryP counterQuery, - const char* const fullCounterPath) { - assert(counterQuery); - assert(fullCounterPath); - - if (openQuery(&counterQuery->query.query) == 0) { - if (addCounter(counterQuery->query.query, - fullCounterPath, - &counterQuery->counter) == 0) { - return 0; - } - } - - return -1; -} - -/* - * Sets up a SingleCounterQuery - * - * param counter the counter query to set up. - * param localizedObject string representing the PDH object to query - * param localizedCounter string representing the PDH counter to query - * param processImageName if the counter query needs the process image name ("java") - * param instance if the counter has instances, this is the instance ("\Processor(0)\") - where 0 is the instance - * param firstSampleOnInit for counters that need two queries to yield their values, - the first query can be issued just after initialization - * - * @returns 0 if successful, negative on failure. - */ -static int -initializeSingleCounter(SingleCounterQueryP const counter, - const char* const localizedObject, - const char* const localizedCounter, - const char* const processImageName, - const char* const instance, - BOOL firstSampleOnInit) { - int retValue = -1; - - const char* fullCounterPath = makeFullCounterPath(localizedObject, - localizedCounter, - processImageName, - instance); - - if (fullCounterPath) { - - assert(counter); - - if (initializeSingleCounterQuery(counter, fullCounterPath) == 0) { - /* - * According to the MSDN documentation, rate counters must be read twice: - * - * "Obtaining the value of rate counters such as Page faults/sec requires that - * PdhCollectQueryData be called twice, with a specific time interval between - * the two calls, before calling PdhGetFormattedCounterValue. Call Sleep to - * implement the waiting period between the two calls to PdhCollectQueryData." - * - * Take the first sample here already to allow for the next (first) "real" sample - * to succeed. - */ - if (firstSampleOnInit) { - PdhCollectQueryData_i(counter->query.query); - } - - retValue = 0; - } - free((char*)fullCounterPath); - } - - return retValue; -} - -static void -perfInit(void) { - InitializePdhCriticalSection(&initializationLock); -} - -static int -getProcessID() { - static int myPid = 0; - if (0 == myPid) { - myPid = _getpid(); - } - return myPid; -} - -/* - * Working against the Process object and it's related counters is inherently problematic - * when using the PDH API: - * - * For PDH, a process is not primarily identified by it's process id, - * but with a sequential number, for example \Process(java#0), \Process(java#1), .... - * The really bad part is that this list is reset as soon as one process exits: - * If \Process(java#1) exits, \Process(java#3) now becomes \Process(java#2) etc. - * - * The PDH query api requires a process identifier to be submitted when registering - * a query, but as soon as the list resets, the query is invalidated (since the name - * changed). - * - * Solution: - * The #number identifier for a Process query can only decrease after process creation. - * - * Therefore we create an array of counter queries for all process object instances - * up to and including ourselves: - * - * Ex. we come in as third process instance (java#2), we then create and register - * queries for the following Process object instances: - * java#0, java#1, java#2 - * - * currentQueryIndexForProcess() keeps track of the current "correct" query - * (in order to keep this index valid when the list resets from underneath, - * ensure to call getCurrentQueryIndexForProcess() before every query involving - * Process object instance data). - */ -static int -currentQueryIndexForProcess(void) { - HQUERY tmpQuery = NULL; - HCOUNTER handleCounter = NULL; - int retValue = -1; - - assert(pdhProcessImageName); - assert(pdhIDProcessCounterFmt); - - if (openQuery(&tmpQuery) == 0) { - int index; - - /* iterate over all instance indexes and try to find our own pid */ - for (index = 0; index < INT_MAX; ++index) { - char fullIDProcessCounterPath[MAX_PATH]; - PDH_FMT_COUNTERVALUE counterValue; - PDH_STATUS res; - - _snprintf(fullIDProcessCounterPath, - MAX_PATH, - pdhIDProcessCounterFmt, - index); - - if (addCounter(tmpQuery, fullIDProcessCounterPath, &handleCounter) != 0) { - break; - } - - res = PdhCollectQueryData_i(tmpQuery); - - if (PDH_INVALID_HANDLE == res || PDH_NO_DATA == res) { - break; - } - - PdhGetFormattedCounterValue_i(handleCounter, - PDH_FMT_LONG, - NULL, - &counterValue); - /* - * 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 (counterValue.CStatus != PDH_CSTATUS_VALID_DATA) { - break; - } - - if ((LONG)getProcessID() == counterValue.longValue) { - retValue = index; - break; - } - } - } - - pdhCleanup(&tmpQuery, &handleCounter); - - return retValue; -} - -/* - * If successful, returns the #index corresponding to our PID - * as resolved by the pdh query: - * "\Process(java#index)\ID Process" (or localized equivalent) - * - * This function should be called before attempting to read - * from any Process related counter(s), and the return value - * is the index to be used for indexing an array of Process object query's: - * - * Example: - * processTotalCPULoad[currentQueryIndex].query - * - * Returns -1 on failure. - */ -static int -getCurrentQueryIndexForProcess() { - int currentQueryIndex = currentQueryIndexForProcess(); - - assert(currentQueryIndex >= 0 && - currentQueryIndex < numberOfJavaProcessesAtInitialization); - - return currentQueryIndex; -} - -/* - * Returns the PDH string identifying the current process image name. - * Use this name as a qualifier when getting counters from the PDH Process Object - * representing your process. - - * Example: - * "\Process(java#0)\Virtual Bytes" - where "java" is the PDH process - * image name. - * - * Please note that the process image name is not necessarily "java", - * hence the use of GetModuleFileName() to detect the process image name. - * - * @return the process image name to be used when retrieving - * PDH counters from the current process. The caller will - own the returned malloc:ed string. NULL if failure. - */ -static const char* -getPdhProcessImageName() { - char moduleName[MAX_PATH]; - char* processImageName; - char* dotPos; - - // Find our module name and use it to extract the image name used by PDH - DWORD getmfnReturn = GetModuleFileName(NULL, moduleName, sizeof(moduleName)); - - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - return NULL; - } - - if (getmfnReturn >= MAX_PATH || 0 == getmfnReturn) { - return NULL; - } - - processImageName = strrchr(moduleName, '\\'); //drop path - processImageName++; //skip slash - dotPos = strrchr(processImageName, '.'); //drop .exe - dotPos[0] = '\0'; - - return allocateAndCopy(processImageName); -} - -/* - * Sets up the supplied MultipleCounterQuery to check on the processors via PDH CPU counters. - * TODO: Refactor and prettify as with the the SingleCounter queries - * if more MultipleCounterQueries are discovered/needed. - * - * @param multiCounterCPULoad a pointer to a MultipleCounterQueryS, will be filled in with - * the necessary info to check the PDH processor counters. - * @return 0 if successful, negative on failure. - */ -static int -initializeMultipleCounterForCPUs(MultipleCounterQueryP multiCounterCPULoad) { - DWORD cSize = 0; - DWORD iSize = 0; - DWORD pCount; - DWORD index; - char* processor = NULL; //'Processor' == PDH_PROCESSOR_IDX - char* time = NULL; //'Time' == PDH_PROCESSOR_TIME_IDX - char* instances = NULL; - char* tmp; - int retValue = -1; - PDH_STATUS pdhStat; - - if (lookupNameByIndex(PDH_PROCESSOR_IDX, &processor) != 0) { - goto end; - } - - if (lookupNameByIndex(PDH_PROCESSOR_TIME_IDX, &time) != 0) { - goto end; - } - - //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 - &cSize, // and 0 length to get - NULL, // required size - &iSize, // of the buffers in chars - PERF_DETAIL_WIZARD, // counter detail level - 0); - - if (pdhFail(pdhStat)) { - goto end; - } - - instances = calloc(iSize, 1); - - if (!instances) { - goto end; - } - - cSize = 0; - - pdhStat = PdhEnumObjectItems_i( - NULL, // reserved - NULL, // local machine - processor, // object to enumerate - NULL, // pass in NULL buffers - &cSize, - instances, // now allocated to be filled in - &iSize, // and size is known - PERF_DETAIL_WIZARD, // counter detail level - 0); - - if (pdhFail(pdhStat)) { - goto end; - } - - // enumerate the Processor instances ("\Processor(0)", "\Processor(1)", ..., "\Processor(_Total)") - for (pCount = 0, tmp = instances; *tmp != '\0'; tmp = &tmp[strlen(tmp)+1], pCount++); - - assert(pCount == numCpus+1); - - //ok, we now have the number of Processor instances - allocate an HCOUNTER for each - multiCounterCPULoad->counters = (HCOUNTER*)malloc(pCount * sizeof(HCOUNTER)); - - if (!multiCounterCPULoad->counters) { - goto end; - } - - multiCounterCPULoad->noOfCounters = pCount; - - if (openQuery(&multiCounterCPULoad->query.query) != 0) { - goto end; - } - - // fetch instance and register its corresponding HCOUNTER with the query - for (index = 0, tmp = instances; *tmp != '\0'; tmp = &tmp[strlen(tmp)+1], ++index) { - const char* const fullCounterPath = makeFullCounterPath(processor, time, NULL, tmp); - - if (!fullCounterPath) { - goto end; - } - - retValue = addCounter(multiCounterCPULoad->query.query, - fullCounterPath, - &multiCounterCPULoad->counters[index]); - - free((char*)fullCounterPath); - - if (retValue != 0) { - goto end; - } - } - - // Query once to initialize the counters which require at least two samples - // (like the % CPU usage) to calculate correctly. - PdhCollectQueryData_i(multiCounterCPULoad->query.query); - - end: - if (processor) { - free(processor); - } - - if (time) { - free(time); - } - - if (instances) { - free(instances); - } - - return retValue; -} - -/* - * Dynamically sets up function pointers to the PDH library. - * - * @param h HMODULE for the PDH library - * @return 0 on success, negative on failure. - */ -static int -bindPdhFunctionPointers(HMODULE h) { - assert(h); - assert(GetCurrentThreadId() == initializationLock.owningThread); - - /* 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"); - - if (!PdhAddCounter_i || !PdhOpenQuery_i || - !PdhCloseQuery_i || !PdhCollectQueryData_i || - !PdhGetFormattedCounterValue_i || !PdhEnumObjectItems_i || - !PdhRemoveCounter_i || !PdhLookupPerfNameByIndex_i) - { - return -1; - } - return 0; -} - -/* - * 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 0 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 = clock(); - - /* - * Need to limit how often we update the query - * to minimize 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 0; -} - -static int -allocateAndInitializePdhConstants() { - const char* pdhLocalizedProcessObject = NULL; - const char* pdhLocalizedIDProcessCounter = NULL; - size_t pdhIDProcessCounterFmtLen; - int currentQueryIndex; - int retValue = -1; - - assert(GetCurrentThreadId() == initializationLock.owningThread); - - assert(!pdhProcessImageName); - pdhProcessImageName = getPdhProcessImageName(); - if (!pdhProcessImageName) { - goto end; - } - - pdhLocalizedProcessObject = getPdhLocalizedArtifact(PDH_PROCESS_IDX); - if (!pdhLocalizedProcessObject) { - goto end; - } - - pdhLocalizedIDProcessCounter = getPdhLocalizedArtifact(PDH_ID_PROCESS_IDX); - if (!pdhLocalizedIDProcessCounter) { - goto end; - } - - assert(!pdhIDProcessCounterFmt); - - pdhIDProcessCounterFmtLen = strlen(pdhProcessImageName); - pdhIDProcessCounterFmtLen += strlen(pdhLocalizedProcessObject); - pdhIDProcessCounterFmtLen += strlen(pdhLocalizedIDProcessCounter); - pdhIDProcessCounterFmtLen += PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN; - pdhIDProcessCounterFmtLen += 2; // "%d" - - assert(pdhIDProcessCounterFmtLen < MAX_PATH); - pdhIDProcessCounterFmt = malloc(pdhIDProcessCounterFmtLen + 1); - if (!pdhIDProcessCounterFmt) { - goto end; - } - - /* "\Process(java#%d)\ID Process" */ - _snprintf(pdhIDProcessCounterFmt, - pdhIDProcessCounterFmtLen, - PROCESS_OBJECT_INSTANCE_COUNTER_FMT, - pdhLocalizedProcessObject, - pdhProcessImageName, - "%d", - pdhLocalizedIDProcessCounter); - - pdhIDProcessCounterFmt[pdhIDProcessCounterFmtLen] = '\0'; - - assert(0 == numberOfJavaProcessesAtInitialization); - currentQueryIndex = currentQueryIndexForProcess(); - if (-1 == currentQueryIndex) { - goto end; - } - - numberOfJavaProcessesAtInitialization = currentQueryIndex + 1; - assert(numberOfJavaProcessesAtInitialization >= 1); - - retValue = 0; - - end: - - if (pdhLocalizedProcessObject) { - free((char*)pdhLocalizedProcessObject); - } - - if (pdhLocalizedIDProcessCounter) { - free((char*)pdhLocalizedIDProcessCounter); - } - - return retValue; -} - -static void -deallocatePdhConstants() { - assert(GetCurrentThreadId() == initializationLock.owningThread); - - if (pdhProcessImageName) { - free((char*)pdhProcessImageName); - pdhProcessImageName = NULL; - } - - if (pdhIDProcessCounterFmt) { - free(pdhIDProcessCounterFmt); - pdhIDProcessCounterFmt = NULL; - } - - numberOfJavaProcessesAtInitialization = 0; -} - -static int -initializeCPUCounters() { - SYSTEM_INFO si; - char* localizedProcessObject; - char* localizedProcessorTimeCounter; - int i; - int retValue = -1; - - assert(GetCurrentThreadId() == initializationLock.owningThread); - - assert(0 == numCpus); - GetSystemInfo(&si); - numCpus = si.dwNumberOfProcessors; - assert(numCpus >= 1); - - /* Initialize the denominator for the jvm load calculations */ - assert(.0 == cpuFactor); - cpuFactor = numCpus * 100; - - if (lookupNameByIndex(PDH_PROCESS_IDX, - &localizedProcessObject) == 0) { - - if (lookupNameByIndex(PDH_PROCESSOR_TIME_IDX, - &localizedProcessorTimeCounter) == 0) { - - assert(processTotalCPULoad); - assert(pdhProcessImageName); - - for (i = 0; i < numberOfJavaProcessesAtInitialization; ++i) { - char instanceIndexBuffer[32]; - retValue = initializeSingleCounter(&processTotalCPULoad[i], - localizedProcessObject, - localizedProcessorTimeCounter, - pdhProcessImageName, - itoa(i, instanceIndexBuffer, 10), - TRUE); - if (retValue != 0) { - break; - } - } - free(localizedProcessorTimeCounter); - } - free(localizedProcessObject); - } - - if (retValue != 0) { - return -1; - } - - assert(multiCounterCPULoad); - return initializeMultipleCounterForCPUs(multiCounterCPULoad); -} - -static void -deallocateCPUCounters() { - int i; - - assert(GetCurrentThreadId() == initializationLock.owningThread); - - if (processTotalCPULoad) { - for (i = 0; i < numberOfJavaProcessesAtInitialization; ++i) { - destroySingleCounter(&processTotalCPULoad[i]); - } - free(processTotalCPULoad); - processTotalCPULoad = NULL; - } - - if (multiCounterCPULoad) { - destroyMultiCounter(multiCounterCPULoad); - free(multiCounterCPULoad); - multiCounterCPULoad = NULL; - } - - cpuFactor = .0; - numCpus = 0; -} - -static void -pdhInitErrorHandler(HMODULE h) { - assert(GetCurrentThreadId() == initializationLock.owningThread); - - deallocatePdhConstants(); - - if (h) { - FreeLibrary(h); - } -} - -/* - * Helper to initialize the PDH library, function pointers and constants. - * - * @return 0 if successful, negative on failure. - */ -static int -pdhInit() { - static BOOL initialized = FALSE; - int retValue; - - if (initialized) { - return 0; - } - - retValue = 0; - - EnterPdhCriticalSection(&initializationLock); { - if (!initialized) { - HMODULE h = NULL; - if ((h = LoadLibrary("pdh.dll")) == NULL) { - retValue = -1; - } else if (bindPdhFunctionPointers(h) < 0) { - retValue = -1; - } else if (allocateAndInitializePdhConstants() < 0) { - retValue = -1; - } - - if (0 == retValue) { - initialized = TRUE; - } else { - pdhInitErrorHandler(h); - } - } - } LeavePdhCriticalSection(&initializationLock); - - return retValue; -} - -static int -allocateCPUCounters() { - assert(GetCurrentThreadId() == initializationLock.owningThread); - assert(numberOfJavaProcessesAtInitialization >= 1); - assert(!processTotalCPULoad); - assert(!multiCounterCPULoad); - - /* - * Create an array of Process object queries, for each instance - * up to and including our own (java#0, java#1, java#2, ...). - */ - processTotalCPULoad = calloc(numberOfJavaProcessesAtInitialization, - sizeof(SingleCounterQueryS)); - - if (!processTotalCPULoad) { - return -1; - } - - multiCounterCPULoad = calloc(1, sizeof(MultipleCounterQueryS)); - - if (!multiCounterCPULoad) { - return -1; - } - - return 0; -} - -static int -initializePdhCPUCounters() { - static BOOL initialized = FALSE; - int retValue; - - if (initialized) { - return 0; - } - - retValue = 0; - - EnterPdhCriticalSection(&initializationLock); { - if (!initialized) { - if (pdhInit() < 0) { - retValue = -1; - } else if (allocateCPUCounters() < 0) { - retValue = -1; - } else if (initializeCPUCounters() < 0) { - retValue = -1; - } - - if (0 == retValue) { - initialized = TRUE; - } else { - deallocateCPUCounters(); - } - } - } LeavePdhCriticalSection(&initializationLock); - - return retValue; -} - -static int -perfCPUInit() { - return initializePdhCPUCounters(); -} - -static double -perfGetProcessCPULoad() { - PDH_FMT_COUNTERVALUE cv; - int currentQueryIndex; - - if (perfCPUInit() < 0) { - // warn? - return -1.0; - } - - currentQueryIndex = getCurrentQueryIndexForProcess(); - - if (getPerformanceData(&processTotalCPULoad[currentQueryIndex].query, - processTotalCPULoad[currentQueryIndex].counter, - &cv, - PDH_FMT_DOUBLE | PDH_FMT_NOCAP100) == 0) { - double d = cv.doubleValue / cpuFactor; - d = min(1, d); - d = max(0, d); - return d; - } - return -1.0; -} - -static double -perfGetCPULoad(int which) { - PDH_FMT_COUNTERVALUE cv; - HCOUNTER c; - - if (perfCPUInit() < 0) { - // warn? - return -1.0; - } - - if (-1 == which) { - 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 ) == 0) { - return cv.doubleValue / 100; - } - return -1.0; -} - -JNIEXPORT jdouble JNICALL -Java_sun_management_OperatingSystemImpl_getSystemCpuLoad0 -(JNIEnv *env, jobject dummy) -{ - return perfGetCPULoad(-1); -} - -JNIEXPORT jdouble JNICALL -Java_sun_management_OperatingSystemImpl_getProcessCpuLoad0 -(JNIEnv *env, jobject dummy) -{ - return perfGetProcessCPULoad(); -} --- /dev/null 2015-04-02 17:38:03.000000000 +0200 +++ new/jdk/src/jdk.management/windows/native/libmanagement_ext/OperatingSystemImpl.c 2015-04-02 17:38:03.000000000 +0200 @@ -0,0 +1,1363 @@ +/* + * Copyright (c) 2003, 2015, 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 "jni.h" +#include "jni_util.h" +#include "jlong.h" +#include "jvm.h" +#include "management_ext.h" +#include "com_sun_management_internal_OperatingSystemImpl.h" + +#include +#include +#include + +#include +#pragma warning (push,0) +#include +#pragma warning (pop) +#include +#include +#include +#include + +/* Disable warnings due to broken header files from Microsoft... */ +#pragma warning(push, 3) +#include +#include +#include +#pragma warning(pop) + +typedef unsigned __int32 juint; +typedef unsigned __int64 julong; + +static void set_low(jlong* value, jint low) { + *value &= (jlong)0xffffffff << 32; + *value |= (jlong)(julong)(juint)low; +} + +static void set_high(jlong* value, jint high) { + *value &= (jlong)(julong)(juint)0xffffffff; + *value |= (jlong)high << 32; +} + +static jlong jlong_from(jint h, jint l) { + jlong result = 0; // initialization to avoid warning + set_high(&result, h); + set_low(&result, l); + return result; +} + +static HANDLE main_process; + +static void perfInit(void); + +JNIEXPORT void JNICALL +Java_com_sun_management_internal_OperatingSystemImpl_initialize0 + (JNIEnv *env, jclass cls) +{ + main_process = GetCurrentProcess(); + perfInit(); +} + +JNIEXPORT jlong JNICALL +Java_com_sun_management_internal_OperatingSystemImpl_getCommittedVirtualMemorySize0 + (JNIEnv *env, jobject mbean) +{ + PROCESS_MEMORY_COUNTERS pmc; + if (GetProcessMemoryInfo(main_process, &pmc, sizeof(PROCESS_MEMORY_COUNTERS)) == 0) { + return (jlong)-1L; + } else { + return (jlong) pmc.PagefileUsage; + } +} + +JNIEXPORT jlong JNICALL +Java_com_sun_management_internal_OperatingSystemImpl_getTotalSwapSpaceSize0 + (JNIEnv *env, jobject mbean) +{ + MEMORYSTATUSEX ms; + ms.dwLength = sizeof(ms); + GlobalMemoryStatusEx(&ms); + return (jlong) ms.ullTotalPageFile; +} + +JNIEXPORT jlong JNICALL +Java_com_sun_management_internal_OperatingSystemImpl_getFreeSwapSpaceSize0 + (JNIEnv *env, jobject mbean) +{ + MEMORYSTATUSEX ms; + ms.dwLength = sizeof(ms); + GlobalMemoryStatusEx(&ms); + return (jlong) ms.ullAvailPageFile; +} + +JNIEXPORT jlong JNICALL +Java_com_sun_management_internal_OperatingSystemImpl_getProcessCpuTime0 + (JNIEnv *env, jobject mbean) +{ + + FILETIME process_creation_time, process_exit_time, + process_user_time, process_kernel_time; + + // Using static variables declared above + // Units are 100-ns intervals. Convert to ns. + GetProcessTimes(main_process, &process_creation_time, + &process_exit_time, + &process_kernel_time, &process_user_time); + return (jlong_from(process_user_time.dwHighDateTime, + process_user_time.dwLowDateTime) + + jlong_from(process_kernel_time.dwHighDateTime, + process_kernel_time.dwLowDateTime)) * 100; +} + +JNIEXPORT jlong JNICALL +Java_com_sun_management_internal_OperatingSystemImpl_getFreePhysicalMemorySize0 + (JNIEnv *env, jobject mbean) +{ + MEMORYSTATUSEX ms; + ms.dwLength = sizeof(ms); + GlobalMemoryStatusEx(&ms); + return (jlong) ms.ullAvailPhys; +} + +JNIEXPORT jlong JNICALL +Java_com_sun_management_internal_OperatingSystemImpl_getTotalPhysicalMemorySize0 + (JNIEnv *env, jobject mbean) +{ + MEMORYSTATUSEX ms; + ms.dwLength = sizeof(ms); + GlobalMemoryStatusEx(&ms); + return (jlong) ms.ullTotalPhys; +} + +/* Performance Data Helper API (PDH) support */ + +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 PDH_STATUS (WINAPI *PdhCollectQueryDataFunc)( + HQUERY hQuery + ); + +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 DWORD (WINAPI *PdhCloseQueryFunc)( + HQUERY hQuery + ); + +typedef DWORD (WINAPI *PdhGetFormattedCounterValueFunc)( + HCOUNTER hCounter, + DWORD dwFormat, + LPDWORD lpdwType, + PPDH_FMT_COUNTERVALUE pValue + ); + +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; + +/* + * Struct for PDH queries. + */ +typedef struct { + HQUERY query; + uint64_t lastUpdate; // Last time query was updated (ticks) +} UpdateQueryS, *UpdateQueryP; + +// Min time between query updates (ticks) +static const int MIN_UPDATE_INTERVAL = 500; + +/* + * Struct for a PDH query with multiple counters. + */ +typedef struct { + UpdateQueryS query; + HCOUNTER* counters; + int noOfCounters; +} MultipleCounterQueryS, *MultipleCounterQueryP; + +/* + * Struct for a PDH query with a single counter. + */ +typedef struct { + UpdateQueryS query; + HCOUNTER counter; +} SingleCounterQueryS, *SingleCounterQueryP; + + +typedef struct { + CRITICAL_SECTION cs; + DWORD owningThread; + DWORD recursionCount; +} PdhCriticalSectionS, *PdhCriticalSectionP; + +static PdhCriticalSectionS initializationLock; + +static void InitializePdhCriticalSection(PdhCriticalSectionP criticalSection) { + assert(criticalSection); + + InitializeCriticalSection(&criticalSection->cs); + criticalSection->owningThread = 0; + criticalSection->recursionCount = 0; +} + +static void EnterPdhCriticalSection(PdhCriticalSectionP criticalSection) { + assert(criticalSection); + + EnterCriticalSection(&criticalSection->cs); + criticalSection->recursionCount++; + if (!criticalSection->owningThread) { + criticalSection->owningThread = GetCurrentThreadId(); + } +} + +static void LeavePdhCriticalSection(PdhCriticalSectionP criticalSection) { + assert(criticalSection); + assert(GetCurrentThreadId() == criticalSection->owningThread); + assert(criticalSection->recursionCount >= 1); + + criticalSection->recursionCount--; + if (!criticalSection->recursionCount) { + criticalSection->owningThread = 0; + } + LeaveCriticalSection(&criticalSection->cs); +} + +/* + * 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. + * To find the correct index for an object or counter, inspect the registry key/value: + * [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009\Counter] + */ +static const DWORD PDH_PROCESSOR_IDX = 238; +static const DWORD PDH_PROCESSOR_TIME_IDX = 6; +static const DWORD PDH_PROCESS_IDX = 230; +static const DWORD PDH_ID_PROCESS_IDX = 784; + +/* useful pdh fmt's */ +static const char* const OBJECT_COUNTER_FMT = "\\%s\\%s"; +static const size_t OBJECT_COUNTER_FMT_LEN = 2; +static const char* const OBJECT_WITH_INSTANCES_COUNTER_FMT = "\\%s(%s)\\%s"; +static const size_t OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN = 4; +static const char* const PROCESS_OBJECT_INSTANCE_COUNTER_FMT = "\\%s(%s#%s)\\%s"; +static const size_t PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN = 5; + +static const char* pdhProcessImageName = NULL; /* "java" */ +static char* pdhIDProcessCounterFmt = NULL; /* "\Process(java#%d)\ID Process" */ + +static int numberOfJavaProcessesAtInitialization = 0; + +/* + * Currently used CPU queries/counters and variables + */ +static SingleCounterQueryP processTotalCPULoad = NULL; +static MultipleCounterQueryP multiCounterCPULoad = NULL; +static double cpuFactor = .0; +static DWORD numCpus = 0; + +/* + * 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 +pdhFail(PDH_STATUS pdhStat) { + return pdhStat != ERROR_SUCCESS && pdhStat != PDH_MORE_DATA; +} + +static const char* +allocateAndCopy(const char* const originalString) { + size_t len; + char* allocatedString; + + assert(originalString); + + len = strlen(originalString); + + allocatedString = malloc(len + 1); + + if (!allocatedString) { + return NULL; + } + + strncpy(allocatedString, originalString, len); + allocatedString[len] = '\0'; + + return allocatedString; +} + +/* + * Allocates memory into the supplied pointer and + * fills it with the localized PDH artifact description, if indexed correctly. + * Caller owns the memory from the point of returning from this function. + * + * @param index the PDH counter index as specified in the registry + * @param ppBuffer pointer to a char*. + * @return 0 if successful, negative on failure. + */ +static int +lookupNameByIndex(DWORD index, char** ppBuffer) { + DWORD size; + + assert(ppBuffer); + + /* determine size needed */ + if (PdhLookupPerfNameByIndex_i(NULL, index, NULL, &size) != PDH_MORE_DATA) { + /* invalid index? */ + return -1; + } + + *ppBuffer = malloc((size_t)size); + + if (!*ppBuffer) { + return -1; + } + + if (PdhLookupPerfNameByIndex_i(NULL, index, *ppBuffer, &size) != ERROR_SUCCESS) { + free(*ppBuffer); + *ppBuffer = NULL; + return -1; + } + + /* windows vista does not null-terminate the string + * (although the docs says it will) */ + (*ppBuffer)[size - 1] = '\0'; + + return 0; +} + +/* +* Construct a fully qualified PDH path +* +* @param objectName a PDH Object string representation (required) +* @param counterName a PDH Counter string representation (required) +* @param imageName a process image name string, ex. "java" (opt) +* @param instance an instance string, ex. "0", "1", ... (opt) +* @return the fully qualified PDH path. +* +* Caller will own the returned malloc:ed string +*/ +static const char* +makeFullCounterPath(const char* const objectName, + const char* const counterName, + const char* const imageName, + const char* const instance) { + + size_t fullCounterPathLen; + char* fullCounterPath; + + assert(objectName); + assert(counterName); + + fullCounterPathLen = strlen(objectName); + fullCounterPathLen += strlen(counterName); + + if (imageName) { + /* + * For paths using the "Process" Object. + * + * Examples: + * abstract: "\Process(imageName#instance)\Counter" + * actual: "\Process(java#2)\ID Process" + */ + fullCounterPathLen += PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN; + fullCounterPathLen += strlen(imageName); + + /* + * imageName must be passed together with an associated + * instance "number" ("0", "1", "2", ...). + * This is required in order to create valid "Process" Object paths. + * + * Examples: "\Process(java#0)", \Process(java#1"), ... + */ + assert(instance); + + fullCounterPathLen += strlen(instance); + + fullCounterPath = malloc(fullCounterPathLen + 1); + + if (!fullCounterPath) { + return NULL; + } + + _snprintf(fullCounterPath, + fullCounterPathLen, + PROCESS_OBJECT_INSTANCE_COUNTER_FMT, + objectName, + imageName, + instance, + counterName); + } else { + if (instance) { + /* + * For paths where the Object has multiple instances. + * + * Examples: + * abstract: "\Object(instance)\Counter" + * actual: "\Processor(0)\% Privileged Time" + */ + fullCounterPathLen += strlen(instance); + fullCounterPathLen += OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN; + } else { + /* + * For "normal" paths. + * + * Examples: + * abstract: "\Object\Counter" + * actual: "\Memory\Available Mbytes" + */ + fullCounterPathLen += OBJECT_COUNTER_FMT_LEN; + } + + fullCounterPath = malloc(fullCounterPathLen + 1); + + if (!fullCounterPath) { + return NULL; + } + + if (instance) { + _snprintf(fullCounterPath, + fullCounterPathLen, + OBJECT_WITH_INSTANCES_COUNTER_FMT, + objectName, + instance, + counterName); + } else { + _snprintf(fullCounterPath, + fullCounterPathLen, + OBJECT_COUNTER_FMT, + objectName, + counterName); + } + } + + fullCounterPath[fullCounterPathLen] = '\0'; + + return fullCounterPath; +} + +/* + * Resolves an index for a PDH artifact to + * a localized, malloc:ed string representation. + * Caller will own the returned malloc:ed string. + * + * @param pdhArtifactIndex PDH index + * @return malloc:ed string representation + * of the requested pdh artifact (localized). + * NULL on failure. + */ +static const char* +getPdhLocalizedArtifact(DWORD pdhArtifactIndex) { + char* pdhLocalizedArtifactString; + + if (lookupNameByIndex(pdhArtifactIndex, + &pdhLocalizedArtifactString) != 0) { + return NULL; + } + + return pdhLocalizedArtifactString; +} + +static void +pdhCleanup(HQUERY* const query, HCOUNTER* const counter) { + if (counter && *counter) { + PdhRemoveCounter_i(*counter); + *counter = NULL; + } + if (query && *query) { + PdhCloseQuery_i(*query); + *query = NULL; + } +} + +static void +destroySingleCounter(SingleCounterQueryP counterQuery) { + if (counterQuery) { + pdhCleanup(&counterQuery->query.query, &counterQuery->counter); + } +} + +static void +destroyMultiCounter(MultipleCounterQueryP multiCounterQuery) { + int i; + if (multiCounterQuery) { + if (multiCounterQuery->counters) { + for (i = 0; i < multiCounterQuery->noOfCounters; i++) { + pdhCleanup(NULL, &multiCounterQuery->counters[i]); + } + free(multiCounterQuery->counters); + multiCounterQuery->counters = NULL; + } + pdhCleanup(&multiCounterQuery->query.query, NULL); + } +} + +static int +openQuery(HQUERY* const query) { + assert(query); + + if (PdhOpenQuery_i(NULL, 0, query) != ERROR_SUCCESS) { + return -1; + } + + return 0; +} + +static int +addCounter(HQUERY query, + const char* const fullCounterPath, + HCOUNTER* const counter) { + + assert(fullCounterPath); + assert(counter); + + if (PdhAddCounter_i(query, + fullCounterPath, + 0, + counter) != ERROR_SUCCESS) { + return -1; + } + + return 0; +} + +/* + * Sets up the supplied SingleCounterQuery to listen for the specified counter. + * + * @param counterQuery the counter query to set up. + * @param fullCounterPath the string specifying the full path to the counter. + * @returns 0 if successful, negative on failure. + */ +static int +initializeSingleCounterQuery(SingleCounterQueryP counterQuery, + const char* const fullCounterPath) { + assert(counterQuery); + assert(fullCounterPath); + + if (openQuery(&counterQuery->query.query) == 0) { + if (addCounter(counterQuery->query.query, + fullCounterPath, + &counterQuery->counter) == 0) { + return 0; + } + } + + return -1; +} + +/* + * Sets up a SingleCounterQuery + * + * param counter the counter query to set up. + * param localizedObject string representing the PDH object to query + * param localizedCounter string representing the PDH counter to query + * param processImageName if the counter query needs the process image name ("java") + * param instance if the counter has instances, this is the instance ("\Processor(0)\") + where 0 is the instance + * param firstSampleOnInit for counters that need two queries to yield their values, + the first query can be issued just after initialization + * + * @returns 0 if successful, negative on failure. + */ +static int +initializeSingleCounter(SingleCounterQueryP const counter, + const char* const localizedObject, + const char* const localizedCounter, + const char* const processImageName, + const char* const instance, + BOOL firstSampleOnInit) { + int retValue = -1; + + const char* fullCounterPath = makeFullCounterPath(localizedObject, + localizedCounter, + processImageName, + instance); + + if (fullCounterPath) { + + assert(counter); + + if (initializeSingleCounterQuery(counter, fullCounterPath) == 0) { + /* + * According to the MSDN documentation, rate counters must be read twice: + * + * "Obtaining the value of rate counters such as Page faults/sec requires that + * PdhCollectQueryData be called twice, with a specific time interval between + * the two calls, before calling PdhGetFormattedCounterValue. Call Sleep to + * implement the waiting period between the two calls to PdhCollectQueryData." + * + * Take the first sample here already to allow for the next (first) "real" sample + * to succeed. + */ + if (firstSampleOnInit) { + PdhCollectQueryData_i(counter->query.query); + } + + retValue = 0; + } + free((char*)fullCounterPath); + } + + return retValue; +} + +static void +perfInit(void) { + InitializePdhCriticalSection(&initializationLock); +} + +static int +getProcessID() { + static int myPid = 0; + if (0 == myPid) { + myPid = _getpid(); + } + return myPid; +} + +/* + * Working against the Process object and it's related counters is inherently problematic + * when using the PDH API: + * + * For PDH, a process is not primarily identified by it's process id, + * but with a sequential number, for example \Process(java#0), \Process(java#1), .... + * The really bad part is that this list is reset as soon as one process exits: + * If \Process(java#1) exits, \Process(java#3) now becomes \Process(java#2) etc. + * + * The PDH query api requires a process identifier to be submitted when registering + * a query, but as soon as the list resets, the query is invalidated (since the name + * changed). + * + * Solution: + * The #number identifier for a Process query can only decrease after process creation. + * + * Therefore we create an array of counter queries for all process object instances + * up to and including ourselves: + * + * Ex. we come in as third process instance (java#2), we then create and register + * queries for the following Process object instances: + * java#0, java#1, java#2 + * + * currentQueryIndexForProcess() keeps track of the current "correct" query + * (in order to keep this index valid when the list resets from underneath, + * ensure to call getCurrentQueryIndexForProcess() before every query involving + * Process object instance data). + */ +static int +currentQueryIndexForProcess(void) { + HQUERY tmpQuery = NULL; + HCOUNTER handleCounter = NULL; + int retValue = -1; + + assert(pdhProcessImageName); + assert(pdhIDProcessCounterFmt); + + if (openQuery(&tmpQuery) == 0) { + int index; + + /* iterate over all instance indexes and try to find our own pid */ + for (index = 0; index < INT_MAX; ++index) { + char fullIDProcessCounterPath[MAX_PATH]; + PDH_FMT_COUNTERVALUE counterValue; + PDH_STATUS res; + + _snprintf(fullIDProcessCounterPath, + MAX_PATH, + pdhIDProcessCounterFmt, + index); + + if (addCounter(tmpQuery, fullIDProcessCounterPath, &handleCounter) != 0) { + break; + } + + res = PdhCollectQueryData_i(tmpQuery); + + if (PDH_INVALID_HANDLE == res || PDH_NO_DATA == res) { + break; + } + + PdhGetFormattedCounterValue_i(handleCounter, + PDH_FMT_LONG, + NULL, + &counterValue); + /* + * 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 (counterValue.CStatus != PDH_CSTATUS_VALID_DATA) { + break; + } + + if ((LONG)getProcessID() == counterValue.longValue) { + retValue = index; + break; + } + } + } + + pdhCleanup(&tmpQuery, &handleCounter); + + return retValue; +} + +/* + * If successful, returns the #index corresponding to our PID + * as resolved by the pdh query: + * "\Process(java#index)\ID Process" (or localized equivalent) + * + * This function should be called before attempting to read + * from any Process related counter(s), and the return value + * is the index to be used for indexing an array of Process object query's: + * + * Example: + * processTotalCPULoad[currentQueryIndex].query + * + * Returns -1 on failure. + */ +static int +getCurrentQueryIndexForProcess() { + int currentQueryIndex = currentQueryIndexForProcess(); + + assert(currentQueryIndex >= 0 && + currentQueryIndex < numberOfJavaProcessesAtInitialization); + + return currentQueryIndex; +} + +/* + * Returns the PDH string identifying the current process image name. + * Use this name as a qualifier when getting counters from the PDH Process Object + * representing your process. + + * Example: + * "\Process(java#0)\Virtual Bytes" - where "java" is the PDH process + * image name. + * + * Please note that the process image name is not necessarily "java", + * hence the use of GetModuleFileName() to detect the process image name. + * + * @return the process image name to be used when retrieving + * PDH counters from the current process. The caller will + own the returned malloc:ed string. NULL if failure. + */ +static const char* +getPdhProcessImageName() { + char moduleName[MAX_PATH]; + char* processImageName; + char* dotPos; + + // Find our module name and use it to extract the image name used by PDH + DWORD getmfnReturn = GetModuleFileName(NULL, moduleName, sizeof(moduleName)); + + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + return NULL; + } + + if (getmfnReturn >= MAX_PATH || 0 == getmfnReturn) { + return NULL; + } + + processImageName = strrchr(moduleName, '\\'); //drop path + processImageName++; //skip slash + dotPos = strrchr(processImageName, '.'); //drop .exe + dotPos[0] = '\0'; + + return allocateAndCopy(processImageName); +} + +/* + * Sets up the supplied MultipleCounterQuery to check on the processors via PDH CPU counters. + * TODO: Refactor and prettify as with the the SingleCounter queries + * if more MultipleCounterQueries are discovered/needed. + * + * @param multiCounterCPULoad a pointer to a MultipleCounterQueryS, will be filled in with + * the necessary info to check the PDH processor counters. + * @return 0 if successful, negative on failure. + */ +static int +initializeMultipleCounterForCPUs(MultipleCounterQueryP multiCounterCPULoad) { + DWORD cSize = 0; + DWORD iSize = 0; + DWORD pCount; + DWORD index; + char* processor = NULL; //'Processor' == PDH_PROCESSOR_IDX + char* time = NULL; //'Time' == PDH_PROCESSOR_TIME_IDX + char* instances = NULL; + char* tmp; + int retValue = -1; + PDH_STATUS pdhStat; + + if (lookupNameByIndex(PDH_PROCESSOR_IDX, &processor) != 0) { + goto end; + } + + if (lookupNameByIndex(PDH_PROCESSOR_TIME_IDX, &time) != 0) { + goto end; + } + + //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 + &cSize, // and 0 length to get + NULL, // required size + &iSize, // of the buffers in chars + PERF_DETAIL_WIZARD, // counter detail level + 0); + + if (pdhFail(pdhStat)) { + goto end; + } + + instances = calloc(iSize, 1); + + if (!instances) { + goto end; + } + + cSize = 0; + + pdhStat = PdhEnumObjectItems_i( + NULL, // reserved + NULL, // local machine + processor, // object to enumerate + NULL, // pass in NULL buffers + &cSize, + instances, // now allocated to be filled in + &iSize, // and size is known + PERF_DETAIL_WIZARD, // counter detail level + 0); + + if (pdhFail(pdhStat)) { + goto end; + } + + // enumerate the Processor instances ("\Processor(0)", "\Processor(1)", ..., "\Processor(_Total)") + for (pCount = 0, tmp = instances; *tmp != '\0'; tmp = &tmp[strlen(tmp)+1], pCount++); + + assert(pCount == numCpus+1); + + //ok, we now have the number of Processor instances - allocate an HCOUNTER for each + multiCounterCPULoad->counters = (HCOUNTER*)malloc(pCount * sizeof(HCOUNTER)); + + if (!multiCounterCPULoad->counters) { + goto end; + } + + multiCounterCPULoad->noOfCounters = pCount; + + if (openQuery(&multiCounterCPULoad->query.query) != 0) { + goto end; + } + + // fetch instance and register its corresponding HCOUNTER with the query + for (index = 0, tmp = instances; *tmp != '\0'; tmp = &tmp[strlen(tmp)+1], ++index) { + const char* const fullCounterPath = makeFullCounterPath(processor, time, NULL, tmp); + + if (!fullCounterPath) { + goto end; + } + + retValue = addCounter(multiCounterCPULoad->query.query, + fullCounterPath, + &multiCounterCPULoad->counters[index]); + + free((char*)fullCounterPath); + + if (retValue != 0) { + goto end; + } + } + + // Query once to initialize the counters which require at least two samples + // (like the % CPU usage) to calculate correctly. + PdhCollectQueryData_i(multiCounterCPULoad->query.query); + + end: + if (processor) { + free(processor); + } + + if (time) { + free(time); + } + + if (instances) { + free(instances); + } + + return retValue; +} + +/* + * Dynamically sets up function pointers to the PDH library. + * + * @param h HMODULE for the PDH library + * @return 0 on success, negative on failure. + */ +static int +bindPdhFunctionPointers(HMODULE h) { + assert(h); + assert(GetCurrentThreadId() == initializationLock.owningThread); + + /* 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"); + + if (!PdhAddCounter_i || !PdhOpenQuery_i || + !PdhCloseQuery_i || !PdhCollectQueryData_i || + !PdhGetFormattedCounterValue_i || !PdhEnumObjectItems_i || + !PdhRemoveCounter_i || !PdhLookupPerfNameByIndex_i) + { + return -1; + } + return 0; +} + +/* + * 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 0 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 = clock(); + + /* + * Need to limit how often we update the query + * to minimize 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 0; +} + +static int +allocateAndInitializePdhConstants() { + const char* pdhLocalizedProcessObject = NULL; + const char* pdhLocalizedIDProcessCounter = NULL; + size_t pdhIDProcessCounterFmtLen; + int currentQueryIndex; + int retValue = -1; + + assert(GetCurrentThreadId() == initializationLock.owningThread); + + assert(!pdhProcessImageName); + pdhProcessImageName = getPdhProcessImageName(); + if (!pdhProcessImageName) { + goto end; + } + + pdhLocalizedProcessObject = getPdhLocalizedArtifact(PDH_PROCESS_IDX); + if (!pdhLocalizedProcessObject) { + goto end; + } + + pdhLocalizedIDProcessCounter = getPdhLocalizedArtifact(PDH_ID_PROCESS_IDX); + if (!pdhLocalizedIDProcessCounter) { + goto end; + } + + assert(!pdhIDProcessCounterFmt); + + pdhIDProcessCounterFmtLen = strlen(pdhProcessImageName); + pdhIDProcessCounterFmtLen += strlen(pdhLocalizedProcessObject); + pdhIDProcessCounterFmtLen += strlen(pdhLocalizedIDProcessCounter); + pdhIDProcessCounterFmtLen += PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN; + pdhIDProcessCounterFmtLen += 2; // "%d" + + assert(pdhIDProcessCounterFmtLen < MAX_PATH); + pdhIDProcessCounterFmt = malloc(pdhIDProcessCounterFmtLen + 1); + if (!pdhIDProcessCounterFmt) { + goto end; + } + + /* "\Process(java#%d)\ID Process" */ + _snprintf(pdhIDProcessCounterFmt, + pdhIDProcessCounterFmtLen, + PROCESS_OBJECT_INSTANCE_COUNTER_FMT, + pdhLocalizedProcessObject, + pdhProcessImageName, + "%d", + pdhLocalizedIDProcessCounter); + + pdhIDProcessCounterFmt[pdhIDProcessCounterFmtLen] = '\0'; + + assert(0 == numberOfJavaProcessesAtInitialization); + currentQueryIndex = currentQueryIndexForProcess(); + if (-1 == currentQueryIndex) { + goto end; + } + + numberOfJavaProcessesAtInitialization = currentQueryIndex + 1; + assert(numberOfJavaProcessesAtInitialization >= 1); + + retValue = 0; + + end: + + if (pdhLocalizedProcessObject) { + free((char*)pdhLocalizedProcessObject); + } + + if (pdhLocalizedIDProcessCounter) { + free((char*)pdhLocalizedIDProcessCounter); + } + + return retValue; +} + +static void +deallocatePdhConstants() { + assert(GetCurrentThreadId() == initializationLock.owningThread); + + if (pdhProcessImageName) { + free((char*)pdhProcessImageName); + pdhProcessImageName = NULL; + } + + if (pdhIDProcessCounterFmt) { + free(pdhIDProcessCounterFmt); + pdhIDProcessCounterFmt = NULL; + } + + numberOfJavaProcessesAtInitialization = 0; +} + +static int +initializeCPUCounters() { + SYSTEM_INFO si; + char* localizedProcessObject; + char* localizedProcessorTimeCounter; + int i; + int retValue = -1; + + assert(GetCurrentThreadId() == initializationLock.owningThread); + + assert(0 == numCpus); + GetSystemInfo(&si); + numCpus = si.dwNumberOfProcessors; + assert(numCpus >= 1); + + /* Initialize the denominator for the jvm load calculations */ + assert(.0 == cpuFactor); + cpuFactor = numCpus * 100; + + if (lookupNameByIndex(PDH_PROCESS_IDX, + &localizedProcessObject) == 0) { + + if (lookupNameByIndex(PDH_PROCESSOR_TIME_IDX, + &localizedProcessorTimeCounter) == 0) { + + assert(processTotalCPULoad); + assert(pdhProcessImageName); + + for (i = 0; i < numberOfJavaProcessesAtInitialization; ++i) { + char instanceIndexBuffer[32]; + retValue = initializeSingleCounter(&processTotalCPULoad[i], + localizedProcessObject, + localizedProcessorTimeCounter, + pdhProcessImageName, + itoa(i, instanceIndexBuffer, 10), + TRUE); + if (retValue != 0) { + break; + } + } + free(localizedProcessorTimeCounter); + } + free(localizedProcessObject); + } + + if (retValue != 0) { + return -1; + } + + assert(multiCounterCPULoad); + return initializeMultipleCounterForCPUs(multiCounterCPULoad); +} + +static void +deallocateCPUCounters() { + int i; + + assert(GetCurrentThreadId() == initializationLock.owningThread); + + if (processTotalCPULoad) { + for (i = 0; i < numberOfJavaProcessesAtInitialization; ++i) { + destroySingleCounter(&processTotalCPULoad[i]); + } + free(processTotalCPULoad); + processTotalCPULoad = NULL; + } + + if (multiCounterCPULoad) { + destroyMultiCounter(multiCounterCPULoad); + free(multiCounterCPULoad); + multiCounterCPULoad = NULL; + } + + cpuFactor = .0; + numCpus = 0; +} + +static void +pdhInitErrorHandler(HMODULE h) { + assert(GetCurrentThreadId() == initializationLock.owningThread); + + deallocatePdhConstants(); + + if (h) { + FreeLibrary(h); + } +} + +/* + * Helper to initialize the PDH library, function pointers and constants. + * + * @return 0 if successful, negative on failure. + */ +static int +pdhInit() { + static BOOL initialized = FALSE; + int retValue; + + if (initialized) { + return 0; + } + + retValue = 0; + + EnterPdhCriticalSection(&initializationLock); { + if (!initialized) { + HMODULE h = NULL; + if ((h = LoadLibrary("pdh.dll")) == NULL) { + retValue = -1; + } else if (bindPdhFunctionPointers(h) < 0) { + retValue = -1; + } else if (allocateAndInitializePdhConstants() < 0) { + retValue = -1; + } + + if (0 == retValue) { + initialized = TRUE; + } else { + pdhInitErrorHandler(h); + } + } + } LeavePdhCriticalSection(&initializationLock); + + return retValue; +} + +static int +allocateCPUCounters() { + assert(GetCurrentThreadId() == initializationLock.owningThread); + assert(numberOfJavaProcessesAtInitialization >= 1); + assert(!processTotalCPULoad); + assert(!multiCounterCPULoad); + + /* + * Create an array of Process object queries, for each instance + * up to and including our own (java#0, java#1, java#2, ...). + */ + processTotalCPULoad = calloc(numberOfJavaProcessesAtInitialization, + sizeof(SingleCounterQueryS)); + + if (!processTotalCPULoad) { + return -1; + } + + multiCounterCPULoad = calloc(1, sizeof(MultipleCounterQueryS)); + + if (!multiCounterCPULoad) { + return -1; + } + + return 0; +} + +static int +initializePdhCPUCounters() { + static BOOL initialized = FALSE; + int retValue; + + if (initialized) { + return 0; + } + + retValue = 0; + + EnterPdhCriticalSection(&initializationLock); { + if (!initialized) { + if (pdhInit() < 0) { + retValue = -1; + } else if (allocateCPUCounters() < 0) { + retValue = -1; + } else if (initializeCPUCounters() < 0) { + retValue = -1; + } + + if (0 == retValue) { + initialized = TRUE; + } else { + deallocateCPUCounters(); + } + } + } LeavePdhCriticalSection(&initializationLock); + + return retValue; +} + +static int +perfCPUInit() { + return initializePdhCPUCounters(); +} + +static double +perfGetProcessCPULoad() { + PDH_FMT_COUNTERVALUE cv; + int currentQueryIndex; + + if (perfCPUInit() < 0) { + // warn? + return -1.0; + } + + currentQueryIndex = getCurrentQueryIndexForProcess(); + + if (getPerformanceData(&processTotalCPULoad[currentQueryIndex].query, + processTotalCPULoad[currentQueryIndex].counter, + &cv, + PDH_FMT_DOUBLE | PDH_FMT_NOCAP100) == 0) { + double d = cv.doubleValue / cpuFactor; + d = min(1, d); + d = max(0, d); + return d; + } + return -1.0; +} + +static double +perfGetCPULoad(int which) { + PDH_FMT_COUNTERVALUE cv; + HCOUNTER c; + + if (perfCPUInit() < 0) { + // warn? + return -1.0; + } + + if (-1 == which) { + 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 ) == 0) { + return cv.doubleValue / 100; + } + return -1.0; +} + +JNIEXPORT jdouble JNICALL +Java_com_sun_management_internal_OperatingSystemImpl_getSystemCpuLoad0 +(JNIEnv *env, jobject dummy) +{ + return perfGetCPULoad(-1); +} + +JNIEXPORT jdouble JNICALL +Java_com_sun_management_internal_OperatingSystemImpl_getProcessCpuLoad0 +(JNIEnv *env, jobject dummy) +{ + return perfGetProcessCPULoad(); +}