# HG changeset patch # User mgronlun # Date 1415391951 -3600 # Node ID 0a4ed597f2291c5138a79d18602fe6da32265368 # Parent ffc348308de2e872f5d510d440604c3726a67a18 8056049: getProcessCpuLoad() stops working in one process when a different process exits Reviewed-by: ctornqvi diff -r ffc348308de2 -r 0a4ed597f229 src/windows/native/sun/management/OperatingSystemImpl.c --- a/src/windows/native/sun/management/OperatingSystemImpl.c Fri Nov 07 09:22:58 2014 -0800 +++ b/src/windows/native/sun/management/OperatingSystemImpl.c Fri Nov 07 21:25:51 2014 +0100 @@ -53,8 +53,6 @@ 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; @@ -66,22 +64,22 @@ } 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; + jlong result = 0; // initialization to avoid warning + set_high(&result, h); + set_low(&result, l); + return result; } static HANDLE main_process; -int perfiInit(void); +static void perfInit(void); JNIEXPORT void JNICALL Java_sun_management_OperatingSystemImpl_initialize (JNIEnv *env, jclass cls) { main_process = GetCurrentProcess(); - perfiInit(); + perfInit(); } JNIEXPORT jlong JNICALL @@ -155,26 +153,7 @@ return (jlong) ms.ullTotalPhys; } -// 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) +/* Performance Data Helper API (PDH) support */ typedef PDH_STATUS (WINAPI *PdhAddCounterFunc)( HQUERY hQuery, @@ -183,48 +162,44 @@ HCOUNTER *phCounter ); typedef PDH_STATUS (WINAPI *PdhOpenQueryFunc)( - LPCWSTR szDataSource, - DWORD dwUserData, - HQUERY *phQuery - ); + 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 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 - ); + HCOUNTER hCounter, + DWORD dwFormat, + LPDWORD lpdwType, + PPDH_FMT_COUNTERVALUE pValue + ); static PdhAddCounterFunc PdhAddCounter_i; static PdhOpenQueryFunc PdhOpenQuery_i; @@ -234,76 +209,757 @@ 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). + uint64_t lastUpdate; // Last time query was updated (ticks) } UpdateQueryS, *UpdateQueryP; -/** - * Struct for the processor load counters. +// 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; + UpdateQueryS query; + HCOUNTER* counters; + int noOfCounters; } MultipleCounterQueryS, *MultipleCounterQueryP; -/** - * Struct for the jvm process load counter. +/* + * Struct for a PDH query with a single counter. */ typedef struct { - UpdateQueryS query; + UpdateQueryS query; HCOUNTER counter; } SingleCounterQueryS, *SingleCounterQueryP; -static char* getProcessPDHHeader(void); + +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); +} -/** - * Currently available counters. +/* + * 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 SingleCounterQueryS cntCtxtSwitchRate; -static SingleCounterQueryS cntVirtualSize; -static SingleCounterQueryS cntProcLoad; -static SingleCounterQueryS cntProcSystemLoad; -static MultipleCounterQueryS multiCounterCPULoad; +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); -static CRITICAL_SECTION processHeaderLock; -static CRITICAL_SECTION initializationLock; + 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; + } -/** - * Initialize the perf module at startup. + _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. */ -int -perfiInit(void) -{ - InitializeCriticalSection(&processHeaderLock); - InitializeCriticalSection(&initializationLock); +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. * - * @return CONFIG_SUCCESSFUL on success, negative on failure. + * @param h HMODULE for the PDH library + * @return 0 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 +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"); @@ -312,42 +968,41 @@ 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) + if (!PdhAddCounter_i || !PdhOpenQuery_i || + !PdhCloseQuery_i || !PdhCollectQueryData_i || + !PdhGetFormattedCounterValue_i || !PdhEnumObjectItems_i || + !PdhRemoveCounter_i || !PdhLookupPerfNameByIndex_i) { - _snprintf(ebuf, elen, "Required method could not be found."); return -1; } - return CONFIG_SUCCESSFUL; + 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 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 + * @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; - now = clock(); + clock_t 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.) + /* + * 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; @@ -358,500 +1013,308 @@ 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); + return 0; } 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); -} +allocateAndInitializePdhConstants() { + const char* pdhLocalizedProcessObject = NULL; + const char* pdhLocalizedIDProcessCounter = NULL; + size_t pdhIDProcessCounterFmtLen; + int currentQueryIndex; + int retValue = -1; -/** - * 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; + assert(GetCurrentThreadId() == initializationLock.owningThread); - 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; + assert(!pdhProcessImageName); + pdhProcessImageName = getPdhProcessImageName(); + if (!pdhProcessImageName) { + goto end; } - 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; + pdhLocalizedProcessObject = getPdhLocalizedArtifact(PDH_PROCESS_IDX); + if (!pdhLocalizedProcessObject) { + goto end; } - // 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; + pdhLocalizedIDProcessCounter = getPdhLocalizedArtifact(PDH_ID_PROCESS_IDX); + if (!pdhLocalizedIDProcessCounter) { 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); + assert(!pdhIDProcessCounterFmt); - if (pdh_fail(pdhStat)) { - /* printf("could not enumerate processors (2) error=%d", pdhStat); */ - error = true; + 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; } - //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; + /* "\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; } - multiCounterCPULoad.noOfCounters = p_count; + numberOfJavaProcessesAtInitialization = currentQueryIndex + 1; + assert(numberOfJavaProcessesAtInitialization >= 1); + + retValue = 0; - if (PdhOpenQuery_i(NULL, 0, &multiCounterCPULoad.query.query) != ERROR_SUCCESS) { - /* printf("could not create query"); */ - error = true; - goto end; + end: + + if (pdhLocalizedProcessObject) { + free((char*)pdhLocalizedProcessObject); } - //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; - } + if (pdhLocalizedIDProcessCounter) { + free((char*)pdhLocalizedIDProcessCounter); } - free(instances); - instances = NULL; + return retValue; +} - // 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; +static void +deallocatePdhConstants() { + assert(GetCurrentThreadId() == initializationLock.owningThread); - end: - if (instances != NULL) { - free(instances); - } - if (tmpQuery != NULL) { - PdhCloseQuery_i(tmpQuery); + if (pdhProcessImageName) { + free((char*)pdhProcessImageName); + pdhProcessImageName = NULL; } - 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; + if (pdhIDProcessCounterFmt) { + free(pdhIDProcessCounterFmt); + pdhIDProcessCounterFmt = NULL; } - return CONFIG_SUCCESSFUL; + + numberOfJavaProcessesAtInitialization = 0; } -/** - * 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]; +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) { - 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; + if (lookupNameByIndex(PDH_PROCESSOR_TIME_IDX, + &localizedProcessorTimeCounter) == 0) { + + assert(processTotalCPULoad); + assert(pdhProcessImageName); - tmpQuery = NULL; - myPid = _getpid(); - error = false; - - if (find_name(PDH_PROCESS_IDX, processes, sizeof(processes) - 1) < 0) { - return NULL; + 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 (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; + if (retValue != 0) { + return -1; } - // 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; + 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; } - c_size = 0; + cpuFactor = .0; + numCpus = 0; +} + +static void +pdhInitErrorHandler(HMODULE h) { + assert(GetCurrentThreadId() == initializationLock.owningThread); + + deallocatePdhConstants(); - 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); + if (h) { + FreeLibrary(h); + } +} - // 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; - } +/* + * 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 (PdhOpenQuery_i(NULL, 0, &tmpQuery) != ERROR_SUCCESS) { - /* printf("Could not create temporary query"); */ - error = true; - goto end; + if (initialized) { + return 0; } - // 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'; + retValue = 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; + 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; } - res = PdhCollectQueryData_i(tmpQuery); + if (0 == retValue) { + initialized = TRUE; + } else { + pdhInitErrorHandler(h); + } + } + } LeavePdhCriticalSection(&initializationLock); - 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; + return retValue; +} + +static int +allocateCPUCounters() { + assert(GetCurrentThreadId() == initializationLock.owningThread); + assert(numberOfJavaProcessesAtInitialization >= 1); + assert(!processTotalCPULoad); + assert(!multiCounterCPULoad); - 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); - } + /* + * 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; } - end: - if (instances != NULL) { - free(instances); + + multiCounterCPULoad = calloc(1, sizeof(MultipleCounterQueryS)); + + if (!multiCounterCPULoad) { + return -1; } - if (tmpQuery != NULL) { - PdhCloseQuery_i(tmpQuery); - } - if (error) { - return NULL; - } - return hotspotheader; + + return 0; } -/** - * 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; +static int +initializePdhCPUCounters() { + static BOOL initialized = FALSE; + int retValue; + + if (initialized) { + return 0; + } + + retValue = 0; - EnterCriticalSection(&processHeaderLock); { - if (processHeader == NULL) { - processHeader = initProcessPDHHeader(); + 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(); + } } - } LeaveCriticalSection(&processHeaderLock); - return processHeader; + } LeavePdhCriticalSection(&initializationLock); + + return retValue; } -int perfInit(void); +static int +perfCPUInit() { + return initializePdhCPUCounters(); +} -double -perfGetCPULoad(int which) -{ +static double +perfGetProcessCPULoad() { PDH_FMT_COUNTERVALUE cv; - HCOUNTER c; + int currentQueryIndex; - if (perfInit() < 0) { + if (perfCPUInit() < 0) { // warn? return -1.0; } - if (multiCounterCPULoad.query.query == NULL) { - // warn? - return -1.0; - } + currentQueryIndex = getCurrentQueryIndexForProcess(); - 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) { + 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); @@ -860,70 +1323,29 @@ 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; +static double +perfGetCPULoad(int which) { + PDH_FMT_COUNTERVALUE cv; + HCOUNTER c; - int error; - - if (running) { - return CONFIG_SUCCESSFUL; + if (perfCPUInit() < 0) { + // warn? + return -1.0; } - 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; - } - } + if (-1 == which) { + c = multiCounterCPULoad->counters[multiCounterCPULoad->noOfCounters - 1]; + } else { + if (which < multiCounterCPULoad->noOfCounters) { + c = multiCounterCPULoad->counters[which]; + } else { + return -1.0; } - } LeaveCriticalSection(&initializationLock); - - if (inited && error == CONFIG_SUCCESSFUL) { - running = true; } - - return error; + if (getPerformanceData(&multiCounterCPULoad->query, c, &cv, PDH_FMT_DOUBLE ) == 0) { + return cv.doubleValue / 100; + } + return -1.0; } JNIEXPORT jdouble JNICALL @@ -937,5 +1359,5 @@ Java_sun_management_OperatingSystemImpl_getProcessCpuLoad (JNIEnv *env, jobject dummy) { - return perfGetProcessLoad(); + return perfGetProcessCPULoad(); }