Print this page
rev 10261 : 8056049: getProcessCpuLoad() stops working in one process when a different process exits
Reviewed-by: ctornqvi
Split |
Split |
Close |
Expand all |
Collapse all |
--- old/jdk/src/windows/native/sun/management/OperatingSystemImpl.c
+++ new/jdk/src/windows/native/sun/management/OperatingSystemImpl.c
1 1 /*
2 2 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
3 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 4 *
5 5 * This code is free software; you can redistribute it and/or modify it
6 6 * under the terms of the GNU General Public License version 2 only, as
7 7 * published by the Free Software Foundation. Oracle designates this
8 8 * particular file as subject to the "Classpath" exception as provided
9 9 * by Oracle in the LICENSE file that accompanied this code.
10 10 *
11 11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 14 * version 2 for more details (a copy is included in the LICENSE file that
15 15 * accompanied this code).
16 16 *
17 17 * You should have received a copy of the GNU General Public License version
18 18 * 2 along with this work; if not, write to the Free Software Foundation,
19 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 20 *
21 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 22 * or visit www.oracle.com if you need additional information or have any
23 23 * questions.
24 24 */
25 25
26 26 #include "jni.h"
27 27 #include "jni_util.h"
28 28 #include "jlong.h"
29 29 #include "jvm.h"
30 30 #include "management.h"
31 31 #include "sun_management_OperatingSystemImpl.h"
32 32
33 33 #include <psapi.h>
34 34 #include <errno.h>
35 35 #include <stdlib.h>
36 36
37 37 #include <malloc.h>
38 38 #pragma warning (push,0)
39 39 #include <windows.h>
40 40 #pragma warning (pop)
41 41 #include <stdio.h>
42 42 #include <time.h>
43 43 #include <stdint.h>
44 44 #include <assert.h>
45 45
↓ open down ↓ |
45 lines elided |
↑ open up ↑ |
46 46 /* Disable warnings due to broken header files from Microsoft... */
47 47 #pragma warning(push, 3)
48 48 #include <pdh.h>
49 49 #include <pdhmsg.h>
50 50 #include <process.h>
51 51 #pragma warning(pop)
52 52
53 53 typedef unsigned __int32 juint;
54 54 typedef unsigned __int64 julong;
55 55
56 -typedef enum boolean_values { false=0, true=1};
57 -
58 56 static void set_low(jlong* value, jint low) {
59 57 *value &= (jlong)0xffffffff << 32;
60 58 *value |= (jlong)(julong)(juint)low;
61 59 }
62 60
63 61 static void set_high(jlong* value, jint high) {
64 62 *value &= (jlong)(julong)(juint)0xffffffff;
65 63 *value |= (jlong)high << 32;
66 64 }
67 65
68 66 static jlong jlong_from(jint h, jint l) {
69 - jlong result = 0; // initialization to avoid warning
70 - set_high(&result, h);
71 - set_low(&result, l);
72 - return result;
67 + jlong result = 0; // initialization to avoid warning
68 + set_high(&result, h);
69 + set_low(&result, l);
70 + return result;
73 71 }
74 72
75 73 static HANDLE main_process;
76 74
77 -int perfiInit(void);
75 +static void perfInit(void);
78 76
79 77 JNIEXPORT void JNICALL
80 78 Java_sun_management_OperatingSystemImpl_initialize
81 79 (JNIEnv *env, jclass cls)
82 80 {
83 81 main_process = GetCurrentProcess();
84 - perfiInit();
82 + perfInit();
85 83 }
86 84
87 85 JNIEXPORT jlong JNICALL
88 86 Java_sun_management_OperatingSystemImpl_getCommittedVirtualMemorySize0
89 87 (JNIEnv *env, jobject mbean)
90 88 {
91 89 PROCESS_MEMORY_COUNTERS pmc;
92 90 if (GetProcessMemoryInfo(main_process, &pmc, sizeof(PROCESS_MEMORY_COUNTERS)) == 0) {
93 91 return (jlong)-1L;
94 92 } else {
95 93 return (jlong) pmc.PagefileUsage;
96 94 }
97 95 }
98 96
99 97 JNIEXPORT jlong JNICALL
100 98 Java_sun_management_OperatingSystemImpl_getTotalSwapSpaceSize
101 99 (JNIEnv *env, jobject mbean)
102 100 {
103 101 MEMORYSTATUSEX ms;
104 102 ms.dwLength = sizeof(ms);
105 103 GlobalMemoryStatusEx(&ms);
106 104 return (jlong) ms.ullTotalPageFile;
107 105 }
108 106
109 107 JNIEXPORT jlong JNICALL
110 108 Java_sun_management_OperatingSystemImpl_getFreeSwapSpaceSize
111 109 (JNIEnv *env, jobject mbean)
112 110 {
113 111 MEMORYSTATUSEX ms;
114 112 ms.dwLength = sizeof(ms);
115 113 GlobalMemoryStatusEx(&ms);
116 114 return (jlong) ms.ullAvailPageFile;
117 115 }
118 116
119 117 JNIEXPORT jlong JNICALL
120 118 Java_sun_management_OperatingSystemImpl_getProcessCpuTime
121 119 (JNIEnv *env, jobject mbean)
122 120 {
123 121
124 122 FILETIME process_creation_time, process_exit_time,
125 123 process_user_time, process_kernel_time;
126 124
127 125 // Using static variables declared above
128 126 // Units are 100-ns intervals. Convert to ns.
129 127 GetProcessTimes(main_process, &process_creation_time,
130 128 &process_exit_time,
131 129 &process_kernel_time, &process_user_time);
132 130 return (jlong_from(process_user_time.dwHighDateTime,
133 131 process_user_time.dwLowDateTime) +
134 132 jlong_from(process_kernel_time.dwHighDateTime,
135 133 process_kernel_time.dwLowDateTime)) * 100;
136 134 }
137 135
138 136 JNIEXPORT jlong JNICALL
139 137 Java_sun_management_OperatingSystemImpl_getFreePhysicalMemorySize
140 138 (JNIEnv *env, jobject mbean)
141 139 {
142 140 MEMORYSTATUSEX ms;
143 141 ms.dwLength = sizeof(ms);
144 142 GlobalMemoryStatusEx(&ms);
145 143 return (jlong) ms.ullAvailPhys;
146 144 }
147 145
↓ open down ↓ |
53 lines elided |
↑ open up ↑ |
148 146 JNIEXPORT jlong JNICALL
149 147 Java_sun_management_OperatingSystemImpl_getTotalPhysicalMemorySize
150 148 (JNIEnv *env, jobject mbean)
151 149 {
152 150 MEMORYSTATUSEX ms;
153 151 ms.dwLength = sizeof(ms);
154 152 GlobalMemoryStatusEx(&ms);
155 153 return (jlong) ms.ullTotalPhys;
156 154 }
157 155
158 -// Seems WinXP PDH returns PDH_MORE_DATA whenever we send in a NULL buffer.
159 -// Let's just ignore it, since we make sure we have enough buffer anyway.
160 -static int
161 -pdh_fail(PDH_STATUS pdhStat) {
162 - return pdhStat != ERROR_SUCCESS && pdhStat != PDH_MORE_DATA;
163 -}
164 -
165 -// INFO: Using PDH APIs Correctly in a Localized Language (Q287159)
166 -// http://support.microsoft.com/default.aspx?scid=kb;EN-US;q287159
167 -// The index value for the base system counters and objects like processor,
168 -// process, thread, memory, and so forth are always the same irrespective
169 -// of the localized version of the operating system or service pack installed.
170 -#define PDH_PROCESSOR_IDX ((DWORD) 238)
171 -#define PDH_PROCESSOR_TIME_IDX ((DWORD) 6)
172 -#define PDH_PRIV_PROCESSOR_TIME_IDX ((DWORD) 144)
173 -#define PDH_PROCESS_IDX ((DWORD) 230)
174 -#define PDH_ID_PROCESS_IDX ((DWORD) 784)
175 -#define PDH_CONTEXT_SWITCH_RATE_IDX ((DWORD) 146)
176 -#define PDH_SYSTEM_IDX ((DWORD) 2)
177 -#define PDH_VIRTUAL_BYTES_IDX ((DWORD) 174)
156 +/* Performance Data Helper API (PDH) support */
178 157
179 158 typedef PDH_STATUS (WINAPI *PdhAddCounterFunc)(
180 159 HQUERY hQuery,
181 160 LPCSTR szFullCounterPath,
182 161 DWORD dwUserData,
183 162 HCOUNTER *phCounter
184 163 );
185 164 typedef PDH_STATUS (WINAPI *PdhOpenQueryFunc)(
186 - LPCWSTR szDataSource,
187 - DWORD dwUserData,
188 - HQUERY *phQuery
189 - );
190 -typedef DWORD (WINAPI *PdhCloseQueryFunc)(
191 - HQUERY hQuery
192 - );
165 + LPCWSTR szDataSource,
166 + DWORD dwUserData,
167 + HQUERY *phQuery
168 + );
193 169 typedef PDH_STATUS (WINAPI *PdhCollectQueryDataFunc)(
194 - HQUERY hQuery
195 - );
196 -typedef DWORD (WINAPI *PdhGetFormattedCounterValueFunc)(
197 - HCOUNTER hCounter,
198 - DWORD dwFormat,
199 - LPDWORD lpdwType,
200 - PPDH_FMT_COUNTERVALUE pValue
201 - );
170 + HQUERY hQuery
171 + );
172 +
202 173 typedef PDH_STATUS (WINAPI *PdhEnumObjectItemsFunc)(
203 - LPCTSTR szDataSource,
204 - LPCTSTR szMachineName,
205 - LPCTSTR szObjectName,
206 - LPTSTR mszCounterList,
207 - LPDWORD pcchCounterListLength,
208 - LPTSTR mszInstanceList,
209 - LPDWORD pcchInstanceListLength,
210 - DWORD dwDetailLevel,
211 - DWORD dwFlags
212 - );
174 + LPCTSTR szDataSource,
175 + LPCTSTR szMachineName,
176 + LPCTSTR szObjectName,
177 + LPTSTR mszCounterList,
178 + LPDWORD pcchCounterListLength,
179 + LPTSTR mszInstanceList,
180 + LPDWORD pcchInstanceListLength,
181 + DWORD dwDetailLevel,
182 + DWORD dwFlags
183 + );
213 184 typedef PDH_STATUS (WINAPI *PdhRemoveCounterFunc)(
214 - HCOUNTER hCounter
215 - );
185 + HCOUNTER hCounter
186 + );
216 187 typedef PDH_STATUS (WINAPI *PdhLookupPerfNameByIndexFunc)(
217 - LPCSTR szMachineName,
218 - DWORD dwNameIndex,
219 - LPSTR szNameBuffer,
220 - LPDWORD pcchNameBufferSize
221 - );
222 -typedef PDH_STATUS (WINAPI *PdhMakeCounterPathFunc)(
223 - PDH_COUNTER_PATH_ELEMENTS *pCounterPathElements,
224 - LPTSTR szFullPathBuffer,
225 - LPDWORD pcchBufferSize,
226 - DWORD dwFlags
227 - );
188 + LPCSTR szMachineName,
189 + DWORD dwNameIndex,
190 + LPSTR szNameBuffer,
191 + LPDWORD pcchNameBufferSize
192 + );
193 +typedef DWORD (WINAPI *PdhCloseQueryFunc)(
194 + HQUERY hQuery
195 + );
196 +
197 +typedef DWORD (WINAPI *PdhGetFormattedCounterValueFunc)(
198 + HCOUNTER hCounter,
199 + DWORD dwFormat,
200 + LPDWORD lpdwType,
201 + PPDH_FMT_COUNTERVALUE pValue
202 + );
228 203
229 204 static PdhAddCounterFunc PdhAddCounter_i;
230 205 static PdhOpenQueryFunc PdhOpenQuery_i;
231 206 static PdhCloseQueryFunc PdhCloseQuery_i;
232 207 static PdhCollectQueryDataFunc PdhCollectQueryData_i;
233 208 static PdhGetFormattedCounterValueFunc PdhGetFormattedCounterValue_i;
234 209 static PdhEnumObjectItemsFunc PdhEnumObjectItems_i;
235 210 static PdhRemoveCounterFunc PdhRemoveCounter_i;
236 211 static PdhLookupPerfNameByIndexFunc PdhLookupPerfNameByIndex_i;
237 -static PdhMakeCounterPathFunc PdhMakeCounterPath_i;
238 -
239 -static HANDLE thisProcess;
240 -static double cpuFactor;
241 -static DWORD num_cpus;
242 -
243 -#define FT2JLONG(X) ((((jlong)X.dwHighDateTime) << 32) | ((jlong)X.dwLowDateTime))
244 -#define COUNTER_BUF_SIZE 256
245 -// Min time between query updates.
246 -#define MIN_UPDATE_INTERVAL 500
247 -#define CONFIG_SUCCESSFUL 0
248 212
249 -/**
213 +/*
250 214 * Struct for PDH queries.
251 215 */
252 216 typedef struct {
253 217 HQUERY query;
254 - uint64_t lastUpdate; // Last time query was updated (current millis).
218 + uint64_t lastUpdate; // Last time query was updated (ticks)
255 219 } UpdateQueryS, *UpdateQueryP;
256 220
257 -/**
258 - * Struct for the processor load counters.
221 +// Min time between query updates (ticks)
222 +static const int MIN_UPDATE_INTERVAL = 500;
223 +
224 +/*
225 + * Struct for a PDH query with multiple counters.
259 226 */
260 227 typedef struct {
261 - UpdateQueryS query;
262 - HCOUNTER* counters;
263 - int noOfCounters;
228 + UpdateQueryS query;
229 + HCOUNTER* counters;
230 + int noOfCounters;
264 231 } MultipleCounterQueryS, *MultipleCounterQueryP;
265 232
266 -/**
267 - * Struct for the jvm process load counter.
233 +/*
234 + * Struct for a PDH query with a single counter.
268 235 */
269 236 typedef struct {
270 - UpdateQueryS query;
237 + UpdateQueryS query;
271 238 HCOUNTER counter;
272 239 } SingleCounterQueryS, *SingleCounterQueryP;
273 240
274 -static char* getProcessPDHHeader(void);
275 241
276 -/**
277 - * Currently available counters.
242 +typedef struct {
243 + CRITICAL_SECTION cs;
244 + DWORD owningThread;
245 + DWORD recursionCount;
246 +} PdhCriticalSectionS, *PdhCriticalSectionP;
247 +
248 +static PdhCriticalSectionS initializationLock;
249 +
250 +static void InitializePdhCriticalSection(PdhCriticalSectionP criticalSection) {
251 + assert(criticalSection);
252 +
253 + InitializeCriticalSection(&criticalSection->cs);
254 + criticalSection->owningThread = 0;
255 + criticalSection->recursionCount = 0;
256 +}
257 +
258 +static void EnterPdhCriticalSection(PdhCriticalSectionP criticalSection) {
259 + assert(criticalSection);
260 +
261 + EnterCriticalSection(&criticalSection->cs);
262 + criticalSection->recursionCount++;
263 + if (!criticalSection->owningThread) {
264 + criticalSection->owningThread = GetCurrentThreadId();
265 + }
266 +}
267 +
268 +static void LeavePdhCriticalSection(PdhCriticalSectionP criticalSection) {
269 + assert(criticalSection);
270 + assert(GetCurrentThreadId() == criticalSection->owningThread);
271 + assert(criticalSection->recursionCount >= 1);
272 +
273 + criticalSection->recursionCount--;
274 + if (!criticalSection->recursionCount) {
275 + criticalSection->owningThread = 0;
276 + }
277 + LeaveCriticalSection(&criticalSection->cs);
278 +}
279 +
280 +/*
281 + * INFO: Using PDH APIs Correctly in a Localized Language (Q287159)
282 + * http://support.microsoft.com/default.aspx?scid=kb;EN-US;q287159
283 + * The index value for the base system counters and objects like processor,
284 + * process, thread, memory, and so forth are always the same irrespective
285 + * of the localized version of the operating system or service pack installed.
286 + * To find the correct index for an object or counter, inspect the registry key/value:
287 + * [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009\Counter]
278 288 */
279 -static SingleCounterQueryS cntCtxtSwitchRate;
280 -static SingleCounterQueryS cntVirtualSize;
281 -static SingleCounterQueryS cntProcLoad;
282 -static SingleCounterQueryS cntProcSystemLoad;
283 -static MultipleCounterQueryS multiCounterCPULoad;
289 +static const DWORD PDH_PROCESSOR_IDX = 238;
290 +static const DWORD PDH_PROCESSOR_TIME_IDX = 6;
291 +static const DWORD PDH_PROCESS_IDX = 230;
292 +static const DWORD PDH_ID_PROCESS_IDX = 784;
293 +
294 +/* useful pdh fmt's */
295 +static const char* const OBJECT_COUNTER_FMT = "\\%s\\%s";
296 +static const size_t OBJECT_COUNTER_FMT_LEN = 2;
297 +static const char* const OBJECT_WITH_INSTANCES_COUNTER_FMT = "\\%s(%s)\\%s";
298 +static const size_t OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN = 4;
299 +static const char* const PROCESS_OBJECT_INSTANCE_COUNTER_FMT = "\\%s(%s#%s)\\%s";
300 +static const size_t PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN = 5;
301 +
302 +static const char* pdhProcessImageName = NULL; /* "java" */
303 +static char* pdhIDProcessCounterFmt = NULL; /* "\Process(java#%d)\ID Process" */
284 304
285 -static CRITICAL_SECTION processHeaderLock;
286 -static CRITICAL_SECTION initializationLock;
305 +static int numberOfJavaProcessesAtInitialization = 0;
287 306
288 -/**
289 - * Initialize the perf module at startup.
307 +/*
308 + * Currently used CPU queries/counters and variables
290 309 */
291 -int
292 -perfiInit(void)
293 -{
294 - InitializeCriticalSection(&processHeaderLock);
295 - InitializeCriticalSection(&initializationLock);
296 - return 0;
310 +static SingleCounterQueryP processTotalCPULoad = NULL;
311 +static MultipleCounterQueryP multiCounterCPULoad = NULL;
312 +static double cpuFactor = .0;
313 +static DWORD numCpus = 0;
314 +
315 +/*
316 + * Seems WinXP PDH returns PDH_MORE_DATA whenever we send in a NULL buffer.
317 + * Let's just ignore it, since we make sure we have enough buffer anyway.
318 + */
319 +static int
320 +pdhFail(PDH_STATUS pdhStat) {
321 + return pdhStat != ERROR_SUCCESS && pdhStat != PDH_MORE_DATA;
297 322 }
298 323
299 -/**
300 - * Dynamically sets up function pointers to the PDH library.
324 +static const char*
325 +allocateAndCopy(const char* const originalString) {
326 + size_t len;
327 + char* allocatedString;
328 +
329 + assert(originalString);
330 +
331 + len = strlen(originalString);
332 +
333 + allocatedString = malloc(len + 1);
334 +
335 + if (!allocatedString) {
336 + return NULL;
337 + }
338 +
339 + strncpy(allocatedString, originalString, len);
340 + allocatedString[len] = '\0';
341 +
342 + return allocatedString;
343 +}
344 +
345 +/*
346 + * Allocates memory into the supplied pointer and
347 + * fills it with the localized PDH artifact description, if indexed correctly.
348 + * Caller owns the memory from the point of returning from this function.
301 349 *
302 - * @return CONFIG_SUCCESSFUL on success, negative on failure.
350 + * @param index the PDH counter index as specified in the registry
351 + * @param ppBuffer pointer to a char*.
352 + * @return 0 if successful, negative on failure.
303 353 */
304 354 static int
305 -get_functions(HMODULE h, char *ebuf, size_t elen) {
306 - // The 'A' at the end means the ANSI (not the UNICODE) vesions of the methods
307 - PdhAddCounter_i = (PdhAddCounterFunc)GetProcAddress(h, "PdhAddCounterA");
308 - PdhOpenQuery_i = (PdhOpenQueryFunc)GetProcAddress(h, "PdhOpenQueryA");
309 - PdhCloseQuery_i = (PdhCloseQueryFunc)GetProcAddress(h, "PdhCloseQuery");
310 - PdhCollectQueryData_i = (PdhCollectQueryDataFunc)GetProcAddress(h, "PdhCollectQueryData");
311 - PdhGetFormattedCounterValue_i = (PdhGetFormattedCounterValueFunc)GetProcAddress(h, "PdhGetFormattedCounterValue");
312 - PdhEnumObjectItems_i = (PdhEnumObjectItemsFunc)GetProcAddress(h, "PdhEnumObjectItemsA");
313 - PdhRemoveCounter_i = (PdhRemoveCounterFunc)GetProcAddress(h, "PdhRemoveCounter");
314 - PdhLookupPerfNameByIndex_i = (PdhLookupPerfNameByIndexFunc)GetProcAddress(h, "PdhLookupPerfNameByIndexA");
315 - PdhMakeCounterPath_i = (PdhMakeCounterPathFunc)GetProcAddress(h, "PdhMakeCounterPathA");
355 +lookupNameByIndex(DWORD index, char** ppBuffer) {
356 + DWORD size;
316 357
317 - if (PdhAddCounter_i == NULL || PdhOpenQuery_i == NULL ||
318 - PdhCloseQuery_i == NULL || PdhCollectQueryData_i == NULL ||
319 - PdhGetFormattedCounterValue_i == NULL || PdhEnumObjectItems_i == NULL ||
320 - PdhRemoveCounter_i == NULL || PdhLookupPerfNameByIndex_i == NULL || PdhMakeCounterPath_i == NULL)
321 - {
322 - _snprintf(ebuf, elen, "Required method could not be found.");
358 + assert(ppBuffer);
359 +
360 + /* determine size needed */
361 + if (PdhLookupPerfNameByIndex_i(NULL, index, NULL, &size) != PDH_MORE_DATA) {
362 + /* invalid index? */
363 + return -1;
364 + }
365 +
366 + *ppBuffer = malloc((size_t)size);
367 +
368 + if (!*ppBuffer) {
369 + return -1;
370 + }
371 +
372 + if (PdhLookupPerfNameByIndex_i(NULL, index, *ppBuffer, &size) != ERROR_SUCCESS) {
373 + free(*ppBuffer);
374 + *ppBuffer = NULL;
323 375 return -1;
324 376 }
325 - return CONFIG_SUCCESSFUL;
377 +
378 + /* windows vista does not null-terminate the string
379 + * (although the docs says it will) */
380 + (*ppBuffer)[size - 1] = '\0';
381 +
382 + return 0;
326 383 }
327 384
328 -/**
329 - * Returns the counter value as a double for the specified query.
330 - * Will collect the query data and update the counter values as necessary.
331 - *
332 - * @param query the query to update (if needed).
333 - * @param c the counter to read.
334 - * @param value where to store the formatted value.
335 - * @param format the format to use (i.e. PDH_FMT_DOUBLE, PDH_FMT_LONG etc)
336 - * @return CONFIG_SUCCESSFUL if no error
337 - * -1 if PdhCollectQueryData fails
338 - * -2 if PdhGetFormattedCounterValue fails
339 - */
340 -static int
341 -getPerformanceData(UpdateQueryP query, HCOUNTER c, PDH_FMT_COUNTERVALUE* value, DWORD format) {
342 - clock_t now;
343 - now = clock();
385 +/*
386 +* Construct a fully qualified PDH path
387 +*
388 +* @param objectName a PDH Object string representation (required)
389 +* @param counterName a PDH Counter string representation (required)
390 +* @param imageName a process image name string, ex. "java" (opt)
391 +* @param instance an instance string, ex. "0", "1", ... (opt)
392 +* @return the fully qualified PDH path.
393 +*
394 +* Caller will own the returned malloc:ed string
395 +*/
396 +static const char*
397 +makeFullCounterPath(const char* const objectName,
398 + const char* const counterName,
399 + const char* const imageName,
400 + const char* const instance) {
401 +
402 + size_t fullCounterPathLen;
403 + char* fullCounterPath;
404 +
405 + assert(objectName);
406 + assert(counterName);
407 +
408 + fullCounterPathLen = strlen(objectName);
409 + fullCounterPathLen += strlen(counterName);
410 +
411 + if (imageName) {
412 + /*
413 + * For paths using the "Process" Object.
414 + *
415 + * Examples:
416 + * abstract: "\Process(imageName#instance)\Counter"
417 + * actual: "\Process(java#2)\ID Process"
418 + */
419 + fullCounterPathLen += PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN;
420 + fullCounterPathLen += strlen(imageName);
421 +
422 + /*
423 + * imageName must be passed together with an associated
424 + * instance "number" ("0", "1", "2", ...).
425 + * This is required in order to create valid "Process" Object paths.
426 + *
427 + * Examples: "\Process(java#0)", \Process(java#1"), ...
428 + */
429 + assert(instance);
344 430
345 - // Need to limit how often we update the query
346 - // to mimise the heisenberg effect.
347 - // (PDH behaves erratically if the counters are
348 - // queried too often, especially counters that
349 - // store and use values from two consecutive updates,
350 - // like cpu load.)
351 - if (now - query->lastUpdate > MIN_UPDATE_INTERVAL) {
352 - if (PdhCollectQueryData_i(query->query) != ERROR_SUCCESS) {
353 - return -1;
431 + fullCounterPathLen += strlen(instance);
432 +
433 + fullCounterPath = malloc(fullCounterPathLen + 1);
434 +
435 + if (!fullCounterPath) {
436 + return NULL;
354 437 }
355 - query->lastUpdate = now;
356 - }
357 438
358 - if (PdhGetFormattedCounterValue_i(c, format, NULL, value) != ERROR_SUCCESS) {
359 - return -2;
439 + _snprintf(fullCounterPath,
440 + fullCounterPathLen,
441 + PROCESS_OBJECT_INSTANCE_COUNTER_FMT,
442 + objectName,
443 + imageName,
444 + instance,
445 + counterName);
446 + } else {
447 + if (instance) {
448 + /*
449 + * For paths where the Object has multiple instances.
450 + *
451 + * Examples:
452 + * abstract: "\Object(instance)\Counter"
453 + * actual: "\Processor(0)\% Privileged Time"
454 + */
455 + fullCounterPathLen += strlen(instance);
456 + fullCounterPathLen += OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN;
457 + } else {
458 + /*
459 + * For "normal" paths.
460 + *
461 + * Examples:
462 + * abstract: "\Object\Counter"
463 + * actual: "\Memory\Available Mbytes"
464 + */
465 + fullCounterPathLen += OBJECT_COUNTER_FMT_LEN;
466 + }
467 +
468 + fullCounterPath = malloc(fullCounterPathLen + 1);
469 +
470 + if (!fullCounterPath) {
471 + return NULL;
472 + }
473 +
474 + if (instance) {
475 + _snprintf(fullCounterPath,
476 + fullCounterPathLen,
477 + OBJECT_WITH_INSTANCES_COUNTER_FMT,
478 + objectName,
479 + instance,
480 + counterName);
481 + } else {
482 + _snprintf(fullCounterPath,
483 + fullCounterPathLen,
484 + OBJECT_COUNTER_FMT,
485 + objectName,
486 + counterName);
487 + }
360 488 }
361 - return CONFIG_SUCCESSFUL;
489 +
490 + fullCounterPath[fullCounterPathLen] = '\0';
491 +
492 + return fullCounterPath;
362 493 }
363 494
364 -/**
365 - * Places the resolved counter name of the counter at the specified index in the
366 - * supplied buffer. There must be enough space in the buffer to hold the counter name.
495 +/*
496 + * Resolves an index for a PDH artifact to
497 + * a localized, malloc:ed string representation.
498 + * Caller will own the returned malloc:ed string.
367 499 *
368 - * @param index the counter index as specified in the registry.
369 - * @param buf the buffer in which to place the counter name.
370 - * @param size the size of the counter name buffer.
371 - * @param ebuf the error message buffer.
372 - * @param elen the length of the error buffer.
373 - * @return CONFIG_SUCCESSFUL if successful, negative on failure.
500 + * @param pdhArtifactIndex PDH index
501 + * @return malloc:ed string representation
502 + * of the requested pdh artifact (localized).
503 + * NULL on failure.
374 504 */
375 -static int
376 -find_name(DWORD index, char *buf, DWORD size) {
377 - PDH_STATUS res;
505 +static const char*
506 +getPdhLocalizedArtifact(DWORD pdhArtifactIndex) {
507 + char* pdhLocalizedArtifactString;
508 +
509 + if (lookupNameByIndex(pdhArtifactIndex,
510 + &pdhLocalizedArtifactString) != 0) {
511 + return NULL;
512 + }
378 513
379 - if ((res = PdhLookupPerfNameByIndex_i(NULL, index, buf, &size)) != ERROR_SUCCESS) {
514 + return pdhLocalizedArtifactString;
515 +}
516 +
517 +static void
518 +pdhCleanup(HQUERY* const query, HCOUNTER* const counter) {
519 + if (counter && *counter) {
520 + PdhRemoveCounter_i(*counter);
521 + *counter = NULL;
522 + }
523 + if (query && *query) {
524 + PdhCloseQuery_i(*query);
525 + *query = NULL;
526 + }
527 +}
380 528
381 - /* printf("Could not open counter %d: error=0x%08x", index, res); */
382 - /* if (res == PDH_CSTATUS_NO_MACHINE) { */
383 - /* printf("User probably does not have sufficient privileges to use"); */
384 - /* printf("performance counters. If you are running on Windows 2003"); */
385 - /* printf("or Windows Vista, make sure the user is in the"); */
386 - /* printf("Performance Logs user group."); */
387 - /* } */
529 +static void
530 +destroySingleCounter(SingleCounterQueryP counterQuery) {
531 + if (counterQuery) {
532 + pdhCleanup(&counterQuery->query.query, &counterQuery->counter);
533 + }
534 +}
535 +
536 +static void
537 +destroyMultiCounter(MultipleCounterQueryP multiCounterQuery) {
538 + int i;
539 + if (multiCounterQuery) {
540 + if (multiCounterQuery->counters) {
541 + for (i = 0; i < multiCounterQuery->noOfCounters; i++) {
542 + pdhCleanup(NULL, &multiCounterQuery->counters[i]);
543 + }
544 + free(multiCounterQuery->counters);
545 + multiCounterQuery->counters = NULL;
546 + }
547 + pdhCleanup(&multiCounterQuery->query.query, NULL);
548 + }
549 +}
550 +
551 +static int
552 +openQuery(HQUERY* const query) {
553 + assert(query);
554 +
555 + if (PdhOpenQuery_i(NULL, 0, query) != ERROR_SUCCESS) {
388 556 return -1;
389 557 }
390 558
391 - if (size == 0) {
392 - /* printf("Failed to get counter name for %d: empty string", index); */
559 + return 0;
560 +}
561 +
562 +static int
563 +addCounter(HQUERY query,
564 + const char* const fullCounterPath,
565 + HCOUNTER* const counter) {
566 +
567 + assert(fullCounterPath);
568 + assert(counter);
569 +
570 + if (PdhAddCounter_i(query,
571 + fullCounterPath,
572 + 0,
573 + counter) != ERROR_SUCCESS) {
393 574 return -1;
394 575 }
395 576
396 - // windows vista does not null-terminate the string (allthough the docs says it will)
397 - buf[size - 1] = '\0';
398 - return CONFIG_SUCCESSFUL;
577 + return 0;
399 578 }
400 579
401 -/**
580 +/*
402 581 * Sets up the supplied SingleCounterQuery to listen for the specified counter.
403 - * initPDH() must have been run prior to calling this function!
404 582 *
405 - * @param counterQuery the counter query to set up.
406 - * @param counterString the string specifying the path to the counter.
407 - * @param ebuf the error buffer.
408 - * @param elen the length of the error buffer.
409 - * @returns CONFIG_SUCCESSFUL if successful, negative on failure.
583 + * @param counterQuery the counter query to set up.
584 + * @param fullCounterPath the string specifying the full path to the counter.
585 + * @returns 0 if successful, negative on failure.
410 586 */
411 587 static int
412 -initSingleCounterQuery(SingleCounterQueryP counterQuery, char *counterString) {
413 - if (PdhOpenQuery_i(NULL, 0, &counterQuery->query.query) != ERROR_SUCCESS) {
414 - /* printf("Could not open query for %s", counterString); */
415 - return -1;
416 - }
417 - if (PdhAddCounter_i(counterQuery->query.query, counterString, 0, &counterQuery->counter) != ERROR_SUCCESS) {
418 - /* printf("Could not add counter %s for query", counterString); */
419 - if (counterQuery->counter != NULL) {
420 - PdhRemoveCounter_i(counterQuery->counter);
588 +initializeSingleCounterQuery(SingleCounterQueryP counterQuery,
589 + const char* const fullCounterPath) {
590 + assert(counterQuery);
591 + assert(fullCounterPath);
592 +
593 + if (openQuery(&counterQuery->query.query) == 0) {
594 + if (addCounter(counterQuery->query.query,
595 + fullCounterPath,
596 + &counterQuery->counter) == 0) {
597 + return 0;
421 598 }
422 - if (counterQuery->query.query != NULL) {
423 - PdhCloseQuery_i(counterQuery->query.query);
599 + }
600 +
601 + return -1;
602 +}
603 +
604 +/*
605 + * Sets up a SingleCounterQuery
606 + *
607 + * param counter the counter query to set up.
608 + * param localizedObject string representing the PDH object to query
609 + * param localizedCounter string representing the PDH counter to query
610 + * param processImageName if the counter query needs the process image name ("java")
611 + * param instance if the counter has instances, this is the instance ("\Processor(0)\")
612 + where 0 is the instance
613 + * param firstSampleOnInit for counters that need two queries to yield their values,
614 + the first query can be issued just after initialization
615 + *
616 + * @returns 0 if successful, negative on failure.
617 + */
618 +static int
619 +initializeSingleCounter(SingleCounterQueryP const counter,
620 + const char* const localizedObject,
621 + const char* const localizedCounter,
622 + const char* const processImageName,
623 + const char* const instance,
624 + BOOL firstSampleOnInit) {
625 + int retValue = -1;
626 +
627 + const char* fullCounterPath = makeFullCounterPath(localizedObject,
628 + localizedCounter,
629 + processImageName,
630 + instance);
631 +
632 + if (fullCounterPath) {
633 +
634 + assert(counter);
635 +
636 + if (initializeSingleCounterQuery(counter, fullCounterPath) == 0) {
637 + /*
638 + * According to the MSDN documentation, rate counters must be read twice:
639 + *
640 + * "Obtaining the value of rate counters such as Page faults/sec requires that
641 + * PdhCollectQueryData be called twice, with a specific time interval between
642 + * the two calls, before calling PdhGetFormattedCounterValue. Call Sleep to
643 + * implement the waiting period between the two calls to PdhCollectQueryData."
644 + *
645 + * Take the first sample here already to allow for the next (first) "real" sample
646 + * to succeed.
647 + */
648 + if (firstSampleOnInit) {
649 + PdhCollectQueryData_i(counter->query.query);
650 + }
651 +
652 + retValue = 0;
424 653 }
425 - memset(counterQuery, 0, sizeof(SingleCounterQueryS));
426 - return -1;
654 + free((char*)fullCounterPath);
427 655 }
428 - return CONFIG_SUCCESSFUL;
656 +
657 + return retValue;
429 658 }
430 659
431 -/**
432 - * Sets up the supplied SingleCounterQuery to listen for the time spent
433 - * by the HotSpot process.
660 +static void
661 +perfInit(void) {
662 + InitializePdhCriticalSection(&initializationLock);
663 +}
664 +
665 +static int
666 +getProcessID() {
667 + static int myPid = 0;
668 + if (0 == myPid) {
669 + myPid = _getpid();
670 + }
671 + return myPid;
672 +}
673 +
674 +/*
675 + * Working against the Process object and it's related counters is inherently problematic
676 + * when using the PDH API:
677 + *
678 + * For PDH, a process is not primarily identified by it's process id,
679 + * but with a sequential number, for example \Process(java#0), \Process(java#1), ....
680 + * The really bad part is that this list is reset as soon as one process exits:
681 + * If \Process(java#1) exits, \Process(java#3) now becomes \Process(java#2) etc.
682 + *
683 + * The PDH query api requires a process identifier to be submitted when registering
684 + * a query, but as soon as the list resets, the query is invalidated (since the name
685 + * changed).
686 + *
687 + * Solution:
688 + * The #number identifier for a Process query can only decrease after process creation.
689 + *
690 + * Therefore we create an array of counter queries for all process object instances
691 + * up to and including ourselves:
692 + *
693 + * Ex. we come in as third process instance (java#2), we then create and register
694 + * queries for the following Process object instances:
695 + * java#0, java#1, java#2
434 696 *
435 - * @param counterQuery the counter query to set up as a process counter.
436 - * @param ebuf the error buffer.
437 - * @param elen the length of the error buffer.
438 - * @returns CONFIG_SUCCESSFUL if successful, negative on failure.
697 + * currentQueryIndexForProcess() keeps track of the current "correct" query
698 + * (in order to keep this index valid when the list resets from underneath,
699 + * ensure to call getCurrentQueryIndexForProcess() before every query involving
700 + * Process object instance data).
439 701 */
440 702 static int
441 -initProcLoadCounter(void) {
442 - char time[COUNTER_BUF_SIZE];
443 - char counter[COUNTER_BUF_SIZE*2];
703 +currentQueryIndexForProcess(void) {
704 + HQUERY tmpQuery = NULL;
705 + HCOUNTER handleCounter = NULL;
706 + int retValue = -1;
707 +
708 + assert(pdhProcessImageName);
709 + assert(pdhIDProcessCounterFmt);
710 +
711 + if (openQuery(&tmpQuery) == 0) {
712 + int index;
713 +
714 + /* iterate over all instance indexes and try to find our own pid */
715 + for (index = 0; index < INT_MAX; ++index) {
716 + char fullIDProcessCounterPath[MAX_PATH];
717 + PDH_FMT_COUNTERVALUE counterValue;
718 + PDH_STATUS res;
444 719
445 - if (find_name(PDH_PROCESSOR_TIME_IDX, time, sizeof(time)-1) < 0) {
446 - return -1;
720 + _snprintf(fullIDProcessCounterPath,
721 + MAX_PATH,
722 + pdhIDProcessCounterFmt,
723 + index);
724 +
725 + if (addCounter(tmpQuery, fullIDProcessCounterPath, &handleCounter) != 0) {
726 + break;
727 + }
728 +
729 + res = PdhCollectQueryData_i(tmpQuery);
730 +
731 + if (PDH_INVALID_HANDLE == res || PDH_NO_DATA == res) {
732 + break;
733 + }
734 +
735 + PdhGetFormattedCounterValue_i(handleCounter,
736 + PDH_FMT_LONG,
737 + NULL,
738 + &counterValue);
739 + /*
740 + * This check seems to be needed for Win2k SMP boxes, since
741 + * they for some reason don't return PDH_NO_DATA for non existing
742 + * counters.
743 + */
744 + if (counterValue.CStatus != PDH_CSTATUS_VALID_DATA) {
745 + break;
746 + }
747 +
748 + if ((LONG)getProcessID() == counterValue.longValue) {
749 + retValue = index;
750 + break;
751 + }
752 + }
447 753 }
448 - _snprintf(counter, sizeof(counter)-1, "%s\\%s", getProcessPDHHeader(), time);
449 - return initSingleCounterQuery(&cntProcLoad, counter);
754 +
755 + pdhCleanup(&tmpQuery, &handleCounter);
756 +
757 + return retValue;
450 758 }
451 759
760 +/*
761 + * If successful, returns the #index corresponding to our PID
762 + * as resolved by the pdh query:
763 + * "\Process(java#index)\ID Process" (or localized equivalent)
764 + *
765 + * This function should be called before attempting to read
766 + * from any Process related counter(s), and the return value
767 + * is the index to be used for indexing an array of Process object query's:
768 + *
769 + * Example:
770 + * processTotalCPULoad[currentQueryIndex].query
771 + *
772 + * Returns -1 on failure.
773 + */
452 774 static int
453 -initProcSystemLoadCounter(void) {
454 - char time[COUNTER_BUF_SIZE];
455 - char counter[COUNTER_BUF_SIZE*2];
775 +getCurrentQueryIndexForProcess() {
776 + int currentQueryIndex = currentQueryIndexForProcess();
456 777
457 - if (find_name(PDH_PRIV_PROCESSOR_TIME_IDX, time, sizeof(time)-1) < 0) {
458 - return -1;
778 + assert(currentQueryIndex >= 0 &&
779 + currentQueryIndex < numberOfJavaProcessesAtInitialization);
780 +
781 + return currentQueryIndex;
782 +}
783 +
784 +/*
785 + * Returns the PDH string identifying the current process image name.
786 + * Use this name as a qualifier when getting counters from the PDH Process Object
787 + * representing your process.
788 +
789 + * Example:
790 + * "\Process(java#0)\Virtual Bytes" - where "java" is the PDH process
791 + * image name.
792 + *
793 + * Please note that the process image name is not necessarily "java",
794 + * hence the use of GetModuleFileName() to detect the process image name.
795 + *
796 + * @return the process image name to be used when retrieving
797 + * PDH counters from the current process. The caller will
798 + own the returned malloc:ed string. NULL if failure.
799 + */
800 +static const char*
801 +getPdhProcessImageName() {
802 + char moduleName[MAX_PATH];
803 + char* processImageName;
804 + char* dotPos;
805 +
806 + // Find our module name and use it to extract the image name used by PDH
807 + DWORD getmfnReturn = GetModuleFileName(NULL, moduleName, sizeof(moduleName));
808 +
809 + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
810 + return NULL;
811 + }
812 +
813 + if (getmfnReturn >= MAX_PATH || 0 == getmfnReturn) {
814 + return NULL;
459 815 }
460 - _snprintf(counter, sizeof(counter)-1, "%s\\%s", getProcessPDHHeader(), time);
461 - return initSingleCounterQuery(&cntProcSystemLoad, counter);
816 +
817 + processImageName = strrchr(moduleName, '\\'); //drop path
818 + processImageName++; //skip slash
819 + dotPos = strrchr(processImageName, '.'); //drop .exe
820 + dotPos[0] = '\0';
821 +
822 + return allocateAndCopy(processImageName);
462 823 }
463 824
464 -/**
465 - * Sets up the supplied MultipleCounterQuery to check on the processors.
466 - * (Comment: Refactor and prettify as with the the SingleCounter queries
467 - * if more MultipleCounterQueries are discovered.)
468 - *
469 - * initPDH() must have been run prior to calling this function.
470 - *
471 - * @param multiQuery a pointer to a MultipleCounterQueryS, will be filled in with
472 - * the necessary info to check the PDH processor counters.
473 - * @return CONFIG_SUCCESSFUL if successful, negative on failure.
474 - */
475 -static int
476 -initProcessorCounters(void) {
477 - char processor[COUNTER_BUF_SIZE]; //'Processor' == #238
478 - char time[COUNTER_BUF_SIZE]; //'Time' == 6
479 - DWORD c_size, i_size;
480 - HQUERY tmpQuery;
481 - DWORD i, p_count;
482 - BOOL error;
483 - char *instances, *tmp;
825 +/*
826 + * Sets up the supplied MultipleCounterQuery to check on the processors via PDH CPU counters.
827 + * TODO: Refactor and prettify as with the the SingleCounter queries
828 + * if more MultipleCounterQueries are discovered/needed.
829 + *
830 + * @param multiCounterCPULoad a pointer to a MultipleCounterQueryS, will be filled in with
831 + * the necessary info to check the PDH processor counters.
832 + * @return 0 if successful, negative on failure.
833 + */
834 +static int
835 +initializeMultipleCounterForCPUs(MultipleCounterQueryP multiCounterCPULoad) {
836 + DWORD cSize = 0;
837 + DWORD iSize = 0;
838 + DWORD pCount;
839 + DWORD index;
840 + char* processor = NULL; //'Processor' == PDH_PROCESSOR_IDX
841 + char* time = NULL; //'Time' == PDH_PROCESSOR_TIME_IDX
842 + char* instances = NULL;
843 + char* tmp;
844 + int retValue = -1;
484 845 PDH_STATUS pdhStat;
485 846
486 - c_size = i_size = 0;
487 - tmpQuery = NULL;
488 - error = false;
489 -
490 - // This __try / __except stuff is there since Windows 2000 beta (or so) sometimes triggered
491 - // an access violation when the user had insufficient privileges to use the performance
492 - // counters. This was previously guarded by a very ugly piece of code which disabled the
493 - // global trap handling in JRockit. Don't know if this really is needed anymore, but otoh,
494 - // if we keep it we don't crash on Win2k beta. /Ihse, 2005-05-30
495 - __try {
496 - if (find_name(PDH_PROCESSOR_IDX, processor, sizeof(processor)-1) < 0) {
497 - return -1;
498 - }
499 - } __except (EXCEPTION_EXECUTE_HANDLER) { // We'll catch all exceptions here.
500 - /* printf("User does not have sufficient privileges to use performance counters"); */
501 - return -1;
847 + if (lookupNameByIndex(PDH_PROCESSOR_IDX, &processor) != 0) {
848 + goto end;
502 849 }
503 850
504 - if (find_name(PDH_PROCESSOR_TIME_IDX, time, sizeof(time)-1) < 0) {
505 - return -1;
851 + if (lookupNameByIndex(PDH_PROCESSOR_TIME_IDX, &time) != 0) {
852 + goto end;
506 853 }
854 +
507 855 //ok, now we have enough to enumerate all processors.
508 - pdhStat = PdhEnumObjectItems_i (
509 - NULL, // reserved
510 - NULL, // local machine
511 - processor, // object to enumerate
512 - NULL, // pass in NULL buffers
513 - &c_size, // and 0 length to get
514 - NULL, // required size
515 - &i_size, // of the buffers in chars
516 - PERF_DETAIL_WIZARD, // counter detail level
517 - 0);
518 - if (pdh_fail(pdhStat)) {
519 - /* printf("could not enumerate processors (1) error=%d", pdhStat); */
520 - return -1;
856 + pdhStat = PdhEnumObjectItems_i(
857 + NULL, // reserved
858 + NULL, // local machine
859 + processor, // object to enumerate
860 + NULL, // pass in NULL buffers
861 + &cSize, // and 0 length to get
862 + NULL, // required size
863 + &iSize, // of the buffers in chars
864 + PERF_DETAIL_WIZARD, // counter detail level
865 + 0);
866 +
867 + if (pdhFail(pdhStat)) {
868 + goto end;
521 869 }
522 870
523 - // use calloc because windows vista does not null terminate the instance names (allthough the docs says it will)
524 - instances = calloc(i_size, 1);
525 - if (instances == NULL) {
526 - /* printf("could not allocate memory (1) %d bytes", i_size); */
527 - error = true;
871 + instances = calloc(iSize, 1);
872 +
873 + if (!instances) {
528 874 goto end;
529 875 }
530 876
531 - c_size = 0;
532 - pdhStat = PdhEnumObjectItems_i (
533 - NULL, // reserved
534 - NULL, // local machine
535 - processor, // object to enumerate
536 - NULL, // pass in NULL buffers
537 - &c_size, // and 0 length to get
538 - instances, // required size
539 - &i_size, // of the buffers in chars
540 - PERF_DETAIL_WIZARD, // counter detail level
541 - 0);
877 + cSize = 0;
542 878
543 - if (pdh_fail(pdhStat)) {
544 - /* printf("could not enumerate processors (2) error=%d", pdhStat); */
545 - error = true;
879 + pdhStat = PdhEnumObjectItems_i(
880 + NULL, // reserved
881 + NULL, // local machine
882 + processor, // object to enumerate
883 + NULL, // pass in NULL buffers
884 + &cSize,
885 + instances, // now allocated to be filled in
886 + &iSize, // and size is known
887 + PERF_DETAIL_WIZARD, // counter detail level
888 + 0);
889 +
890 + if (pdhFail(pdhStat)) {
546 891 goto end;
547 892 }
548 - //count perf count instances.
549 - for (p_count = 0, tmp = instances; *tmp != 0; tmp = &tmp[lstrlen(tmp)+1], p_count++);
550 893
551 - //is this correct for HT?
552 - assert(p_count == num_cpus+1);
894 + // enumerate the Processor instances ("\Processor(0)", "\Processor(1)", ..., "\Processor(_Total)")
895 + for (pCount = 0, tmp = instances; *tmp != '\0'; tmp = &tmp[strlen(tmp)+1], pCount++);
896 +
897 + assert(pCount == numCpus+1);
898 +
899 + //ok, we now have the number of Processor instances - allocate an HCOUNTER for each
900 + multiCounterCPULoad->counters = (HCOUNTER*)malloc(pCount * sizeof(HCOUNTER));
553 901
554 - //ok, have number of perf counters.
555 - multiCounterCPULoad.counters = calloc(p_count, sizeof(HCOUNTER));
556 - if (multiCounterCPULoad.counters == NULL) {
557 - /* printf("could not allocate memory (2) count=%d", p_count); */
558 - error = true;
902 + if (!multiCounterCPULoad->counters) {
559 903 goto end;
560 904 }
561 905
562 - multiCounterCPULoad.noOfCounters = p_count;
906 + multiCounterCPULoad->noOfCounters = pCount;
563 907
564 - if (PdhOpenQuery_i(NULL, 0, &multiCounterCPULoad.query.query) != ERROR_SUCCESS) {
565 - /* printf("could not create query"); */
566 - error = true;
908 + if (openQuery(&multiCounterCPULoad->query.query) != 0) {
567 909 goto end;
568 910 }
569 - //now, fetch the counters.
570 - for (i = 0, tmp = instances; *tmp != '\0'; tmp = &tmp[lstrlen(tmp)+1], i++) {
571 - char counter[2*COUNTER_BUF_SIZE];
572 911
573 - _snprintf(counter, sizeof(counter)-1, "\\%s(%s)\\%s", processor, tmp, time);
912 + // fetch instance and register its corresponding HCOUNTER with the query
913 + for (index = 0, tmp = instances; *tmp != '\0'; tmp = &tmp[strlen(tmp)+1], ++index) {
914 + const char* const fullCounterPath = makeFullCounterPath(processor, time, NULL, tmp);
574 915
575 - if (PdhAddCounter_i(multiCounterCPULoad.query.query, counter, 0, &multiCounterCPULoad.counters[i]) != ERROR_SUCCESS) {
576 - /* printf("error adding processor counter %s", counter); */
577 - error = true;
916 + if (!fullCounterPath) {
578 917 goto end;
579 918 }
580 - }
581 919
582 - free(instances);
583 - instances = NULL;
920 + retValue = addCounter(multiCounterCPULoad->query.query,
921 + fullCounterPath,
922 + &multiCounterCPULoad->counters[index]);
923 +
924 + free((char*)fullCounterPath);
925 +
926 + if (retValue != 0) {
927 + goto end;
928 + }
929 + }
584 930
585 - // Query once to initialize the counters needing at least two queries
931 + // Query once to initialize the counters which require at least two samples
586 932 // (like the % CPU usage) to calculate correctly.
587 - if (PdhCollectQueryData_i(multiCounterCPULoad.query.query) != ERROR_SUCCESS)
588 - error = true;
933 + PdhCollectQueryData_i(multiCounterCPULoad->query.query);
589 934
590 - end:
591 - if (instances != NULL) {
592 - free(instances);
935 + end:
936 + if (processor) {
937 + free(processor);
593 938 }
594 - if (tmpQuery != NULL) {
595 - PdhCloseQuery_i(tmpQuery);
939 +
940 + if (time) {
941 + free(time);
596 942 }
597 - if (error) {
598 - int i;
599 943
600 - if (multiCounterCPULoad.counters != NULL) {
601 - for (i = 0; i < multiCounterCPULoad.noOfCounters; i++) {
602 - if (multiCounterCPULoad.counters[i] != NULL) {
603 - PdhRemoveCounter_i(multiCounterCPULoad.counters[i]);
604 - }
605 - }
606 - free(multiCounterCPULoad.counters[i]);
607 - }
608 - if (multiCounterCPULoad.query.query != NULL) {
609 - PdhCloseQuery_i(multiCounterCPULoad.query.query);
610 - }
611 - memset(&multiCounterCPULoad, 0, sizeof(MultipleCounterQueryS));
612 - return -1;
944 + if (instances) {
945 + free(instances);
613 946 }
614 - return CONFIG_SUCCESSFUL;
947 +
948 + return retValue;
615 949 }
616 950
617 -/**
618 - * Help function that initializes the PDH process header for the JRockit process.
619 - * (You should probably use getProcessPDHHeader() instead!)
620 - *
621 - * initPDH() must have been run prior to calling this function.
622 - *
623 - * @param ebuf the error buffer.
624 - * @param elen the length of the error buffer.
951 +/*
952 + * Dynamically sets up function pointers to the PDH library.
625 953 *
626 - * @return the PDH instance description corresponding to the JVM process.
954 + * @param h HMODULE for the PDH library
955 + * @return 0 on success, negative on failure.
627 956 */
628 -static char*
629 -initProcessPDHHeader(void) {
630 - static char hotspotheader[2*COUNTER_BUF_SIZE];
957 +static int
958 +bindPdhFunctionPointers(HMODULE h) {
959 + assert(h);
960 + assert(GetCurrentThreadId() == initializationLock.owningThread);
631 961
632 - char counter[2*COUNTER_BUF_SIZE];
633 - char processes[COUNTER_BUF_SIZE]; //'Process' == #230
634 - char pid[COUNTER_BUF_SIZE]; //'ID Process' == 784
635 - char module_name[MAX_PATH];
636 - PDH_STATUS pdhStat;
637 - DWORD c_size = 0, i_size = 0;
638 - HQUERY tmpQuery = NULL;
639 - int i, myPid = _getpid();
640 - BOOL error = false;
641 - char *instances, *tmp, *instance_name, *dot_pos;
962 + /* The 'A' at the end means the ANSI (not the UNICODE) vesions of the methods */
963 + PdhAddCounter_i = (PdhAddCounterFunc)GetProcAddress(h, "PdhAddCounterA");
964 + PdhOpenQuery_i = (PdhOpenQueryFunc)GetProcAddress(h, "PdhOpenQueryA");
965 + PdhCloseQuery_i = (PdhCloseQueryFunc)GetProcAddress(h, "PdhCloseQuery");
966 + PdhCollectQueryData_i = (PdhCollectQueryDataFunc)GetProcAddress(h, "PdhCollectQueryData");
967 + PdhGetFormattedCounterValue_i = (PdhGetFormattedCounterValueFunc)GetProcAddress(h, "PdhGetFormattedCounterValue");
968 + PdhEnumObjectItems_i = (PdhEnumObjectItemsFunc)GetProcAddress(h, "PdhEnumObjectItemsA");
969 + PdhRemoveCounter_i = (PdhRemoveCounterFunc)GetProcAddress(h, "PdhRemoveCounter");
970 + PdhLookupPerfNameByIndex_i = (PdhLookupPerfNameByIndexFunc)GetProcAddress(h, "PdhLookupPerfNameByIndexA");
642 971
643 - tmpQuery = NULL;
644 - myPid = _getpid();
645 - error = false;
972 + if (!PdhAddCounter_i || !PdhOpenQuery_i ||
973 + !PdhCloseQuery_i || !PdhCollectQueryData_i ||
974 + !PdhGetFormattedCounterValue_i || !PdhEnumObjectItems_i ||
975 + !PdhRemoveCounter_i || !PdhLookupPerfNameByIndex_i)
976 + {
977 + return -1;
978 + }
979 + return 0;
980 +}
646 981
647 - if (find_name(PDH_PROCESS_IDX, processes, sizeof(processes) - 1) < 0) {
648 - return NULL;
982 +/*
983 + * Returns the counter value as a double for the specified query.
984 + * Will collect the query data and update the counter values as necessary.
985 + *
986 + * @param query the query to update (if needed).
987 + * @param c the counter to read.
988 + * @param value where to store the formatted value.
989 + * @param format the format to use (i.e. PDH_FMT_DOUBLE, PDH_FMT_LONG etc)
990 + * @return 0 if no error
991 + * -1 if PdhCollectQueryData fails
992 + * -2 if PdhGetFormattedCounterValue fails
993 + */
994 +static int
995 +getPerformanceData(UpdateQueryP query, HCOUNTER c, PDH_FMT_COUNTERVALUE* value, DWORD format) {
996 + clock_t now = clock();
997 +
998 + /*
999 + * Need to limit how often we update the query
1000 + * to minimize the Heisenberg effect.
1001 + * (PDH behaves erratically if the counters are
1002 + * queried too often, especially counters that
1003 + * store and use values from two consecutive updates,
1004 + * like cpu load.)
1005 + */
1006 + if (now - query->lastUpdate > MIN_UPDATE_INTERVAL) {
1007 + if (PdhCollectQueryData_i(query->query) != ERROR_SUCCESS) {
1008 + return -1;
1009 + }
1010 + query->lastUpdate = now;
649 1011 }
650 1012
651 - if (find_name(PDH_ID_PROCESS_IDX, pid, sizeof(pid) - 1) < 0) {
652 - return NULL;
1013 + if (PdhGetFormattedCounterValue_i(c, format, NULL, value) != ERROR_SUCCESS) {
1014 + return -2;
653 1015 }
654 - //time is same.
655 1016
656 - c_size = 0;
657 - i_size = 0;
1017 + return 0;
1018 +}
658 1019
659 - pdhStat = PdhEnumObjectItems_i (
660 - NULL, // reserved
661 - NULL, // local machine
662 - processes, // object to enumerate
663 - NULL, // pass in NULL buffers
664 - &c_size, // and 0 length to get
665 - NULL, // required size
666 - &i_size, // of the buffers in chars
667 - PERF_DETAIL_WIZARD, // counter detail level
668 - 0);
669 -
670 - //ok, now we have enough to enumerate all processes
671 - if (pdh_fail(pdhStat)) {
672 - /* printf("Could not enumerate processes (1) error=%d", pdhStat); */
673 - return NULL;
1020 +static int
1021 +allocateAndInitializePdhConstants() {
1022 + const char* pdhLocalizedProcessObject = NULL;
1023 + const char* pdhLocalizedIDProcessCounter = NULL;
1024 + size_t pdhIDProcessCounterFmtLen;
1025 + int currentQueryIndex;
1026 + int retValue = -1;
1027 +
1028 + assert(GetCurrentThreadId() == initializationLock.owningThread);
1029 +
1030 + assert(!pdhProcessImageName);
1031 + pdhProcessImageName = getPdhProcessImageName();
1032 + if (!pdhProcessImageName) {
1033 + goto end;
674 1034 }
675 1035
676 - // use calloc because windows vista does not null terminate the instance names (allthough the docs says it will)
677 - if ((instances = calloc(i_size, 1)) == NULL) {
678 - /* printf("Could not allocate memory %d bytes", i_size); */
679 - error = true;
1036 + pdhLocalizedProcessObject = getPdhLocalizedArtifact(PDH_PROCESS_IDX);
1037 + if (!pdhLocalizedProcessObject) {
680 1038 goto end;
681 1039 }
682 1040
683 - c_size = 0;
1041 + pdhLocalizedIDProcessCounter = getPdhLocalizedArtifact(PDH_ID_PROCESS_IDX);
1042 + if (!pdhLocalizedIDProcessCounter) {
1043 + goto end;
1044 + }
684 1045
685 - pdhStat = PdhEnumObjectItems_i (
686 - NULL, // reserved
687 - NULL, // local machine
688 - processes, // object to enumerate
689 - NULL, // pass in NULL buffers
690 - &c_size, // and 0 length to get
691 - instances, // required size
692 - &i_size, // of the buffers in chars
693 - PERF_DETAIL_WIZARD, // counter detail level
694 - 0);
1046 + assert(!pdhIDProcessCounterFmt);
695 1047
696 - // ok, now we have enough to enumerate all processes
697 - if (pdh_fail(pdhStat)) {
698 - /* printf("Could not enumerate processes (2) error=%d", pdhStat); */
699 - error = true;
1048 + pdhIDProcessCounterFmtLen = strlen(pdhProcessImageName);
1049 + pdhIDProcessCounterFmtLen += strlen(pdhLocalizedProcessObject);
1050 + pdhIDProcessCounterFmtLen += strlen(pdhLocalizedIDProcessCounter);
1051 + pdhIDProcessCounterFmtLen += PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN;
1052 + pdhIDProcessCounterFmtLen += 2; // "%d"
1053 +
1054 + assert(pdhIDProcessCounterFmtLen < MAX_PATH);
1055 + pdhIDProcessCounterFmt = malloc(pdhIDProcessCounterFmtLen + 1);
1056 + if (!pdhIDProcessCounterFmt) {
700 1057 goto end;
701 1058 }
702 1059
703 - if (PdhOpenQuery_i(NULL, 0, &tmpQuery) != ERROR_SUCCESS) {
704 - /* printf("Could not create temporary query"); */
705 - error = true;
1060 + /* "\Process(java#%d)\ID Process" */
1061 + _snprintf(pdhIDProcessCounterFmt,
1062 + pdhIDProcessCounterFmtLen,
1063 + PROCESS_OBJECT_INSTANCE_COUNTER_FMT,
1064 + pdhLocalizedProcessObject,
1065 + pdhProcessImageName,
1066 + "%d",
1067 + pdhLocalizedIDProcessCounter);
1068 +
1069 + pdhIDProcessCounterFmt[pdhIDProcessCounterFmtLen] = '\0';
1070 +
1071 + assert(0 == numberOfJavaProcessesAtInitialization);
1072 + currentQueryIndex = currentQueryIndexForProcess();
1073 + if (-1 == currentQueryIndex) {
706 1074 goto end;
707 1075 }
708 1076
709 - // Find our module name and use it to extract the instance name used by PDH
710 - if (GetModuleFileName(NULL, module_name, MAX_PATH) >= MAX_PATH-1) {
711 - /* printf("Module name truncated"); */
712 - error = true;
713 - goto end;
1077 + numberOfJavaProcessesAtInitialization = currentQueryIndex + 1;
1078 + assert(numberOfJavaProcessesAtInitialization >= 1);
1079 +
1080 + retValue = 0;
1081 +
1082 + end:
1083 +
1084 + if (pdhLocalizedProcessObject) {
1085 + free((char*)pdhLocalizedProcessObject);
714 1086 }
715 - instance_name = strrchr(module_name, '\\'); //drop path
716 - instance_name++; //skip slash
717 - dot_pos = strchr(instance_name, '.'); //drop .exe
718 - dot_pos[0] = '\0';
719 1087
720 - //now, fetch the counters.
721 - for (tmp = instances; *tmp != 0 && !error; tmp = &tmp[lstrlen(tmp)+1]) {
722 - HCOUNTER hc = NULL;
723 - BOOL done = false;
1088 + if (pdhLocalizedIDProcessCounter) {
1089 + free((char*)pdhLocalizedIDProcessCounter);
1090 + }
724 1091
725 - // Skip until we find our own process name
726 - if (strcmp(tmp, instance_name) != 0) {
727 - continue;
728 - }
1092 + return retValue;
1093 +}
729 1094
730 - // iterate over all instance indexes and try to find our own pid
731 - for (i = 0; !done && !error; i++){
732 - PDH_STATUS res;
733 - _snprintf(counter, sizeof(counter)-1, "\\%s(%s#%d)\\%s", processes, tmp, i, pid);
1095 +static void
1096 +deallocatePdhConstants() {
1097 + assert(GetCurrentThreadId() == initializationLock.owningThread);
734 1098
735 - if (PdhAddCounter_i(tmpQuery, counter, 0, &hc) != ERROR_SUCCESS) {
736 - /* printf("Failed to create process id query"); */
737 - error = true;
738 - goto end;
739 - }
1099 + if (pdhProcessImageName) {
1100 + free((char*)pdhProcessImageName);
1101 + pdhProcessImageName = NULL;
1102 + }
740 1103
741 - res = PdhCollectQueryData_i(tmpQuery);
1104 + if (pdhIDProcessCounterFmt) {
1105 + free(pdhIDProcessCounterFmt);
1106 + pdhIDProcessCounterFmt = NULL;
1107 + }
742 1108
743 - if (res == PDH_INVALID_HANDLE) {
744 - /* printf("Failed to query process id"); */
745 - res = -1;
746 - done = true;
747 - } else if (res == PDH_NO_DATA) {
748 - done = true;
749 - } else {
750 - PDH_FMT_COUNTERVALUE cv;
1109 + numberOfJavaProcessesAtInitialization = 0;
1110 +}
751 1111
752 - PdhGetFormattedCounterValue_i(hc, PDH_FMT_LONG, NULL, &cv);
753 - /*
754 - * This check seems to be needed for Win2k SMP boxes, since
755 - * they for some reason don't return PDH_NO_DATA for non existing
756 - * counters.
757 - */
758 - if (cv.CStatus != PDH_CSTATUS_VALID_DATA) {
759 - done = true;
760 - } else if (cv.longValue == myPid) {
761 - _snprintf(hotspotheader, sizeof(hotspotheader)-1, "\\%s(%s#%d)\0", processes, tmp, i);
762 - PdhRemoveCounter_i(hc);
763 - goto end;
1112 +static int
1113 +initializeCPUCounters() {
1114 + SYSTEM_INFO si;
1115 + char* localizedProcessObject;
1116 + char* localizedProcessorTimeCounter;
1117 + int i;
1118 + int retValue = -1;
1119 +
1120 + assert(GetCurrentThreadId() == initializationLock.owningThread);
1121 +
1122 + assert(0 == numCpus);
1123 + GetSystemInfo(&si);
1124 + numCpus = si.dwNumberOfProcessors;
1125 + assert(numCpus >= 1);
1126 +
1127 + /* Initialize the denominator for the jvm load calculations */
1128 + assert(.0 == cpuFactor);
1129 + cpuFactor = numCpus * 100;
1130 +
1131 + if (lookupNameByIndex(PDH_PROCESS_IDX,
1132 + &localizedProcessObject) == 0) {
1133 +
1134 + if (lookupNameByIndex(PDH_PROCESSOR_TIME_IDX,
1135 + &localizedProcessorTimeCounter) == 0) {
1136 +
1137 + assert(processTotalCPULoad);
1138 + assert(pdhProcessImageName);
1139 +
1140 + for (i = 0; i < numberOfJavaProcessesAtInitialization; ++i) {
1141 + char instanceIndexBuffer[32];
1142 + retValue = initializeSingleCounter(&processTotalCPULoad[i],
1143 + localizedProcessObject,
1144 + localizedProcessorTimeCounter,
1145 + pdhProcessImageName,
1146 + itoa(i, instanceIndexBuffer, 10),
1147 + TRUE);
1148 + if (retValue != 0) {
1149 + break;
764 1150 }
765 1151 }
766 - PdhRemoveCounter_i(hc);
1152 + free(localizedProcessorTimeCounter);
767 1153 }
1154 + free(localizedProcessObject);
768 1155 }
769 - end:
770 - if (instances != NULL) {
771 - free(instances);
1156 +
1157 + if (retValue != 0) {
1158 + return -1;
772 1159 }
773 - if (tmpQuery != NULL) {
774 - PdhCloseQuery_i(tmpQuery);
1160 +
1161 + assert(multiCounterCPULoad);
1162 + return initializeMultipleCounterForCPUs(multiCounterCPULoad);
1163 +}
1164 +
1165 +static void
1166 +deallocateCPUCounters() {
1167 + int i;
1168 +
1169 + assert(GetCurrentThreadId() == initializationLock.owningThread);
1170 +
1171 + if (processTotalCPULoad) {
1172 + for (i = 0; i < numberOfJavaProcessesAtInitialization; ++i) {
1173 + destroySingleCounter(&processTotalCPULoad[i]);
1174 + }
1175 + free(processTotalCPULoad);
1176 + processTotalCPULoad = NULL;
775 1177 }
776 - if (error) {
777 - return NULL;
1178 +
1179 + if (multiCounterCPULoad) {
1180 + destroyMultiCounter(multiCounterCPULoad);
1181 + free(multiCounterCPULoad);
1182 + multiCounterCPULoad = NULL;
778 1183 }
779 - return hotspotheader;
1184 +
1185 + cpuFactor = .0;
1186 + numCpus = 0;
780 1187 }
781 1188
782 -/**
783 - * Returns the PDH string prefix identifying the HotSpot process. Use this prefix when getting
784 - * counters from the PDH process object representing HotSpot.
785 - *
786 - * Note: this call may take some time to complete.
787 - *
788 - * @param ebuf error buffer.
789 - * @param elen error buffer length.
1189 +static void
1190 +pdhInitErrorHandler(HMODULE h) {
1191 + assert(GetCurrentThreadId() == initializationLock.owningThread);
1192 +
1193 + deallocatePdhConstants();
1194 +
1195 + if (h) {
1196 + FreeLibrary(h);
1197 + }
1198 +}
1199 +
1200 +/*
1201 + * Helper to initialize the PDH library, function pointers and constants.
790 1202 *
791 - * @return the header to be used when retrieving PDH counters from the HotSpot process.
792 - * Will return NULL if the call failed.
1203 + * @return 0 if successful, negative on failure.
793 1204 */
794 -static char *
795 -getProcessPDHHeader(void) {
796 - static char *processHeader = NULL;
1205 +static int
1206 +pdhInit() {
1207 + static BOOL initialized = FALSE;
1208 + int retValue;
1209 +
1210 + if (initialized) {
1211 + return 0;
1212 + }
1213 +
1214 + retValue = 0;
1215 +
1216 + EnterPdhCriticalSection(&initializationLock); {
1217 + if (!initialized) {
1218 + HMODULE h = NULL;
1219 + if ((h = LoadLibrary("pdh.dll")) == NULL) {
1220 + retValue = -1;
1221 + } else if (bindPdhFunctionPointers(h) < 0) {
1222 + retValue = -1;
1223 + } else if (allocateAndInitializePdhConstants() < 0) {
1224 + retValue = -1;
1225 + }
797 1226
798 - EnterCriticalSection(&processHeaderLock); {
799 - if (processHeader == NULL) {
800 - processHeader = initProcessPDHHeader();
1227 + if (0 == retValue) {
1228 + initialized = TRUE;
1229 + } else {
1230 + pdhInitErrorHandler(h);
1231 + }
801 1232 }
802 - } LeaveCriticalSection(&processHeaderLock);
803 - return processHeader;
804 -}
1233 + } LeavePdhCriticalSection(&initializationLock);
805 1234
806 -int perfInit(void);
1235 + return retValue;
1236 +}
807 1237
808 -double
809 -perfGetCPULoad(int which)
810 -{
811 - PDH_FMT_COUNTERVALUE cv;
812 - HCOUNTER c;
1238 +static int
1239 +allocateCPUCounters() {
1240 + assert(GetCurrentThreadId() == initializationLock.owningThread);
1241 + assert(numberOfJavaProcessesAtInitialization >= 1);
1242 + assert(!processTotalCPULoad);
1243 + assert(!multiCounterCPULoad);
1244 +
1245 + /*
1246 + * Create an array of Process object queries, for each instance
1247 + * up to and including our own (java#0, java#1, java#2, ...).
1248 + */
1249 + processTotalCPULoad = calloc(numberOfJavaProcessesAtInitialization,
1250 + sizeof(SingleCounterQueryS));
813 1251
814 - if (perfInit() < 0) {
815 - // warn?
816 - return -1.0;
1252 + if (!processTotalCPULoad) {
1253 + return -1;
817 1254 }
818 1255
819 - if (multiCounterCPULoad.query.query == NULL) {
820 - // warn?
821 - return -1.0;
1256 + multiCounterCPULoad = calloc(1, sizeof(MultipleCounterQueryS));
1257 +
1258 + if (!multiCounterCPULoad) {
1259 + return -1;
822 1260 }
823 1261
824 - if (which == -1) {
825 - c = multiCounterCPULoad.counters[multiCounterCPULoad.noOfCounters - 1];
826 - } else {
827 - if (which < multiCounterCPULoad.noOfCounters) {
828 - c = multiCounterCPULoad.counters[which];
829 - } else {
830 - return -1.0;
1262 + return 0;
1263 +}
1264 +
1265 +static int
1266 +initializePdhCPUCounters() {
1267 + static BOOL initialized = FALSE;
1268 + int retValue;
1269 +
1270 + if (initialized) {
1271 + return 0;
1272 + }
1273 +
1274 + retValue = 0;
1275 +
1276 + EnterPdhCriticalSection(&initializationLock); {
1277 + if (!initialized) {
1278 + if (pdhInit() < 0) {
1279 + retValue = -1;
1280 + } else if (allocateCPUCounters() < 0) {
1281 + retValue = -1;
1282 + } else if (initializeCPUCounters() < 0) {
1283 + retValue = -1;
1284 + }
1285 +
1286 + if (0 == retValue) {
1287 + initialized = TRUE;
1288 + } else {
1289 + deallocateCPUCounters();
1290 + }
831 1291 }
832 - }
833 - if (getPerformanceData(&multiCounterCPULoad.query, c, &cv, PDH_FMT_DOUBLE ) == CONFIG_SUCCESSFUL) {
834 - return cv.doubleValue / 100;
835 - }
836 - return -1.0;
1292 + } LeavePdhCriticalSection(&initializationLock);
1293 +
1294 + return retValue;
837 1295 }
838 1296
839 -double
840 -perfGetProcessLoad(void)
841 -{
1297 +static int
1298 +perfCPUInit() {
1299 + return initializePdhCPUCounters();
1300 +}
1301 +
1302 +static double
1303 +perfGetProcessCPULoad() {
842 1304 PDH_FMT_COUNTERVALUE cv;
1305 + int currentQueryIndex;
843 1306
844 - if (perfInit() < 0) {
1307 + if (perfCPUInit() < 0) {
845 1308 // warn?
846 1309 return -1.0;
847 1310 }
848 1311
849 - if (cntProcLoad.query.query == NULL) {
850 - // warn?
851 - return -1.0;
852 - }
1312 + currentQueryIndex = getCurrentQueryIndexForProcess();
853 1313
854 - if (getPerformanceData(&cntProcLoad.query, cntProcLoad.counter, &cv, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100) == CONFIG_SUCCESSFUL) {
1314 + if (getPerformanceData(&processTotalCPULoad[currentQueryIndex].query,
1315 + processTotalCPULoad[currentQueryIndex].counter,
1316 + &cv,
1317 + PDH_FMT_DOUBLE | PDH_FMT_NOCAP100) == 0) {
855 1318 double d = cv.doubleValue / cpuFactor;
856 1319 d = min(1, d);
857 1320 d = max(0, d);
858 1321 return d;
859 1322 }
860 1323 return -1.0;
861 1324 }
862 1325
863 -/**
864 - * Helper to initialize the PDH library. Loads the library and sets up the functions.
865 - * Note that once loaded, we will never unload the PDH library.
866 - *
867 - * @return CONFIG_SUCCESSFUL if successful, negative on failure.
868 - */
869 -int
870 -perfInit(void) {
871 - static HMODULE h;
872 - static BOOL running, inited;
873 -
874 - int error;
1326 +static double
1327 +perfGetCPULoad(int which) {
1328 + PDH_FMT_COUNTERVALUE cv;
1329 + HCOUNTER c;
875 1330
876 - if (running) {
877 - return CONFIG_SUCCESSFUL;
1331 + if (perfCPUInit() < 0) {
1332 + // warn?
1333 + return -1.0;
878 1334 }
879 1335
880 - error = CONFIG_SUCCESSFUL;
881 -
882 - // this is double checked locking again, but we try to bypass the worst by
883 - // implicit membar at end of lock.
884 - EnterCriticalSection(&initializationLock); {
885 - if (!inited) {
886 - char buf[64] = "";
887 - SYSTEM_INFO si;
888 -
889 - // CMH. But windows will not care about our affinity when giving
890 - // us measurements. Need the real, raw num cpus.
891 -
892 - GetSystemInfo(&si);
893 - num_cpus = si.dwNumberOfProcessors;
894 - // Initialize the denominator for the jvm load calculations
895 - cpuFactor = num_cpus * 100;
896 -
897 - /**
898 - * Do this dynamically, so we don't fail to start on systems without pdh.
899 - */
900 - if ((h = LoadLibrary("pdh.dll")) == NULL) {
901 - /* printf("Could not load pdh.dll (%d)", GetLastError()); */
902 - error = -2;
903 - } else if (get_functions(h, buf, sizeof(buf)) < 0) {
904 - FreeLibrary(h);
905 - h = NULL;
906 - error = -2;
907 - /* printf("Failed to init pdh functions: %s.\n", buf); */
908 - } else {
909 - if (initProcessorCounters() != 0) {
910 - /* printf("Failed to init system load counters.\n"); */
911 - } else if (initProcLoadCounter() != 0) {
912 - /* printf("Failed to init process load counter.\n"); */
913 - } else if (initProcSystemLoadCounter() != 0) {
914 - /* printf("Failed to init process system load counter.\n"); */
915 - } else {
916 - inited = true;
917 - }
918 - }
1336 + if (-1 == which) {
1337 + c = multiCounterCPULoad->counters[multiCounterCPULoad->noOfCounters - 1];
1338 + } else {
1339 + if (which < multiCounterCPULoad->noOfCounters) {
1340 + c = multiCounterCPULoad->counters[which];
1341 + } else {
1342 + return -1.0;
919 1343 }
920 - } LeaveCriticalSection(&initializationLock);
921 -
922 - if (inited && error == CONFIG_SUCCESSFUL) {
923 - running = true;
924 1344 }
925 -
926 - return error;
1345 + if (getPerformanceData(&multiCounterCPULoad->query, c, &cv, PDH_FMT_DOUBLE ) == 0) {
1346 + return cv.doubleValue / 100;
1347 + }
1348 + return -1.0;
927 1349 }
928 1350
929 1351 JNIEXPORT jdouble JNICALL
930 1352 Java_sun_management_OperatingSystemImpl_getSystemCpuLoad
931 1353 (JNIEnv *env, jobject dummy)
932 1354 {
933 1355 return perfGetCPULoad(-1);
934 1356 }
935 1357
936 1358 JNIEXPORT jdouble JNICALL
937 1359 Java_sun_management_OperatingSystemImpl_getProcessCpuLoad
938 1360 (JNIEnv *env, jobject dummy)
939 1361 {
940 - return perfGetProcessLoad();
1362 + return perfGetProcessCPULoad();
941 1363 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX