mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
1361 lines
41 KiB
C
1361 lines
41 KiB
C
/*
|
|
* Copyright (c) 2003, 2025, 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)
|
|
|
|
#include <sysinfoapi.h>
|
|
|
|
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_getFreeMemorySize0
|
|
(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_getTotalMemorySize0
|
|
(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 (millis)
|
|
} UpdateQueryS, *UpdateQueryP;
|
|
|
|
// Min time between query updates (millis)
|
|
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;
|
|
|
|
/* PDH format patterns, and lengths of their constant component. */
|
|
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);
|
|
|
|
// Always include space for null terminator:
|
|
fullCounterPathLen = strlen(objectName) + strlen(counterName) + 1;
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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).
|
|
*
|
|
* Returns -1 on failure.
|
|
*/
|
|
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 < 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 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) versions 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) {
|
|
uint64_t now = GetTickCount64();
|
|
|
|
/*
|
|
* 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 += 3; // "%d" and '\0'
|
|
|
|
assert(pdhIDProcessCounterFmtLen < MAX_PATH);
|
|
pdhIDProcessCounterFmt = malloc(pdhIDProcessCounterFmtLen);
|
|
if (!pdhIDProcessCounterFmt) {
|
|
goto end;
|
|
}
|
|
|
|
/* "\Process(java#%d)\ID Process" */
|
|
snprintf(pdhIDProcessCounterFmt,
|
|
pdhIDProcessCounterFmtLen,
|
|
PROCESS_OBJECT_INSTANCE_COUNTER_FMT,
|
|
pdhLocalizedProcessObject,
|
|
pdhProcessImageName,
|
|
"%d",
|
|
pdhLocalizedIDProcessCounter);
|
|
|
|
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 (currentQueryIndex >= 0 && 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_getCpuLoad0
|
|
(JNIEnv *env, jobject dummy)
|
|
{
|
|
return perfGetCPULoad(-1);
|
|
}
|
|
|
|
JNIEXPORT jdouble JNICALL
|
|
Java_com_sun_management_internal_OperatingSystemImpl_getProcessCpuLoad0
|
|
(JNIEnv *env, jobject dummy)
|
|
{
|
|
return perfGetProcessCPULoad();
|
|
}
|