| /* |
| * 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 <psapi.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| |
| #include <malloc.h> |
| #pragma warning (push,0) |
| #include <windows.h> |
| #pragma warning (pop) |
| #include <stdio.h> |
| #include <time.h> |
| #include <stdint.h> |
| #include <assert.h> |
| |
| /* Disable warnings due to broken header files from Microsoft... */ |
| #pragma warning(push, 3) |
| #include <pdh.h> |
| #include <pdhmsg.h> |
| #include <process.h> |
| #pragma warning(pop) |
| |
| typedef unsigned __int32 juint; |
| typedef unsigned __int64 julong; |
| |
| 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(); |
| } |