130 // dwTotalVirtual, dwAvailVirtual, |
151 // dwTotalVirtual, dwAvailVirtual, |
131 // dwMemoryLoad (% of memory in use) |
152 // dwMemoryLoad (% of memory in use) |
132 GlobalMemoryStatus(&ms); |
153 GlobalMemoryStatus(&ms); |
133 return ms.dwTotalPhys; |
154 return ms.dwTotalPhys; |
134 } |
155 } |
|
156 |
|
157 // Seems WinXP PDH returns PDH_MORE_DATA whenever we send in a NULL buffer. |
|
158 // Let's just ignore it, since we make sure we have enough buffer anyway. |
|
159 static int |
|
160 pdh_fail(PDH_STATUS pdhStat) { |
|
161 return pdhStat != ERROR_SUCCESS && pdhStat != PDH_MORE_DATA; |
|
162 } |
|
163 |
|
164 // INFO: Using PDH APIs Correctly in a Localized Language (Q287159) |
|
165 // http://support.microsoft.com/default.aspx?scid=kb;EN-US;q287159 |
|
166 // The index value for the base system counters and objects like processor, |
|
167 // process, thread, memory, and so forth are always the same irrespective |
|
168 // of the localized version of the operating system or service pack installed. |
|
169 #define PDH_PROCESSOR_IDX ((DWORD) 238) |
|
170 #define PDH_PROCESSOR_TIME_IDX ((DWORD) 6) |
|
171 #define PDH_PRIV_PROCESSOR_TIME_IDX ((DWORD) 144) |
|
172 #define PDH_PROCESS_IDX ((DWORD) 230) |
|
173 #define PDH_ID_PROCESS_IDX ((DWORD) 784) |
|
174 #define PDH_CONTEXT_SWITCH_RATE_IDX ((DWORD) 146) |
|
175 #define PDH_SYSTEM_IDX ((DWORD) 2) |
|
176 #define PDH_VIRTUAL_BYTES_IDX ((DWORD) 174) |
|
177 |
|
178 typedef PDH_STATUS (WINAPI *PdhAddCounterFunc)( |
|
179 HQUERY hQuery, |
|
180 LPCSTR szFullCounterPath, |
|
181 DWORD dwUserData, |
|
182 HCOUNTER *phCounter |
|
183 ); |
|
184 typedef PDH_STATUS (WINAPI *PdhOpenQueryFunc)( |
|
185 LPCWSTR szDataSource, |
|
186 DWORD dwUserData, |
|
187 HQUERY *phQuery |
|
188 ); |
|
189 typedef DWORD (WINAPI *PdhCloseQueryFunc)( |
|
190 HQUERY hQuery |
|
191 ); |
|
192 typedef PDH_STATUS (WINAPI *PdhCollectQueryDataFunc)( |
|
193 HQUERY hQuery |
|
194 ); |
|
195 typedef DWORD (WINAPI *PdhGetFormattedCounterValueFunc)( |
|
196 HCOUNTER hCounter, |
|
197 DWORD dwFormat, |
|
198 LPDWORD lpdwType, |
|
199 PPDH_FMT_COUNTERVALUE pValue |
|
200 ); |
|
201 typedef PDH_STATUS (WINAPI *PdhEnumObjectItemsFunc)( |
|
202 LPCTSTR szDataSource, |
|
203 LPCTSTR szMachineName, |
|
204 LPCTSTR szObjectName, |
|
205 LPTSTR mszCounterList, |
|
206 LPDWORD pcchCounterListLength, |
|
207 LPTSTR mszInstanceList, |
|
208 LPDWORD pcchInstanceListLength, |
|
209 DWORD dwDetailLevel, |
|
210 DWORD dwFlags |
|
211 ); |
|
212 typedef PDH_STATUS (WINAPI *PdhRemoveCounterFunc)( |
|
213 HCOUNTER hCounter |
|
214 ); |
|
215 typedef PDH_STATUS (WINAPI *PdhLookupPerfNameByIndexFunc)( |
|
216 LPCSTR szMachineName, |
|
217 DWORD dwNameIndex, |
|
218 LPSTR szNameBuffer, |
|
219 LPDWORD pcchNameBufferSize |
|
220 ); |
|
221 typedef PDH_STATUS (WINAPI *PdhMakeCounterPathFunc)( |
|
222 PDH_COUNTER_PATH_ELEMENTS *pCounterPathElements, |
|
223 LPTSTR szFullPathBuffer, |
|
224 LPDWORD pcchBufferSize, |
|
225 DWORD dwFlags |
|
226 ); |
|
227 |
|
228 static PdhAddCounterFunc PdhAddCounter_i; |
|
229 static PdhOpenQueryFunc PdhOpenQuery_i; |
|
230 static PdhCloseQueryFunc PdhCloseQuery_i; |
|
231 static PdhCollectQueryDataFunc PdhCollectQueryData_i; |
|
232 static PdhGetFormattedCounterValueFunc PdhGetFormattedCounterValue_i; |
|
233 static PdhEnumObjectItemsFunc PdhEnumObjectItems_i; |
|
234 static PdhRemoveCounterFunc PdhRemoveCounter_i; |
|
235 static PdhLookupPerfNameByIndexFunc PdhLookupPerfNameByIndex_i; |
|
236 static PdhMakeCounterPathFunc PdhMakeCounterPath_i; |
|
237 |
|
238 static HANDLE thisProcess; |
|
239 static double cpuFactor; |
|
240 static DWORD num_cpus; |
|
241 |
|
242 #define FT2JLONG(X) ((((jlong)X.dwHighDateTime) << 32) | ((jlong)X.dwLowDateTime)) |
|
243 #define COUNTER_BUF_SIZE 256 |
|
244 // Min time between query updates. |
|
245 #define MIN_UPDATE_INTERVAL 500 |
|
246 #define CONFIG_SUCCESSFUL 0 |
|
247 |
|
248 /** |
|
249 * Struct for PDH queries. |
|
250 */ |
|
251 typedef struct { |
|
252 HQUERY query; |
|
253 uint64_t lastUpdate; // Last time query was updated (current millis). |
|
254 } UpdateQueryS, *UpdateQueryP; |
|
255 |
|
256 /** |
|
257 * Struct for the processor load counters. |
|
258 */ |
|
259 typedef struct { |
|
260 UpdateQueryS query; |
|
261 HCOUNTER* counters; |
|
262 int noOfCounters; |
|
263 } MultipleCounterQueryS, *MultipleCounterQueryP; |
|
264 |
|
265 /** |
|
266 * Struct for the jvm process load counter. |
|
267 */ |
|
268 typedef struct { |
|
269 UpdateQueryS query; |
|
270 HCOUNTER counter; |
|
271 } SingleCounterQueryS, *SingleCounterQueryP; |
|
272 |
|
273 static char* getProcessPDHHeader(void); |
|
274 |
|
275 /** |
|
276 * Currently available counters. |
|
277 */ |
|
278 static SingleCounterQueryS cntCtxtSwitchRate; |
|
279 static SingleCounterQueryS cntVirtualSize; |
|
280 static SingleCounterQueryS cntProcLoad; |
|
281 static SingleCounterQueryS cntProcSystemLoad; |
|
282 static MultipleCounterQueryS multiCounterCPULoad; |
|
283 |
|
284 static CRITICAL_SECTION processHeaderLock; |
|
285 static CRITICAL_SECTION initializationLock; |
|
286 |
|
287 /** |
|
288 * Initialize the perf module at startup. |
|
289 */ |
|
290 int |
|
291 perfiInit(void) |
|
292 { |
|
293 InitializeCriticalSection(&processHeaderLock); |
|
294 InitializeCriticalSection(&initializationLock); |
|
295 return 0; |
|
296 } |
|
297 |
|
298 /** |
|
299 * Dynamically sets up function pointers to the PDH library. |
|
300 * |
|
301 * @return CONFIG_SUCCESSFUL on success, negative on failure. |
|
302 */ |
|
303 static int |
|
304 get_functions(HMODULE h, char *ebuf, size_t elen) { |
|
305 // The 'A' at the end means the ANSI (not the UNICODE) vesions of the methods |
|
306 PdhAddCounter_i = (PdhAddCounterFunc)GetProcAddress(h, "PdhAddCounterA"); |
|
307 PdhOpenQuery_i = (PdhOpenQueryFunc)GetProcAddress(h, "PdhOpenQueryA"); |
|
308 PdhCloseQuery_i = (PdhCloseQueryFunc)GetProcAddress(h, "PdhCloseQuery"); |
|
309 PdhCollectQueryData_i = (PdhCollectQueryDataFunc)GetProcAddress(h, "PdhCollectQueryData"); |
|
310 PdhGetFormattedCounterValue_i = (PdhGetFormattedCounterValueFunc)GetProcAddress(h, "PdhGetFormattedCounterValue"); |
|
311 PdhEnumObjectItems_i = (PdhEnumObjectItemsFunc)GetProcAddress(h, "PdhEnumObjectItemsA"); |
|
312 PdhRemoveCounter_i = (PdhRemoveCounterFunc)GetProcAddress(h, "PdhRemoveCounter"); |
|
313 PdhLookupPerfNameByIndex_i = (PdhLookupPerfNameByIndexFunc)GetProcAddress(h, "PdhLookupPerfNameByIndexA"); |
|
314 PdhMakeCounterPath_i = (PdhMakeCounterPathFunc)GetProcAddress(h, "PdhMakeCounterPathA"); |
|
315 |
|
316 if (PdhAddCounter_i == NULL || PdhOpenQuery_i == NULL || |
|
317 PdhCloseQuery_i == NULL || PdhCollectQueryData_i == NULL || |
|
318 PdhGetFormattedCounterValue_i == NULL || PdhEnumObjectItems_i == NULL || |
|
319 PdhRemoveCounter_i == NULL || PdhLookupPerfNameByIndex_i == NULL || PdhMakeCounterPath_i == NULL) |
|
320 { |
|
321 _snprintf(ebuf, elen, "Required method could not be found."); |
|
322 return -1; |
|
323 } |
|
324 return CONFIG_SUCCESSFUL; |
|
325 } |
|
326 |
|
327 /** |
|
328 * Returns the counter value as a double for the specified query. |
|
329 * Will collect the query data and update the counter values as necessary. |
|
330 * |
|
331 * @param query the query to update (if needed). |
|
332 * @param c the counter to read. |
|
333 * @param value where to store the formatted value. |
|
334 * @param format the format to use (i.e. PDH_FMT_DOUBLE, PDH_FMT_LONG etc) |
|
335 * @return CONFIG_SUCCESSFUL if no error |
|
336 * -1 if PdhCollectQueryData fails |
|
337 * -2 if PdhGetFormattedCounterValue fails |
|
338 */ |
|
339 static int |
|
340 getPerformanceData(UpdateQueryP query, HCOUNTER c, PDH_FMT_COUNTERVALUE* value, DWORD format) { |
|
341 clock_t now; |
|
342 now = clock(); |
|
343 |
|
344 // Need to limit how often we update the query |
|
345 // to mimise the heisenberg effect. |
|
346 // (PDH behaves erratically if the counters are |
|
347 // queried too often, especially counters that |
|
348 // store and use values from two consecutive updates, |
|
349 // like cpu load.) |
|
350 if (now - query->lastUpdate > MIN_UPDATE_INTERVAL) { |
|
351 if (PdhCollectQueryData_i(query->query) != ERROR_SUCCESS) { |
|
352 return -1; |
|
353 } |
|
354 query->lastUpdate = now; |
|
355 } |
|
356 |
|
357 if (PdhGetFormattedCounterValue_i(c, format, NULL, value) != ERROR_SUCCESS) { |
|
358 return -2; |
|
359 } |
|
360 return CONFIG_SUCCESSFUL; |
|
361 } |
|
362 |
|
363 /** |
|
364 * Places the resolved counter name of the counter at the specified index in the |
|
365 * supplied buffer. There must be enough space in the buffer to hold the counter name. |
|
366 * |
|
367 * @param index the counter index as specified in the registry. |
|
368 * @param buf the buffer in which to place the counter name. |
|
369 * @param size the size of the counter name buffer. |
|
370 * @param ebuf the error message buffer. |
|
371 * @param elen the length of the error buffer. |
|
372 * @return CONFIG_SUCCESSFUL if successful, negative on failure. |
|
373 */ |
|
374 static int |
|
375 find_name(DWORD index, char *buf, DWORD size) { |
|
376 PDH_STATUS res; |
|
377 |
|
378 if ((res = PdhLookupPerfNameByIndex_i(NULL, index, buf, &size)) != ERROR_SUCCESS) { |
|
379 |
|
380 /* printf("Could not open counter %d: error=0x%08x", index, res); */ |
|
381 /* if (res == PDH_CSTATUS_NO_MACHINE) { */ |
|
382 /* printf("User probably does not have sufficient privileges to use"); */ |
|
383 /* printf("performance counters. If you are running on Windows 2003"); */ |
|
384 /* printf("or Windows Vista, make sure the user is in the"); */ |
|
385 /* printf("Performance Logs user group."); */ |
|
386 /* } */ |
|
387 return -1; |
|
388 } |
|
389 |
|
390 if (size == 0) { |
|
391 /* printf("Failed to get counter name for %d: empty string", index); */ |
|
392 return -1; |
|
393 } |
|
394 |
|
395 // windows vista does not null-terminate the string (allthough the docs says it will) |
|
396 buf[size - 1] = '\0'; |
|
397 return CONFIG_SUCCESSFUL; |
|
398 } |
|
399 |
|
400 /** |
|
401 * Sets up the supplied SingleCounterQuery to listen for the specified counter. |
|
402 * initPDH() must have been run prior to calling this function! |
|
403 * |
|
404 * @param counterQuery the counter query to set up. |
|
405 * @param counterString the string specifying the path to the counter. |
|
406 * @param ebuf the error buffer. |
|
407 * @param elen the length of the error buffer. |
|
408 * @returns CONFIG_SUCCESSFUL if successful, negative on failure. |
|
409 */ |
|
410 static int |
|
411 initSingleCounterQuery(SingleCounterQueryP counterQuery, char *counterString) { |
|
412 if (PdhOpenQuery_i(NULL, 0, &counterQuery->query.query) != ERROR_SUCCESS) { |
|
413 /* printf("Could not open query for %s", counterString); */ |
|
414 return -1; |
|
415 } |
|
416 if (PdhAddCounter_i(counterQuery->query.query, counterString, 0, &counterQuery->counter) != ERROR_SUCCESS) { |
|
417 /* printf("Could not add counter %s for query", counterString); */ |
|
418 if (counterQuery->counter != NULL) { |
|
419 PdhRemoveCounter_i(counterQuery->counter); |
|
420 } |
|
421 if (counterQuery->query.query != NULL) { |
|
422 PdhCloseQuery_i(counterQuery->query.query); |
|
423 } |
|
424 memset(counterQuery, 0, sizeof(SingleCounterQueryS)); |
|
425 return -1; |
|
426 } |
|
427 return CONFIG_SUCCESSFUL; |
|
428 } |
|
429 |
|
430 /** |
|
431 * Sets up the supplied SingleCounterQuery to listen for the time spent |
|
432 * by the HotSpot process. |
|
433 * |
|
434 * @param counterQuery the counter query to set up as a process counter. |
|
435 * @param ebuf the error buffer. |
|
436 * @param elen the length of the error buffer. |
|
437 * @returns CONFIG_SUCCESSFUL if successful, negative on failure. |
|
438 */ |
|
439 static int |
|
440 initProcLoadCounter(void) { |
|
441 char time[COUNTER_BUF_SIZE]; |
|
442 char counter[COUNTER_BUF_SIZE*2]; |
|
443 |
|
444 if (find_name(PDH_PROCESSOR_TIME_IDX, time, sizeof(time)-1) < 0) { |
|
445 return -1; |
|
446 } |
|
447 _snprintf(counter, sizeof(counter)-1, "%s\\%s", getProcessPDHHeader(), time); |
|
448 return initSingleCounterQuery(&cntProcLoad, counter); |
|
449 } |
|
450 |
|
451 static int |
|
452 initProcSystemLoadCounter(void) { |
|
453 char time[COUNTER_BUF_SIZE]; |
|
454 char counter[COUNTER_BUF_SIZE*2]; |
|
455 |
|
456 if (find_name(PDH_PRIV_PROCESSOR_TIME_IDX, time, sizeof(time)-1) < 0) { |
|
457 return -1; |
|
458 } |
|
459 _snprintf(counter, sizeof(counter)-1, "%s\\%s", getProcessPDHHeader(), time); |
|
460 return initSingleCounterQuery(&cntProcSystemLoad, counter); |
|
461 } |
|
462 |
|
463 /** |
|
464 * Sets up the supplied MultipleCounterQuery to check on the processors. |
|
465 * (Comment: Refactor and prettify as with the the SingleCounter queries |
|
466 * if more MultipleCounterQueries are discovered.) |
|
467 * |
|
468 * initPDH() must have been run prior to calling this function. |
|
469 * |
|
470 * @param multiQuery a pointer to a MultipleCounterQueryS, will be filled in with |
|
471 * the necessary info to check the PDH processor counters. |
|
472 * @return CONFIG_SUCCESSFUL if successful, negative on failure. |
|
473 */ |
|
474 static int |
|
475 initProcessorCounters(void) { |
|
476 char processor[COUNTER_BUF_SIZE]; //'Processor' == #238 |
|
477 char time[COUNTER_BUF_SIZE]; //'Time' == 6 |
|
478 DWORD c_size, i_size; |
|
479 HQUERY tmpQuery; |
|
480 DWORD i, p_count; |
|
481 BOOL error; |
|
482 char *instances, *tmp; |
|
483 PDH_STATUS pdhStat; |
|
484 |
|
485 c_size = i_size = 0; |
|
486 tmpQuery = NULL; |
|
487 error = false; |
|
488 |
|
489 // This __try / __except stuff is there since Windows 2000 beta (or so) sometimes triggered |
|
490 // an access violation when the user had insufficient privileges to use the performance |
|
491 // counters. This was previously guarded by a very ugly piece of code which disabled the |
|
492 // global trap handling in JRockit. Don't know if this really is needed anymore, but otoh, |
|
493 // if we keep it we don't crash on Win2k beta. /Ihse, 2005-05-30 |
|
494 __try { |
|
495 if (find_name(PDH_PROCESSOR_IDX, processor, sizeof(processor)-1) < 0) { |
|
496 return -1; |
|
497 } |
|
498 } __except (EXCEPTION_EXECUTE_HANDLER) { // We'll catch all exceptions here. |
|
499 /* printf("User does not have sufficient privileges to use performance counters"); */ |
|
500 return -1; |
|
501 } |
|
502 |
|
503 if (find_name(PDH_PROCESSOR_TIME_IDX, time, sizeof(time)-1) < 0) { |
|
504 return -1; |
|
505 } |
|
506 //ok, now we have enough to enumerate all processors. |
|
507 pdhStat = PdhEnumObjectItems_i ( |
|
508 NULL, // reserved |
|
509 NULL, // local machine |
|
510 processor, // object to enumerate |
|
511 NULL, // pass in NULL buffers |
|
512 &c_size, // and 0 length to get |
|
513 NULL, // required size |
|
514 &i_size, // of the buffers in chars |
|
515 PERF_DETAIL_WIZARD, // counter detail level |
|
516 0); |
|
517 if (pdh_fail(pdhStat)) { |
|
518 /* printf("could not enumerate processors (1) error=%d", pdhStat); */ |
|
519 return -1; |
|
520 } |
|
521 |
|
522 // use calloc because windows vista does not null terminate the instance names (allthough the docs says it will) |
|
523 instances = calloc(i_size, 1); |
|
524 if (instances == NULL) { |
|
525 /* printf("could not allocate memory (1) %d bytes", i_size); */ |
|
526 error = true; |
|
527 goto end; |
|
528 } |
|
529 |
|
530 c_size = 0; |
|
531 pdhStat = PdhEnumObjectItems_i ( |
|
532 NULL, // reserved |
|
533 NULL, // local machine |
|
534 processor, // object to enumerate |
|
535 NULL, // pass in NULL buffers |
|
536 &c_size, // and 0 length to get |
|
537 instances, // required size |
|
538 &i_size, // of the buffers in chars |
|
539 PERF_DETAIL_WIZARD, // counter detail level |
|
540 0); |
|
541 |
|
542 if (pdh_fail(pdhStat)) { |
|
543 /* printf("could not enumerate processors (2) error=%d", pdhStat); */ |
|
544 error = true; |
|
545 goto end; |
|
546 } |
|
547 //count perf count instances. |
|
548 for (p_count = 0, tmp = instances; *tmp != 0; tmp = &tmp[lstrlen(tmp)+1], p_count++); |
|
549 |
|
550 //is this correct for HT? |
|
551 assert(p_count == num_cpus+1); |
|
552 |
|
553 //ok, have number of perf counters. |
|
554 multiCounterCPULoad.counters = calloc(p_count, sizeof(HCOUNTER)); |
|
555 if (multiCounterCPULoad.counters == NULL) { |
|
556 /* printf("could not allocate memory (2) count=%d", p_count); */ |
|
557 error = true; |
|
558 goto end; |
|
559 } |
|
560 |
|
561 multiCounterCPULoad.noOfCounters = p_count; |
|
562 |
|
563 if (PdhOpenQuery_i(NULL, 0, &multiCounterCPULoad.query.query) != ERROR_SUCCESS) { |
|
564 /* printf("could not create query"); */ |
|
565 error = true; |
|
566 goto end; |
|
567 } |
|
568 //now, fetch the counters. |
|
569 for (i = 0, tmp = instances; *tmp != '\0'; tmp = &tmp[lstrlen(tmp)+1], i++) { |
|
570 char counter[2*COUNTER_BUF_SIZE]; |
|
571 |
|
572 _snprintf(counter, sizeof(counter)-1, "\\%s(%s)\\%s", processor, tmp, time); |
|
573 |
|
574 if (PdhAddCounter_i(multiCounterCPULoad.query.query, counter, 0, &multiCounterCPULoad.counters[i]) != ERROR_SUCCESS) { |
|
575 /* printf("error adding processor counter %s", counter); */ |
|
576 error = true; |
|
577 goto end; |
|
578 } |
|
579 } |
|
580 |
|
581 free(instances); |
|
582 instances = NULL; |
|
583 |
|
584 // Query once to initialize the counters needing at least two queries |
|
585 // (like the % CPU usage) to calculate correctly. |
|
586 if (PdhCollectQueryData_i(multiCounterCPULoad.query.query) != ERROR_SUCCESS) |
|
587 error = true; |
|
588 |
|
589 end: |
|
590 if (instances != NULL) { |
|
591 free(instances); |
|
592 } |
|
593 if (tmpQuery != NULL) { |
|
594 PdhCloseQuery_i(tmpQuery); |
|
595 } |
|
596 if (error) { |
|
597 int i; |
|
598 |
|
599 if (multiCounterCPULoad.counters != NULL) { |
|
600 for (i = 0; i < multiCounterCPULoad.noOfCounters; i++) { |
|
601 if (multiCounterCPULoad.counters[i] != NULL) { |
|
602 PdhRemoveCounter_i(multiCounterCPULoad.counters[i]); |
|
603 } |
|
604 } |
|
605 free(multiCounterCPULoad.counters[i]); |
|
606 } |
|
607 if (multiCounterCPULoad.query.query != NULL) { |
|
608 PdhCloseQuery_i(multiCounterCPULoad.query.query); |
|
609 } |
|
610 memset(&multiCounterCPULoad, 0, sizeof(MultipleCounterQueryS)); |
|
611 return -1; |
|
612 } |
|
613 return CONFIG_SUCCESSFUL; |
|
614 } |
|
615 |
|
616 /** |
|
617 * Help function that initializes the PDH process header for the JRockit process. |
|
618 * (You should probably use getProcessPDHHeader() instead!) |
|
619 * |
|
620 * initPDH() must have been run prior to calling this function. |
|
621 * |
|
622 * @param ebuf the error buffer. |
|
623 * @param elen the length of the error buffer. |
|
624 * |
|
625 * @return the PDH instance description corresponding to the JVM process. |
|
626 */ |
|
627 static char* |
|
628 initProcessPDHHeader(void) { |
|
629 static char hotspotheader[2*COUNTER_BUF_SIZE]; |
|
630 |
|
631 char counter[2*COUNTER_BUF_SIZE]; |
|
632 char processes[COUNTER_BUF_SIZE]; //'Process' == #230 |
|
633 char pid[COUNTER_BUF_SIZE]; //'ID Process' == 784 |
|
634 char module_name[MAX_PATH]; |
|
635 PDH_STATUS pdhStat; |
|
636 DWORD c_size = 0, i_size = 0; |
|
637 HQUERY tmpQuery = NULL; |
|
638 int i, myPid = _getpid(); |
|
639 BOOL error = false; |
|
640 char *instances, *tmp, *instance_name, *dot_pos; |
|
641 |
|
642 tmpQuery = NULL; |
|
643 myPid = _getpid(); |
|
644 error = false; |
|
645 |
|
646 if (find_name(PDH_PROCESS_IDX, processes, sizeof(processes) - 1) < 0) { |
|
647 return NULL; |
|
648 } |
|
649 |
|
650 if (find_name(PDH_ID_PROCESS_IDX, pid, sizeof(pid) - 1) < 0) { |
|
651 return NULL; |
|
652 } |
|
653 //time is same. |
|
654 |
|
655 c_size = 0; |
|
656 i_size = 0; |
|
657 |
|
658 pdhStat = PdhEnumObjectItems_i ( |
|
659 NULL, // reserved |
|
660 NULL, // local machine |
|
661 processes, // object to enumerate |
|
662 NULL, // pass in NULL buffers |
|
663 &c_size, // and 0 length to get |
|
664 NULL, // required size |
|
665 &i_size, // of the buffers in chars |
|
666 PERF_DETAIL_WIZARD, // counter detail level |
|
667 0); |
|
668 |
|
669 //ok, now we have enough to enumerate all processes |
|
670 if (pdh_fail(pdhStat)) { |
|
671 /* printf("Could not enumerate processes (1) error=%d", pdhStat); */ |
|
672 return NULL; |
|
673 } |
|
674 |
|
675 // use calloc because windows vista does not null terminate the instance names (allthough the docs says it will) |
|
676 if ((instances = calloc(i_size, 1)) == NULL) { |
|
677 /* printf("Could not allocate memory %d bytes", i_size); */ |
|
678 error = true; |
|
679 goto end; |
|
680 } |
|
681 |
|
682 c_size = 0; |
|
683 |
|
684 pdhStat = PdhEnumObjectItems_i ( |
|
685 NULL, // reserved |
|
686 NULL, // local machine |
|
687 processes, // object to enumerate |
|
688 NULL, // pass in NULL buffers |
|
689 &c_size, // and 0 length to get |
|
690 instances, // required size |
|
691 &i_size, // of the buffers in chars |
|
692 PERF_DETAIL_WIZARD, // counter detail level |
|
693 0); |
|
694 |
|
695 // ok, now we have enough to enumerate all processes |
|
696 if (pdh_fail(pdhStat)) { |
|
697 /* printf("Could not enumerate processes (2) error=%d", pdhStat); */ |
|
698 error = true; |
|
699 goto end; |
|
700 } |
|
701 |
|
702 if (PdhOpenQuery_i(NULL, 0, &tmpQuery) != ERROR_SUCCESS) { |
|
703 /* printf("Could not create temporary query"); */ |
|
704 error = true; |
|
705 goto end; |
|
706 } |
|
707 |
|
708 // Find our module name and use it to extract the instance name used by PDH |
|
709 if (GetModuleFileName(NULL, module_name, MAX_PATH) >= MAX_PATH-1) { |
|
710 /* printf("Module name truncated"); */ |
|
711 error = true; |
|
712 goto end; |
|
713 } |
|
714 instance_name = strrchr(module_name, '\\'); //drop path |
|
715 instance_name++; //skip slash |
|
716 dot_pos = strchr(instance_name, '.'); //drop .exe |
|
717 dot_pos[0] = '\0'; |
|
718 |
|
719 //now, fetch the counters. |
|
720 for (tmp = instances; *tmp != 0 && !error; tmp = &tmp[lstrlen(tmp)+1]) { |
|
721 HCOUNTER hc = NULL; |
|
722 BOOL done = false; |
|
723 |
|
724 // Skip until we find our own process name |
|
725 if (strcmp(tmp, instance_name) != 0) { |
|
726 continue; |
|
727 } |
|
728 |
|
729 // iterate over all instance indexes and try to find our own pid |
|
730 for (i = 0; !done && !error; i++){ |
|
731 PDH_STATUS res; |
|
732 _snprintf(counter, sizeof(counter)-1, "\\%s(%s#%d)\\%s", processes, tmp, i, pid); |
|
733 |
|
734 if (PdhAddCounter_i(tmpQuery, counter, 0, &hc) != ERROR_SUCCESS) { |
|
735 /* printf("Failed to create process id query"); */ |
|
736 error = true; |
|
737 goto end; |
|
738 } |
|
739 |
|
740 res = PdhCollectQueryData_i(tmpQuery); |
|
741 |
|
742 if (res == PDH_INVALID_HANDLE) { |
|
743 /* printf("Failed to query process id"); */ |
|
744 res = -1; |
|
745 done = true; |
|
746 } else if (res == PDH_NO_DATA) { |
|
747 done = true; |
|
748 } else { |
|
749 PDH_FMT_COUNTERVALUE cv; |
|
750 |
|
751 PdhGetFormattedCounterValue_i(hc, PDH_FMT_LONG, NULL, &cv); |
|
752 /* |
|
753 * This check seems to be needed for Win2k SMP boxes, since |
|
754 * they for some reason don't return PDH_NO_DATA for non existing |
|
755 * counters. |
|
756 */ |
|
757 if (cv.CStatus != PDH_CSTATUS_VALID_DATA) { |
|
758 done = true; |
|
759 } else if (cv.longValue == myPid) { |
|
760 _snprintf(hotspotheader, sizeof(hotspotheader)-1, "\\%s(%s#%d)\0", processes, tmp, i); |
|
761 PdhRemoveCounter_i(hc); |
|
762 goto end; |
|
763 } |
|
764 } |
|
765 PdhRemoveCounter_i(hc); |
|
766 } |
|
767 } |
|
768 end: |
|
769 if (instances != NULL) { |
|
770 free(instances); |
|
771 } |
|
772 if (tmpQuery != NULL) { |
|
773 PdhCloseQuery_i(tmpQuery); |
|
774 } |
|
775 if (error) { |
|
776 return NULL; |
|
777 } |
|
778 return hotspotheader; |
|
779 } |
|
780 |
|
781 /** |
|
782 * Returns the PDH string prefix identifying the HotSpot process. Use this prefix when getting |
|
783 * counters from the PDH process object representing HotSpot. |
|
784 * |
|
785 * Note: this call may take some time to complete. |
|
786 * |
|
787 * @param ebuf error buffer. |
|
788 * @param elen error buffer length. |
|
789 * |
|
790 * @return the header to be used when retrieving PDH counters from the HotSpot process. |
|
791 * Will return NULL if the call failed. |
|
792 */ |
|
793 static char * |
|
794 getProcessPDHHeader(void) { |
|
795 static char *processHeader = NULL; |
|
796 |
|
797 EnterCriticalSection(&processHeaderLock); { |
|
798 if (processHeader == NULL) { |
|
799 processHeader = initProcessPDHHeader(); |
|
800 } |
|
801 } LeaveCriticalSection(&processHeaderLock); |
|
802 return processHeader; |
|
803 } |
|
804 |
|
805 int perfInit(void); |
|
806 |
|
807 double |
|
808 perfGetCPULoad(int which) |
|
809 { |
|
810 PDH_FMT_COUNTERVALUE cv; |
|
811 HCOUNTER c; |
|
812 |
|
813 if (perfInit() < 0) { |
|
814 // warn? |
|
815 return -1.0; |
|
816 } |
|
817 |
|
818 if (multiCounterCPULoad.query.query == NULL) { |
|
819 // warn? |
|
820 return -1.0; |
|
821 } |
|
822 |
|
823 if (which == -1) { |
|
824 c = multiCounterCPULoad.counters[multiCounterCPULoad.noOfCounters - 1]; |
|
825 } else { |
|
826 if (which < multiCounterCPULoad.noOfCounters) { |
|
827 c = multiCounterCPULoad.counters[which]; |
|
828 } else { |
|
829 return -1.0; |
|
830 } |
|
831 } |
|
832 if (getPerformanceData(&multiCounterCPULoad.query, c, &cv, PDH_FMT_DOUBLE ) == CONFIG_SUCCESSFUL) { |
|
833 return cv.doubleValue / 100; |
|
834 } |
|
835 return -1.0; |
|
836 } |
|
837 |
|
838 double |
|
839 perfGetProcessLoad(void) |
|
840 { |
|
841 PDH_FMT_COUNTERVALUE cv; |
|
842 |
|
843 if (perfInit() < 0) { |
|
844 // warn? |
|
845 return -1.0; |
|
846 } |
|
847 |
|
848 if (cntProcLoad.query.query == NULL) { |
|
849 // warn? |
|
850 return -1.0; |
|
851 } |
|
852 |
|
853 if (getPerformanceData(&cntProcLoad.query, cntProcLoad.counter, &cv, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100) == CONFIG_SUCCESSFUL) { |
|
854 double d = cv.doubleValue / cpuFactor; |
|
855 d = min(1, d); |
|
856 d = max(0, d); |
|
857 return d; |
|
858 } |
|
859 return -1.0; |
|
860 } |
|
861 |
|
862 /** |
|
863 * Helper to initialize the PDH library. Loads the library and sets up the functions. |
|
864 * Note that once loaded, we will never unload the PDH library. |
|
865 * |
|
866 * @return CONFIG_SUCCESSFUL if successful, negative on failure. |
|
867 */ |
|
868 int |
|
869 perfInit(void) { |
|
870 static HMODULE h; |
|
871 static BOOL running, inited; |
|
872 |
|
873 int error; |
|
874 |
|
875 if (running) { |
|
876 return CONFIG_SUCCESSFUL; |
|
877 } |
|
878 |
|
879 error = CONFIG_SUCCESSFUL; |
|
880 |
|
881 // this is double checked locking again, but we try to bypass the worst by |
|
882 // implicit membar at end of lock. |
|
883 EnterCriticalSection(&initializationLock); { |
|
884 if (!inited) { |
|
885 char buf[64] = ""; |
|
886 SYSTEM_INFO si; |
|
887 |
|
888 // CMH. But windows will not care about our affinity when giving |
|
889 // us measurements. Need the real, raw num cpus. |
|
890 |
|
891 GetSystemInfo(&si); |
|
892 num_cpus = si.dwNumberOfProcessors; |
|
893 // Initialize the denominator for the jvm load calculations |
|
894 cpuFactor = num_cpus * 100; |
|
895 |
|
896 /** |
|
897 * Do this dynamically, so we don't fail to start on systems without pdh. |
|
898 */ |
|
899 if ((h = LoadLibrary("pdh.dll")) == NULL) { |
|
900 /* printf("Could not load pdh.dll (%d)", GetLastError()); */ |
|
901 error = -2; |
|
902 } else if (get_functions(h, buf, sizeof(buf)) < 0) { |
|
903 FreeLibrary(h); |
|
904 h = NULL; |
|
905 error = -2; |
|
906 /* printf("Failed to init pdh functions: %s.\n", buf); */ |
|
907 } else { |
|
908 if (initProcessorCounters() != 0) { |
|
909 /* printf("Failed to init system load counters.\n"); */ |
|
910 } else if (initProcLoadCounter() != 0) { |
|
911 /* printf("Failed to init process load counter.\n"); */ |
|
912 } else if (initProcSystemLoadCounter() != 0) { |
|
913 /* printf("Failed to init process system load counter.\n"); */ |
|
914 } else { |
|
915 inited = true; |
|
916 } |
|
917 } |
|
918 } |
|
919 } LeaveCriticalSection(&initializationLock); |
|
920 |
|
921 if (inited && error == CONFIG_SUCCESSFUL) { |
|
922 running = true; |
|
923 } |
|
924 |
|
925 return error; |
|
926 } |
|
927 |
|
928 JNIEXPORT jdouble JNICALL |
|
929 Java_com_sun_management_OperatingSystem_getSystemCpuLoad |
|
930 (JNIEnv *env, jobject dummy) |
|
931 { |
|
932 return perfGetCPULoad(-1); |
|
933 } |
|
934 |
|
935 JNIEXPORT jdouble JNICALL |
|
936 Java_com_sun_management_OperatingSystem_getProcessCpuLoad |
|
937 (JNIEnv *env, jobject dummy) |
|
938 { |
|
939 return perfGetProcessLoad(); |
|
940 } |