1 /* |
|
2 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Oracle designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Oracle in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
22 * or visit www.oracle.com if you need additional information or have any |
|
23 * questions. |
|
24 */ |
|
25 |
|
26 #include "jni.h" |
|
27 #include "jni_util.h" |
|
28 #include "jlong.h" |
|
29 #include "jvm.h" |
|
30 #include "management.h" |
|
31 #include "sun_management_OperatingSystemImpl.h" |
|
32 |
|
33 #include <psapi.h> |
|
34 #include <errno.h> |
|
35 #include <stdlib.h> |
|
36 |
|
37 #include <malloc.h> |
|
38 #pragma warning (push,0) |
|
39 #include <windows.h> |
|
40 #pragma warning (pop) |
|
41 #include <stdio.h> |
|
42 #include <time.h> |
|
43 #include <stdint.h> |
|
44 #include <assert.h> |
|
45 |
|
46 /* Disable warnings due to broken header files from Microsoft... */ |
|
47 #pragma warning(push, 3) |
|
48 #include <pdh.h> |
|
49 #include <pdhmsg.h> |
|
50 #include <process.h> |
|
51 #pragma warning(pop) |
|
52 |
|
53 typedef unsigned __int32 juint; |
|
54 typedef unsigned __int64 julong; |
|
55 |
|
56 static void set_low(jlong* value, jint low) { |
|
57 *value &= (jlong)0xffffffff << 32; |
|
58 *value |= (jlong)(julong)(juint)low; |
|
59 } |
|
60 |
|
61 static void set_high(jlong* value, jint high) { |
|
62 *value &= (jlong)(julong)(juint)0xffffffff; |
|
63 *value |= (jlong)high << 32; |
|
64 } |
|
65 |
|
66 static jlong jlong_from(jint h, jint l) { |
|
67 jlong result = 0; // initialization to avoid warning |
|
68 set_high(&result, h); |
|
69 set_low(&result, l); |
|
70 return result; |
|
71 } |
|
72 |
|
73 static HANDLE main_process; |
|
74 |
|
75 static void perfInit(void); |
|
76 |
|
77 JNIEXPORT void JNICALL |
|
78 Java_sun_management_OperatingSystemImpl_initialize0 |
|
79 (JNIEnv *env, jclass cls) |
|
80 { |
|
81 main_process = GetCurrentProcess(); |
|
82 perfInit(); |
|
83 } |
|
84 |
|
85 JNIEXPORT jlong JNICALL |
|
86 Java_sun_management_OperatingSystemImpl_getCommittedVirtualMemorySize0 |
|
87 (JNIEnv *env, jobject mbean) |
|
88 { |
|
89 PROCESS_MEMORY_COUNTERS pmc; |
|
90 if (GetProcessMemoryInfo(main_process, &pmc, sizeof(PROCESS_MEMORY_COUNTERS)) == 0) { |
|
91 return (jlong)-1L; |
|
92 } else { |
|
93 return (jlong) pmc.PagefileUsage; |
|
94 } |
|
95 } |
|
96 |
|
97 JNIEXPORT jlong JNICALL |
|
98 Java_sun_management_OperatingSystemImpl_getTotalSwapSpaceSize0 |
|
99 (JNIEnv *env, jobject mbean) |
|
100 { |
|
101 MEMORYSTATUSEX ms; |
|
102 ms.dwLength = sizeof(ms); |
|
103 GlobalMemoryStatusEx(&ms); |
|
104 return (jlong) ms.ullTotalPageFile; |
|
105 } |
|
106 |
|
107 JNIEXPORT jlong JNICALL |
|
108 Java_sun_management_OperatingSystemImpl_getFreeSwapSpaceSize0 |
|
109 (JNIEnv *env, jobject mbean) |
|
110 { |
|
111 MEMORYSTATUSEX ms; |
|
112 ms.dwLength = sizeof(ms); |
|
113 GlobalMemoryStatusEx(&ms); |
|
114 return (jlong) ms.ullAvailPageFile; |
|
115 } |
|
116 |
|
117 JNIEXPORT jlong JNICALL |
|
118 Java_sun_management_OperatingSystemImpl_getProcessCpuTime0 |
|
119 (JNIEnv *env, jobject mbean) |
|
120 { |
|
121 |
|
122 FILETIME process_creation_time, process_exit_time, |
|
123 process_user_time, process_kernel_time; |
|
124 |
|
125 // Using static variables declared above |
|
126 // Units are 100-ns intervals. Convert to ns. |
|
127 GetProcessTimes(main_process, &process_creation_time, |
|
128 &process_exit_time, |
|
129 &process_kernel_time, &process_user_time); |
|
130 return (jlong_from(process_user_time.dwHighDateTime, |
|
131 process_user_time.dwLowDateTime) + |
|
132 jlong_from(process_kernel_time.dwHighDateTime, |
|
133 process_kernel_time.dwLowDateTime)) * 100; |
|
134 } |
|
135 |
|
136 JNIEXPORT jlong JNICALL |
|
137 Java_sun_management_OperatingSystemImpl_getFreePhysicalMemorySize0 |
|
138 (JNIEnv *env, jobject mbean) |
|
139 { |
|
140 MEMORYSTATUSEX ms; |
|
141 ms.dwLength = sizeof(ms); |
|
142 GlobalMemoryStatusEx(&ms); |
|
143 return (jlong) ms.ullAvailPhys; |
|
144 } |
|
145 |
|
146 JNIEXPORT jlong JNICALL |
|
147 Java_sun_management_OperatingSystemImpl_getTotalPhysicalMemorySize0 |
|
148 (JNIEnv *env, jobject mbean) |
|
149 { |
|
150 MEMORYSTATUSEX ms; |
|
151 ms.dwLength = sizeof(ms); |
|
152 GlobalMemoryStatusEx(&ms); |
|
153 return (jlong) ms.ullTotalPhys; |
|
154 } |
|
155 |
|
156 /* Performance Data Helper API (PDH) support */ |
|
157 |
|
158 typedef PDH_STATUS (WINAPI *PdhAddCounterFunc)( |
|
159 HQUERY hQuery, |
|
160 LPCSTR szFullCounterPath, |
|
161 DWORD dwUserData, |
|
162 HCOUNTER *phCounter |
|
163 ); |
|
164 typedef PDH_STATUS (WINAPI *PdhOpenQueryFunc)( |
|
165 LPCWSTR szDataSource, |
|
166 DWORD dwUserData, |
|
167 HQUERY *phQuery |
|
168 ); |
|
169 typedef PDH_STATUS (WINAPI *PdhCollectQueryDataFunc)( |
|
170 HQUERY hQuery |
|
171 ); |
|
172 |
|
173 typedef PDH_STATUS (WINAPI *PdhEnumObjectItemsFunc)( |
|
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 ); |
|
184 typedef PDH_STATUS (WINAPI *PdhRemoveCounterFunc)( |
|
185 HCOUNTER hCounter |
|
186 ); |
|
187 typedef PDH_STATUS (WINAPI *PdhLookupPerfNameByIndexFunc)( |
|
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 ); |
|
203 |
|
204 static PdhAddCounterFunc PdhAddCounter_i; |
|
205 static PdhOpenQueryFunc PdhOpenQuery_i; |
|
206 static PdhCloseQueryFunc PdhCloseQuery_i; |
|
207 static PdhCollectQueryDataFunc PdhCollectQueryData_i; |
|
208 static PdhGetFormattedCounterValueFunc PdhGetFormattedCounterValue_i; |
|
209 static PdhEnumObjectItemsFunc PdhEnumObjectItems_i; |
|
210 static PdhRemoveCounterFunc PdhRemoveCounter_i; |
|
211 static PdhLookupPerfNameByIndexFunc PdhLookupPerfNameByIndex_i; |
|
212 |
|
213 /* |
|
214 * Struct for PDH queries. |
|
215 */ |
|
216 typedef struct { |
|
217 HQUERY query; |
|
218 uint64_t lastUpdate; // Last time query was updated (ticks) |
|
219 } UpdateQueryS, *UpdateQueryP; |
|
220 |
|
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. |
|
226 */ |
|
227 typedef struct { |
|
228 UpdateQueryS query; |
|
229 HCOUNTER* counters; |
|
230 int noOfCounters; |
|
231 } MultipleCounterQueryS, *MultipleCounterQueryP; |
|
232 |
|
233 /* |
|
234 * Struct for a PDH query with a single counter. |
|
235 */ |
|
236 typedef struct { |
|
237 UpdateQueryS query; |
|
238 HCOUNTER counter; |
|
239 } SingleCounterQueryS, *SingleCounterQueryP; |
|
240 |
|
241 |
|
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] |
|
288 */ |
|
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" */ |
|
304 |
|
305 static int numberOfJavaProcessesAtInitialization = 0; |
|
306 |
|
307 /* |
|
308 * Currently used CPU queries/counters and variables |
|
309 */ |
|
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; |
|
322 } |
|
323 |
|
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. |
|
349 * |
|
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. |
|
353 */ |
|
354 static int |
|
355 lookupNameByIndex(DWORD index, char** ppBuffer) { |
|
356 DWORD size; |
|
357 |
|
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; |
|
375 return -1; |
|
376 } |
|
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; |
|
383 } |
|
384 |
|
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); |
|
430 |
|
431 fullCounterPathLen += strlen(instance); |
|
432 |
|
433 fullCounterPath = malloc(fullCounterPathLen + 1); |
|
434 |
|
435 if (!fullCounterPath) { |
|
436 return NULL; |
|
437 } |
|
438 |
|
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 } |
|
488 } |
|
489 |
|
490 fullCounterPath[fullCounterPathLen] = '\0'; |
|
491 |
|
492 return fullCounterPath; |
|
493 } |
|
494 |
|
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. |
|
499 * |
|
500 * @param pdhArtifactIndex PDH index |
|
501 * @return malloc:ed string representation |
|
502 * of the requested pdh artifact (localized). |
|
503 * NULL on failure. |
|
504 */ |
|
505 static const char* |
|
506 getPdhLocalizedArtifact(DWORD pdhArtifactIndex) { |
|
507 char* pdhLocalizedArtifactString; |
|
508 |
|
509 if (lookupNameByIndex(pdhArtifactIndex, |
|
510 &pdhLocalizedArtifactString) != 0) { |
|
511 return NULL; |
|
512 } |
|
513 |
|
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 } |
|
528 |
|
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) { |
|
556 return -1; |
|
557 } |
|
558 |
|
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) { |
|
574 return -1; |
|
575 } |
|
576 |
|
577 return 0; |
|
578 } |
|
579 |
|
580 /* |
|
581 * Sets up the supplied SingleCounterQuery to listen for the specified counter. |
|
582 * |
|
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. |
|
586 */ |
|
587 static int |
|
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; |
|
598 } |
|
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; |
|
653 } |
|
654 free((char*)fullCounterPath); |
|
655 } |
|
656 |
|
657 return retValue; |
|
658 } |
|
659 |
|
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 |
|
696 * |
|
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). |
|
701 */ |
|
702 static int |
|
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; |
|
719 |
|
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 } |
|
753 } |
|
754 |
|
755 pdhCleanup(&tmpQuery, &handleCounter); |
|
756 |
|
757 return retValue; |
|
758 } |
|
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 */ |
|
774 static int |
|
775 getCurrentQueryIndexForProcess() { |
|
776 int currentQueryIndex = currentQueryIndexForProcess(); |
|
777 |
|
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; |
|
815 } |
|
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); |
|
823 } |
|
824 |
|
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; |
|
845 PDH_STATUS pdhStat; |
|
846 |
|
847 if (lookupNameByIndex(PDH_PROCESSOR_IDX, &processor) != 0) { |
|
848 goto end; |
|
849 } |
|
850 |
|
851 if (lookupNameByIndex(PDH_PROCESSOR_TIME_IDX, &time) != 0) { |
|
852 goto end; |
|
853 } |
|
854 |
|
855 //ok, now we have enough to enumerate all processors. |
|
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; |
|
869 } |
|
870 |
|
871 instances = calloc(iSize, 1); |
|
872 |
|
873 if (!instances) { |
|
874 goto end; |
|
875 } |
|
876 |
|
877 cSize = 0; |
|
878 |
|
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)) { |
|
891 goto end; |
|
892 } |
|
893 |
|
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)); |
|
901 |
|
902 if (!multiCounterCPULoad->counters) { |
|
903 goto end; |
|
904 } |
|
905 |
|
906 multiCounterCPULoad->noOfCounters = pCount; |
|
907 |
|
908 if (openQuery(&multiCounterCPULoad->query.query) != 0) { |
|
909 goto end; |
|
910 } |
|
911 |
|
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); |
|
915 |
|
916 if (!fullCounterPath) { |
|
917 goto end; |
|
918 } |
|
919 |
|
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 } |
|
930 |
|
931 // Query once to initialize the counters which require at least two samples |
|
932 // (like the % CPU usage) to calculate correctly. |
|
933 PdhCollectQueryData_i(multiCounterCPULoad->query.query); |
|
934 |
|
935 end: |
|
936 if (processor) { |
|
937 free(processor); |
|
938 } |
|
939 |
|
940 if (time) { |
|
941 free(time); |
|
942 } |
|
943 |
|
944 if (instances) { |
|
945 free(instances); |
|
946 } |
|
947 |
|
948 return retValue; |
|
949 } |
|
950 |
|
951 /* |
|
952 * Dynamically sets up function pointers to the PDH library. |
|
953 * |
|
954 * @param h HMODULE for the PDH library |
|
955 * @return 0 on success, negative on failure. |
|
956 */ |
|
957 static int |
|
958 bindPdhFunctionPointers(HMODULE h) { |
|
959 assert(h); |
|
960 assert(GetCurrentThreadId() == initializationLock.owningThread); |
|
961 |
|
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"); |
|
971 |
|
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 } |
|
981 |
|
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; |
|
1011 } |
|
1012 |
|
1013 if (PdhGetFormattedCounterValue_i(c, format, NULL, value) != ERROR_SUCCESS) { |
|
1014 return -2; |
|
1015 } |
|
1016 |
|
1017 return 0; |
|
1018 } |
|
1019 |
|
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; |
|
1034 } |
|
1035 |
|
1036 pdhLocalizedProcessObject = getPdhLocalizedArtifact(PDH_PROCESS_IDX); |
|
1037 if (!pdhLocalizedProcessObject) { |
|
1038 goto end; |
|
1039 } |
|
1040 |
|
1041 pdhLocalizedIDProcessCounter = getPdhLocalizedArtifact(PDH_ID_PROCESS_IDX); |
|
1042 if (!pdhLocalizedIDProcessCounter) { |
|
1043 goto end; |
|
1044 } |
|
1045 |
|
1046 assert(!pdhIDProcessCounterFmt); |
|
1047 |
|
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) { |
|
1057 goto end; |
|
1058 } |
|
1059 |
|
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) { |
|
1074 goto end; |
|
1075 } |
|
1076 |
|
1077 numberOfJavaProcessesAtInitialization = currentQueryIndex + 1; |
|
1078 assert(numberOfJavaProcessesAtInitialization >= 1); |
|
1079 |
|
1080 retValue = 0; |
|
1081 |
|
1082 end: |
|
1083 |
|
1084 if (pdhLocalizedProcessObject) { |
|
1085 free((char*)pdhLocalizedProcessObject); |
|
1086 } |
|
1087 |
|
1088 if (pdhLocalizedIDProcessCounter) { |
|
1089 free((char*)pdhLocalizedIDProcessCounter); |
|
1090 } |
|
1091 |
|
1092 return retValue; |
|
1093 } |
|
1094 |
|
1095 static void |
|
1096 deallocatePdhConstants() { |
|
1097 assert(GetCurrentThreadId() == initializationLock.owningThread); |
|
1098 |
|
1099 if (pdhProcessImageName) { |
|
1100 free((char*)pdhProcessImageName); |
|
1101 pdhProcessImageName = NULL; |
|
1102 } |
|
1103 |
|
1104 if (pdhIDProcessCounterFmt) { |
|
1105 free(pdhIDProcessCounterFmt); |
|
1106 pdhIDProcessCounterFmt = NULL; |
|
1107 } |
|
1108 |
|
1109 numberOfJavaProcessesAtInitialization = 0; |
|
1110 } |
|
1111 |
|
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; |
|
1150 } |
|
1151 } |
|
1152 free(localizedProcessorTimeCounter); |
|
1153 } |
|
1154 free(localizedProcessObject); |
|
1155 } |
|
1156 |
|
1157 if (retValue != 0) { |
|
1158 return -1; |
|
1159 } |
|
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; |
|
1177 } |
|
1178 |
|
1179 if (multiCounterCPULoad) { |
|
1180 destroyMultiCounter(multiCounterCPULoad); |
|
1181 free(multiCounterCPULoad); |
|
1182 multiCounterCPULoad = NULL; |
|
1183 } |
|
1184 |
|
1185 cpuFactor = .0; |
|
1186 numCpus = 0; |
|
1187 } |
|
1188 |
|
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. |
|
1202 * |
|
1203 * @return 0 if successful, negative on failure. |
|
1204 */ |
|
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 } |
|
1226 |
|
1227 if (0 == retValue) { |
|
1228 initialized = TRUE; |
|
1229 } else { |
|
1230 pdhInitErrorHandler(h); |
|
1231 } |
|
1232 } |
|
1233 } LeavePdhCriticalSection(&initializationLock); |
|
1234 |
|
1235 return retValue; |
|
1236 } |
|
1237 |
|
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)); |
|
1251 |
|
1252 if (!processTotalCPULoad) { |
|
1253 return -1; |
|
1254 } |
|
1255 |
|
1256 multiCounterCPULoad = calloc(1, sizeof(MultipleCounterQueryS)); |
|
1257 |
|
1258 if (!multiCounterCPULoad) { |
|
1259 return -1; |
|
1260 } |
|
1261 |
|
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 } |
|
1291 } |
|
1292 } LeavePdhCriticalSection(&initializationLock); |
|
1293 |
|
1294 return retValue; |
|
1295 } |
|
1296 |
|
1297 static int |
|
1298 perfCPUInit() { |
|
1299 return initializePdhCPUCounters(); |
|
1300 } |
|
1301 |
|
1302 static double |
|
1303 perfGetProcessCPULoad() { |
|
1304 PDH_FMT_COUNTERVALUE cv; |
|
1305 int currentQueryIndex; |
|
1306 |
|
1307 if (perfCPUInit() < 0) { |
|
1308 // warn? |
|
1309 return -1.0; |
|
1310 } |
|
1311 |
|
1312 currentQueryIndex = getCurrentQueryIndexForProcess(); |
|
1313 |
|
1314 if (getPerformanceData(&processTotalCPULoad[currentQueryIndex].query, |
|
1315 processTotalCPULoad[currentQueryIndex].counter, |
|
1316 &cv, |
|
1317 PDH_FMT_DOUBLE | PDH_FMT_NOCAP100) == 0) { |
|
1318 double d = cv.doubleValue / cpuFactor; |
|
1319 d = min(1, d); |
|
1320 d = max(0, d); |
|
1321 return d; |
|
1322 } |
|
1323 return -1.0; |
|
1324 } |
|
1325 |
|
1326 static double |
|
1327 perfGetCPULoad(int which) { |
|
1328 PDH_FMT_COUNTERVALUE cv; |
|
1329 HCOUNTER c; |
|
1330 |
|
1331 if (perfCPUInit() < 0) { |
|
1332 // warn? |
|
1333 return -1.0; |
|
1334 } |
|
1335 |
|
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; |
|
1343 } |
|
1344 } |
|
1345 if (getPerformanceData(&multiCounterCPULoad->query, c, &cv, PDH_FMT_DOUBLE ) == 0) { |
|
1346 return cv.doubleValue / 100; |
|
1347 } |
|
1348 return -1.0; |
|
1349 } |
|
1350 |
|
1351 JNIEXPORT jdouble JNICALL |
|
1352 Java_sun_management_OperatingSystemImpl_getSystemCpuLoad0 |
|
1353 (JNIEnv *env, jobject dummy) |
|
1354 { |
|
1355 return perfGetCPULoad(-1); |
|
1356 } |
|
1357 |
|
1358 JNIEXPORT jdouble JNICALL |
|
1359 Java_sun_management_OperatingSystemImpl_getProcessCpuLoad0 |
|
1360 (JNIEnv *env, jobject dummy) |
|
1361 { |
|
1362 return perfGetProcessCPULoad(); |
|
1363 } |
|