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_initialize 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_getTotalSwapSpaceSize 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_getFreeSwapSpaceSize 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_getProcessCpuTime 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_getFreePhysicalMemorySize 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_getTotalPhysicalMemorySize 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_getSystemCpuLoad 1353 (JNIEnv *env, jobject dummy) 1354 { 1355 return perfGetCPULoad(-1); 1356 } 1357 1358 JNIEXPORT jdouble JNICALL 1359 Java_sun_management_OperatingSystemImpl_getProcessCpuLoad 1360 (JNIEnv *env, jobject dummy) 1361 { 1362 return perfGetProcessCPULoad(); 1363 }