/* * 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_initialize (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_getTotalSwapSpaceSize (JNIEnv *env, jobject mbean) { MEMORYSTATUSEX ms; ms.dwLength = sizeof(ms); GlobalMemoryStatusEx(&ms); return (jlong) ms.ullTotalPageFile; } JNIEXPORT jlong JNICALL Java_sun_management_OperatingSystemImpl_getFreeSwapSpaceSize (JNIEnv *env, jobject mbean) { MEMORYSTATUSEX ms; ms.dwLength = sizeof(ms); GlobalMemoryStatusEx(&ms); return (jlong) ms.ullAvailPageFile; } JNIEXPORT jlong JNICALL Java_sun_management_OperatingSystemImpl_getProcessCpuTime (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_getFreePhysicalMemorySize (JNIEnv *env, jobject mbean) { MEMORYSTATUSEX ms; ms.dwLength = sizeof(ms); GlobalMemoryStatusEx(&ms); return (jlong) ms.ullAvailPhys; } JNIEXPORT jlong JNICALL Java_sun_management_OperatingSystemImpl_getTotalPhysicalMemorySize (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_getSystemCpuLoad (JNIEnv *env, jobject dummy) { return perfGetCPULoad(-1); } JNIEXPORT jdouble JNICALL Java_sun_management_OperatingSystemImpl_getProcessCpuLoad (JNIEnv *env, jobject dummy) { return perfGetProcessCPULoad(); }