1 /* 2 * Copyright (c) 2012, 2018, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include "precompiled.hpp" 26 #include "logging/log.hpp" 27 #include "memory/allocation.inline.hpp" 28 #include "memory/resourceArea.hpp" 29 #include "pdh_interface.hpp" 30 #include "runtime/os_perf.hpp" 31 #include "runtime/os.hpp" 32 #include "vm_version_ext_x86.hpp" 33 #include "utilities/macros.hpp" 34 #include <math.h> 35 #include <psapi.h> 36 #include <TlHelp32.h> 37 38 /* 39 * Windows provides a vast plethora of performance objects and counters, 40 * consumption of which is assisted using the Performance Data Helper (PDH) interface. 41 * We import a selected few api entry points from PDH, see pdh_interface.hpp. 42 * 43 * The code located in this file is to a large extent an abstraction over much of the 44 * plumbing needed to start consuming an object and/or counter of choice. 45 * 46 */ 47 48 /* 49 * How to use: 50 * 1. Create query 51 * 2. Add counters to the query 52 * 3. Collect the performance data using the query 53 * 4. Display the performance data using the counters associated with the query 54 * 5. Destroy query (counter destruction implied) 55 */ 56 57 /* 58 * Every PDH artifact, like processor, process, thread, memory, and so forth are 59 * identified with an index that is always the same irrespective 60 * of the localized version of the operating system or service pack installed. 61 * INFO: Using PDH APIs Correctly in a Localized Language (Q287159) 62 * http://support.microsoft.com/default.aspx?scid=kb;EN-US;q287159 63 * 64 * To find the correct index for an object or counter, inspect the registry key / value: 65 * [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009\Counter] 66 * 67 * some common PDH indexes 68 */ 69 static const DWORD PDH_PROCESSOR_IDX = 238; 70 static const DWORD PDH_PROCESSOR_TIME_IDX = 6; 71 static const DWORD PDH_PRIV_PROCESSOR_TIME_IDX = 144; 72 static const DWORD PDH_PROCESS_IDX = 230; 73 static const DWORD PDH_ID_PROCESS_IDX = 784; 74 static const DWORD PDH_CONTEXT_SWITCH_RATE_IDX = 146; 75 static const DWORD PDH_SYSTEM_IDX = 2; 76 77 /* useful pdh fmt's */ 78 static const char* const OBJECT_COUNTER_FMT = "\\%s\\%s"; 79 static const size_t OBJECT_COUNTER_FMT_LEN = 2; 80 static const char* const OBJECT_WITH_INSTANCES_COUNTER_FMT = "\\%s(%s)\\%s"; 81 static const size_t OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN = 4; 82 static const char* const PROCESS_OBJECT_INSTANCE_COUNTER_FMT = "\\%s(%s#%s)\\%s"; 83 static const size_t PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN = 5; 84 85 static const char* process_image_name = NULL; // for example, "java" but could have another image name 86 static char* pdh_IDProcess_counter_fmt = NULL; // "\Process(java#%d)\ID Process" */ 87 88 // Need to limit how often we update a query to minimize the heisenberg effect. 89 // (PDH behaves erratically if the counters are queried too often, especially counters that 90 // store and use values from two consecutive updates, like cpu load.) 91 static const int min_update_interval_millis = 500; 92 93 /* 94 * Structs for PDH queries. 95 */ 96 typedef struct { 97 HQUERY query; 98 s8 lastUpdate; // Last time query was updated (current millis). 99 } UpdateQueryS, *UpdateQueryP; 100 101 102 typedef struct { 103 UpdateQueryS query; 104 HCOUNTER counter; 105 bool initialized; 106 } CounterQueryS, *CounterQueryP; 107 108 typedef struct { 109 UpdateQueryS query; 110 HCOUNTER* counters; 111 int noOfCounters; 112 bool initialized; 113 } MultiCounterQueryS, *MultiCounterQueryP; 114 115 typedef struct { 116 MultiCounterQueryP queries; 117 int size; 118 bool initialized; 119 } MultiCounterQuerySetS, *MultiCounterQuerySetP; 120 121 static void pdh_cleanup(HQUERY* const query, HCOUNTER* const counter) { 122 if (counter != NULL && *counter != NULL) { 123 PdhDll::PdhRemoveCounter(*counter); 124 *counter = NULL; 125 } 126 if (query != NULL && *query != NULL) { 127 PdhDll::PdhCloseQuery(*query); 128 *query = NULL; 129 } 130 } 131 132 static CounterQueryP create_counter_query() { 133 CounterQueryP const query = NEW_C_HEAP_ARRAY(CounterQueryS, 1, mtInternal); 134 memset(query, 0, sizeof(CounterQueryS)); 135 return query; 136 } 137 138 static void destroy_counter_query(CounterQueryP query) { 139 assert(query != NULL, "invariant"); 140 pdh_cleanup(&query->query.query, &query->counter); 141 FREE_C_HEAP_ARRAY(CounterQueryS, query); 142 } 143 144 static MultiCounterQueryP create_multi_counter_query() { 145 MultiCounterQueryP const query = NEW_C_HEAP_ARRAY(MultiCounterQueryS, 1, mtInternal); 146 memset(query, 0, sizeof(MultiCounterQueryS)); 147 return query; 148 } 149 150 static void destroy_counter_query(MultiCounterQueryP counter_query) { 151 if (counter_query != NULL) { 152 for (int i = 0; i < counter_query->noOfCounters; ++i) { 153 pdh_cleanup(NULL, &counter_query->counters[i]); 154 } 155 FREE_C_HEAP_ARRAY(char, counter_query->counters); 156 pdh_cleanup(&counter_query->query.query, NULL); 157 FREE_C_HEAP_ARRAY(MultiCounterQueryS, counter_query); 158 } 159 } 160 161 static void destroy_counter_query(MultiCounterQuerySetP counter_query_set) { 162 for (int i = 0; i < counter_query_set->size; i++) { 163 for (int j = 0; j < counter_query_set->queries[i].noOfCounters; ++j) { 164 pdh_cleanup(NULL, &counter_query_set->queries[i].counters[j]); 165 } 166 FREE_C_HEAP_ARRAY(char, counter_query_set->queries[i].counters); 167 pdh_cleanup(&counter_query_set->queries[i].query.query, NULL); 168 } 169 FREE_C_HEAP_ARRAY(MultiCounterQueryS, counter_query_set->queries); 170 FREE_C_HEAP_ARRAY(MultiCounterQuerySetS, counter_query_set); 171 } 172 173 static int open_query(HQUERY* query) { 174 return PdhDll::PdhOpenQuery(NULL, 0, query); 175 } 176 177 template <typename QueryP> 178 static int open_query(QueryP query) { 179 return open_query(&query->query); 180 } 181 182 static int allocate_counters(MultiCounterQueryP query, size_t nofCounters) { 183 assert(query != NULL, "invariant"); 184 assert(!query->initialized, "invariant"); 185 assert(0 == query->noOfCounters, "invariant"); 186 assert(query->counters == NULL, "invariant"); 187 query->counters = (HCOUNTER*)NEW_C_HEAP_ARRAY(char, nofCounters * sizeof(HCOUNTER), mtInternal); 188 if (query->counters == NULL) { 189 return OS_ERR; 190 } 191 memset(query->counters, 0, nofCounters * sizeof(HCOUNTER)); 192 query->noOfCounters = (int)nofCounters; 193 return OS_OK; 194 } 195 196 static int allocate_counters(MultiCounterQuerySetP query_set, size_t nofCounters) { 197 assert(query_set != NULL, "invariant"); 198 assert(!query_set->initialized, "invariant"); 199 for (int i = 0; i < query_set->size; ++i) { 200 if (allocate_counters(&query_set->queries[i], nofCounters) != OS_OK) { 201 return OS_ERR; 202 } 203 } 204 return OS_OK; 205 } 206 207 static void deallocate_counters(MultiCounterQueryP query) { 208 if (query->counters != NULL) { 209 FREE_C_HEAP_ARRAY(char, query->counters); 210 query->counters = NULL; 211 query->noOfCounters = 0; 212 } 213 } 214 215 static OSReturn add_counter(UpdateQueryP query, HCOUNTER* counter, const char* path, bool first_sample_on_init) { 216 assert(query != NULL, "invariant"); 217 assert(counter != NULL, "invariant"); 218 assert(path != NULL, "invariant"); 219 if (query->query == NULL) { 220 if (open_query(query) != ERROR_SUCCESS) { 221 return OS_ERR; 222 } 223 } 224 assert(query->query != NULL, "invariant"); 225 PDH_STATUS status = PdhDll::PdhAddCounter(query->query, path, 0, counter); 226 if (PDH_CSTATUS_NO_OBJECT == status || PDH_CSTATUS_NO_COUNTER == status) { 227 return OS_ERR; 228 } 229 /* 230 * According to the MSDN documentation, rate counters must be read twice: 231 * 232 * "Obtaining the value of rate counters such as Page faults/sec requires that 233 * PdhCollectQueryData be called twice, with a specific time interval between 234 * the two calls, before calling PdhGetFormattedCounterValue. Call Sleep to 235 * implement the waiting period between the two calls to PdhCollectQueryData." 236 * 237 * Take the first sample here already to allow for the next "real" sample 238 * to succeed. 239 */ 240 if (first_sample_on_init) { 241 PdhDll::PdhCollectQueryData(query->query); 242 } 243 return OS_OK; 244 } 245 246 template <typename QueryP> 247 static OSReturn add_counter(QueryP counter_query, HCOUNTER* counter, const char* path, bool first_sample_on_init) { 248 assert(counter_query != NULL, "invariant"); 249 assert(counter != NULL, "invariant"); 250 assert(path != NULL, "invariant"); 251 return add_counter(&counter_query->query, counter, path, first_sample_on_init); 252 } 253 254 static OSReturn add_counter(CounterQueryP counter_query, const char* path, bool first_sample_on_init) { 255 if (add_counter(counter_query, &counter_query->counter, path, first_sample_on_init) != OS_OK) { 256 // performance counter might be disabled in the registry 257 return OS_ERR; 258 } 259 counter_query->initialized = true; 260 return OS_OK; 261 } 262 263 static OSReturn add_process_counter(MultiCounterQueryP query, int slot_index, const char* path, bool first_sample_on_init) { 264 assert(query != NULL, "invariant"); 265 assert(query != NULL, "invariant"); 266 assert(slot_index < query->noOfCounters, "invariant"); 267 assert(query->counters[slot_index] == NULL, "invariant"); 268 const OSReturn ret = add_counter(query, &query->counters[slot_index], path, first_sample_on_init); 269 if (OS_OK == ret) { 270 if (slot_index + 1 == query->noOfCounters) { 271 query->initialized = true; 272 } 273 } 274 return ret; 275 } 276 277 static int collect_query_data(UpdateQueryP update_query) { 278 assert(update_query != NULL, "invariant"); 279 const s8 now = os::javaTimeMillis(); 280 if (now - update_query->lastUpdate > min_update_interval_millis) { 281 if (PdhDll::PdhCollectQueryData(update_query->query) != ERROR_SUCCESS) { 282 return OS_ERR; 283 } 284 update_query->lastUpdate = now; 285 } 286 return OS_OK; 287 } 288 289 template <typename Query> 290 static int collect_query_data(Query* counter_query) { 291 assert(counter_query != NULL, "invariant"); 292 return collect_query_data(&counter_query->query); 293 } 294 295 static int formatted_counter_value(HCOUNTER counter, DWORD format, PDH_FMT_COUNTERVALUE* const value) { 296 assert(value != NULL, "invariant"); 297 if (PdhDll::PdhGetFormattedCounterValue(counter, format, NULL, value) != ERROR_SUCCESS) { 298 return OS_ERR; 299 } 300 return OS_OK; 301 } 302 303 /* 304 * Working against the Process object and it's related counters is inherently problematic 305 * when using the PDH API: 306 * 307 * Using PDH, a process is not primarily identified by the process id, 308 * but with a sequential number, for example \Process(java#0), \Process(java#1), ... 309 * The really bad part is that this list is reset as soon as a process exits: 310 * If \Process(java#1) exits, \Process(java#3) now becomes \Process(java#2) etc. 311 * 312 * The PDH api requires a process identifier to be submitted when registering 313 * a query, but as soon as the list resets, the query is invalidated (since the name changed). 314 * 315 * Solution: 316 * The #number identifier for a Process query can only decrease after process creation. 317 * 318 * We therefore create an array of counter queries for all process object instances 319 * up to and including ourselves: 320 * 321 * Ex. we come in as third process instance (java#2), we then create and register 322 * queries for the following Process object instances: 323 * java#0, java#1, java#2 324 * 325 * current_query_index_for_process() keeps track of the current "correct" query 326 * (in order to keep this index valid when the list resets from underneath, 327 * ensure to call current_query_index_for_process() before every query involving 328 * Process object instance data). 329 */ 330 static int current_query_index_for_process() { 331 assert(process_image_name != NULL, "invariant"); 332 assert(pdh_IDProcess_counter_fmt != NULL, "invariant"); 333 HQUERY tmpQuery = NULL; 334 if (open_query(&tmpQuery) != ERROR_SUCCESS) { 335 return 0; 336 } 337 char counter[512]; 338 HCOUNTER handle_counter = NULL; 339 // iterate over all instance indexes and try to find our own pid 340 for (int index = 0; index < max_intx; index++) { 341 jio_snprintf(counter, sizeof(counter) - 1, pdh_IDProcess_counter_fmt, index); 342 assert(strlen(counter) < sizeof(counter), "invariant"); 343 if (PdhDll::PdhAddCounter(tmpQuery, counter, 0, &handle_counter) != ERROR_SUCCESS) { 344 pdh_cleanup(&tmpQuery, &handle_counter); 345 return 0; 346 } 347 const PDH_STATUS res = PdhDll::PdhCollectQueryData(tmpQuery); 348 if (res == PDH_INVALID_HANDLE || res == PDH_NO_DATA) { 349 pdh_cleanup(&tmpQuery, &handle_counter); 350 return 0; 351 } else { 352 PDH_FMT_COUNTERVALUE counter_value; 353 formatted_counter_value(handle_counter, PDH_FMT_LONG, &counter_value); 354 pdh_cleanup(NULL, &handle_counter); 355 if ((LONG)os::current_process_id() == counter_value.longValue) { 356 pdh_cleanup(&tmpQuery, NULL); 357 return index; 358 } 359 } 360 } 361 pdh_cleanup(&tmpQuery, NULL); 362 return 0; 363 } 364 365 static MultiCounterQuerySetP create_process_counter_query() { 366 MultiCounterQuerySetP const query = NEW_C_HEAP_ARRAY(MultiCounterQuerySetS, 1, mtInternal); 367 memset(query, 0, sizeof(MultiCounterQuerySetS)); 368 const int current_process_idx = current_query_index_for_process(); 369 query->queries = NEW_C_HEAP_ARRAY(MultiCounterQueryS, current_process_idx + 1, mtInternal); 370 memset(query->queries, 0, sizeof(MultiCounterQueryS) * (current_process_idx + 1)); 371 query->size = current_process_idx + 1; 372 return query; 373 } 374 375 static MultiCounterQueryP current_process_counter_query(MultiCounterQuerySetP process_query_set) { 376 assert(process_query_set != NULL, "invariant"); 377 const int current_query_index = current_query_index_for_process(); 378 assert(current_query_index < process_query_set->size, "invariant"); 379 return &process_query_set->queries[current_query_index]; 380 } 381 382 static void clear_multi_counter(MultiCounterQueryP query) { 383 for (int i = 0; i < query->noOfCounters; ++i) { 384 pdh_cleanup(NULL, &query->counters[i]); 385 } 386 pdh_cleanup(&query->query.query, NULL); 387 } 388 389 static int collect_process_query_data(MultiCounterQuerySetP counter_query_set) { 390 const int current_process_idx = current_query_index_for_process(); 391 while (current_process_idx < counter_query_set->size - 1) { 392 const int new_size = --counter_query_set->size; 393 clear_multi_counter(&counter_query_set->queries[new_size]); 394 } 395 return collect_query_data(&counter_query_set->queries[current_process_idx]); 396 } 397 398 static int query_process_counter(MultiCounterQuerySetP process_query_set, int slot_index, DWORD format, PDH_FMT_COUNTERVALUE* const value) { 399 MultiCounterQueryP const current_query = current_process_counter_query(process_query_set); 400 assert(current_query != NULL, "invariant"); 401 assert(slot_index < current_query->noOfCounters, "invariant"); 402 assert(current_query->counters[slot_index] != NULL, "invariant"); 403 return formatted_counter_value(current_query->counters[slot_index], format, value); 404 } 405 406 /* 407 * Construct a fully qualified PDH path 408 * 409 * @param objectName a PDH Object string representation(required) 410 * @param counterName a PDH Counter string representation(required) 411 * @param imageName a process image name string, ex. "java" (opt) 412 * @param instance an instance string, ex. "0", "1", ... (opt) 413 * @return the fully qualified PDH path. 414 * 415 * Caller will need a ResourceMark. 416 * 417 * (PdhMakeCounterPath() seems buggy on concatenating instances, hence this function instead) 418 */ 419 static const char* make_fully_qualified_counter_path(const char* object_name, 420 const char* counter_name, 421 const char* image_name = NULL, 422 const char* instance = NULL) { 423 assert(object_name != NULL, "invariant"); 424 assert(counter_name != NULL, "invariant"); 425 size_t full_counter_path_len = strlen(object_name) + strlen(counter_name); 426 427 char* full_counter_path; 428 size_t jio_snprintf_result = 0; 429 if (image_name) { 430 /* 431 * For paths using the "Process" Object. 432 * 433 * Examples: 434 * form: "\object_name(image_name#instance)\counter_name" 435 * actual: "\Process(java#2)\ID Process" 436 */ 437 full_counter_path_len += PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN; 438 full_counter_path_len += strlen(image_name); 439 /* 440 * image_name must be passed together with an associated 441 * instance "number" ("0", "1", "2", ...). 442 * This is required in order to create valid "Process" Object paths. 443 * 444 * Examples: "\Process(java#0)", \Process(java#1"), ... 445 */ 446 assert(instance != NULL, "invariant"); 447 full_counter_path_len += strlen(instance); 448 full_counter_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, full_counter_path_len + 1); 449 if (full_counter_path == NULL) { 450 return NULL; 451 } 452 jio_snprintf_result = jio_snprintf(full_counter_path, 453 full_counter_path_len + 1, 454 PROCESS_OBJECT_INSTANCE_COUNTER_FMT, 455 object_name, 456 image_name, 457 instance, 458 counter_name); 459 } else { 460 if (instance) { 461 /* 462 * For paths where the Object has multiple instances. 463 * 464 * Examples: 465 * form: "\object_name(instance)\counter_name" 466 * actual: "\Processor(0)\% Privileged Time" 467 */ 468 full_counter_path_len += strlen(instance); 469 full_counter_path_len += OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN; 470 } else { 471 /* 472 * For "normal" paths. 473 * 474 * Examples: 475 * form: "\object_name\counter_name" 476 * actual: "\Memory\Available Mbytes" 477 */ 478 full_counter_path_len += OBJECT_COUNTER_FMT_LEN; 479 } 480 full_counter_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, full_counter_path_len + 1); 481 if (full_counter_path == NULL) { 482 return NULL; 483 } 484 if (instance) { 485 jio_snprintf_result = jio_snprintf(full_counter_path, 486 full_counter_path_len + 1, 487 OBJECT_WITH_INSTANCES_COUNTER_FMT, 488 object_name, 489 instance, 490 counter_name); 491 } else { 492 jio_snprintf_result = jio_snprintf(full_counter_path, 493 full_counter_path_len + 1, 494 OBJECT_COUNTER_FMT, 495 object_name, 496 counter_name); 497 } 498 } 499 assert(full_counter_path_len == jio_snprintf_result, "invariant"); 500 return full_counter_path; 501 } 502 503 static void log_invalid_pdh_index(DWORD index) { 504 log_warning(os)("Unable to resolve PDH index: (%ld)", index); 505 log_warning(os)("Please check the registry if this performance object/counter is disabled"); 506 } 507 508 static bool is_valid_pdh_index(DWORD index) { 509 DWORD dummy = 0; 510 if (PdhDll::PdhLookupPerfNameByIndex(NULL, index, NULL, &dummy) != PDH_MORE_DATA) { 511 log_invalid_pdh_index(index); 512 return false; 513 } 514 return true; 515 } 516 517 /* 518 * Maps an index to a resource area allocated string for the localized PDH artifact. 519 * 520 * Caller will need a ResourceMark. 521 * 522 * @param index the counter index as specified in the registry 523 * @param ppBuffer pointer to a char* 524 * @return OS_OK if successful, OS_ERR on failure. 525 */ 526 static OSReturn lookup_name_by_index(DWORD index, char** p_string) { 527 assert(p_string != NULL, "invariant"); 528 if (!is_valid_pdh_index(index)) { 529 return OS_ERR; 530 } 531 // determine size needed 532 DWORD size = 0; 533 PDH_STATUS status = PdhDll::PdhLookupPerfNameByIndex(NULL, index, NULL, &size); 534 assert(status == PDH_MORE_DATA, "invariant"); 535 *p_string = NEW_RESOURCE_ARRAY_RETURN_NULL(char, size); 536 if (*p_string== NULL) { 537 return OS_ERR; 538 } 539 if (PdhDll::PdhLookupPerfNameByIndex(NULL, index, *p_string, &size) != ERROR_SUCCESS) { 540 return OS_ERR; 541 } 542 if (0 == size || *p_string == NULL) { 543 return OS_ERR; 544 } 545 // windows vista does not null-terminate the string (although the docs says it will) 546 (*p_string)[size - 1] = '\0'; 547 return OS_OK; 548 } 549 550 static const char* copy_string_to_c_heap(const char* string) { 551 assert(string != NULL, "invariant"); 552 const size_t len = strlen(string); 553 char* const cheap_allocated_string = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal); 554 if (NULL == cheap_allocated_string) { 555 return NULL; 556 } 557 strncpy(cheap_allocated_string, string, len + 1); 558 return cheap_allocated_string; 559 } 560 561 /* 562 * Maps an index to a resource area allocated string for the localized PDH artifact. 563 * 564 * Caller will need a ResourceMark. 565 * 566 * @param index the counter index as specified in the registry 567 * @return localized pdh artifact string if successful, NULL on failure. 568 */ 569 static const char* pdh_localized_artifact(DWORD pdh_artifact_index) { 570 char* pdh_localized_artifact_string = NULL; 571 // get localized name from pdh artifact index 572 if (lookup_name_by_index(pdh_artifact_index, &pdh_localized_artifact_string) != OS_OK) { 573 return NULL; 574 } 575 return pdh_localized_artifact_string; 576 } 577 578 /* 579 * Returns the PDH string identifying the current process image name. 580 * Use this prefix when getting counters from the PDH process object 581 * representing your process. 582 * Ex. "Process(java#0)\Virtual Bytes" - where "java" is the PDH process 583 * image description. 584 * 585 * Caller needs ResourceMark. 586 * 587 * @return the process image description. NULL if the call failed. 588 */ 589 static const char* pdh_process_image_name() { 590 char* module_name = NEW_RESOURCE_ARRAY_RETURN_NULL(char, MAX_PATH); 591 if (NULL == module_name) { 592 return NULL; 593 } 594 // Find our module name and use it to extract the image name used by PDH 595 DWORD getmfn_return = GetModuleFileName(NULL, module_name, MAX_PATH); 596 if (getmfn_return >= MAX_PATH || 0 == getmfn_return) { 597 return NULL; 598 } 599 if (os::get_last_error() == ERROR_INSUFFICIENT_BUFFER) { 600 return NULL; 601 } 602 char* process_image_name = strrchr(module_name, '\\'); //drop path 603 process_image_name++; //skip slash 604 char* dot_pos = strrchr(process_image_name, '.'); //drop .exe 605 dot_pos[0] = '\0'; 606 return process_image_name; 607 } 608 609 static void deallocate_pdh_constants() { 610 if (process_image_name != NULL) { 611 FREE_C_HEAP_ARRAY(char, process_image_name); 612 process_image_name = NULL; 613 } 614 if (pdh_IDProcess_counter_fmt != NULL) { 615 FREE_C_HEAP_ARRAY(char, pdh_IDProcess_counter_fmt); 616 pdh_IDProcess_counter_fmt = NULL; 617 } 618 } 619 620 static int allocate_pdh_constants() { 621 assert(process_image_name == NULL, "invariant"); 622 const char* pdh_image_name = pdh_process_image_name(); 623 if (pdh_image_name == NULL) { 624 return OS_ERR; 625 } 626 process_image_name = copy_string_to_c_heap(pdh_image_name); 627 628 const char* pdh_localized_process_object = pdh_localized_artifact(PDH_PROCESS_IDX); 629 if (pdh_localized_process_object == NULL) { 630 return OS_ERR; 631 } 632 633 const char* pdh_localized_IDProcess_counter = pdh_localized_artifact(PDH_ID_PROCESS_IDX); 634 if (pdh_localized_IDProcess_counter == NULL) { 635 return OS_ERR; 636 } 637 638 size_t pdh_IDProcess_counter_fmt_len = strlen(process_image_name); 639 pdh_IDProcess_counter_fmt_len += strlen(pdh_localized_process_object); 640 pdh_IDProcess_counter_fmt_len += strlen(pdh_localized_IDProcess_counter); 641 pdh_IDProcess_counter_fmt_len += PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN; 642 pdh_IDProcess_counter_fmt_len += 2; // "%d" 643 644 assert(pdh_IDProcess_counter_fmt == NULL, "invariant"); 645 pdh_IDProcess_counter_fmt = NEW_C_HEAP_ARRAY_RETURN_NULL(char, pdh_IDProcess_counter_fmt_len + 1, mtInternal); 646 if (pdh_IDProcess_counter_fmt == NULL) { 647 return OS_ERR; 648 } 649 650 /* "\Process(java#%d)\ID Process" */ 651 const size_t len = jio_snprintf(pdh_IDProcess_counter_fmt, 652 pdh_IDProcess_counter_fmt_len + 1, 653 PROCESS_OBJECT_INSTANCE_COUNTER_FMT, 654 pdh_localized_process_object, 655 process_image_name, 656 "%d", 657 pdh_localized_IDProcess_counter); 658 659 assert(pdh_IDProcess_counter_fmt != NULL, "invariant"); 660 assert(len == pdh_IDProcess_counter_fmt_len, "invariant"); 661 return OS_OK; 662 } 663 664 /* 665 * Enuerate the Processor PDH object and returns a buffer containing the enumerated instances. 666 * Caller needs ResourceMark; 667 * 668 * @return buffer if successful, NULL on failure. 669 */ 670 static const char* enumerate_cpu_instances() { 671 char* processor; //'Processor' == PDH_PROCESSOR_IDX 672 if (lookup_name_by_index(PDH_PROCESSOR_IDX, &processor) != OS_OK) { 673 return NULL; 674 } 675 DWORD c_size = 0; 676 DWORD i_size = 0; 677 // enumerate all processors. 678 PDH_STATUS pdhStat = PdhDll::PdhEnumObjectItems(NULL, // reserved 679 NULL, // local machine 680 processor, // object to enumerate 681 NULL, 682 &c_size, 683 NULL, // instance buffer is NULL and 684 &i_size, // pass 0 length in order to get the required size 685 PERF_DETAIL_WIZARD, // counter detail level 686 0); 687 if (PdhDll::PdhStatusFail((pdhStat))) { 688 return NULL; 689 } 690 char* const instances = NEW_RESOURCE_ARRAY_RETURN_NULL(char, i_size); 691 if (instances == NULL) { 692 return NULL; 693 } 694 c_size = 0; 695 pdhStat = PdhDll::PdhEnumObjectItems(NULL, // reserved 696 NULL, // local machine 697 processor, // object to enumerate 698 NULL, 699 &c_size, 700 instances, // now instance buffer is allocated to be filled in 701 &i_size, // and the required size is known 702 PERF_DETAIL_WIZARD, // counter detail level 703 0); 704 if (PdhDll::PdhStatusFail((pdhStat))) { 705 return NULL; 706 } 707 return instances; 708 } 709 710 static int count_logical_cpus(const char* instances) { 711 assert(instances != NULL, "invariant"); 712 // count logical instances. 713 DWORD count; 714 char* tmp; 715 for (count = 0, tmp = const_cast<char*>(instances); *tmp != '\0'; tmp = &tmp[strlen(tmp) + 1], count++); 716 // PDH reports an instance for each logical processor plus an instance for the total (_Total) 717 assert(count == os::processor_count() + 1, "invalid enumeration!"); 718 return count - 1; 719 } 720 721 static int number_of_logical_cpus() { 722 static int numberOfCPUS = 0; 723 if (numberOfCPUS == 0) { 724 const char* instances = enumerate_cpu_instances(); 725 if (instances == NULL) { 726 return OS_ERR; 727 } 728 numberOfCPUS = count_logical_cpus(instances); 729 } 730 return numberOfCPUS; 731 } 732 733 static double cpu_factor() { 734 static DWORD numCpus = 0; 735 static double cpuFactor = .0; 736 if (numCpus == 0) { 737 numCpus = number_of_logical_cpus(); 738 assert(os::processor_count() <= (int)numCpus, "invariant"); 739 cpuFactor = numCpus * 100; 740 } 741 return cpuFactor; 742 } 743 744 static void log_error_message_on_no_PDH_artifact(const char* full_counter_name) { 745 log_warning(os)("Unable to register PDH query for \"%s\"", full_counter_name); 746 log_warning(os)("Please check the registry if this performance object/counter is disabled"); 747 } 748 749 static int initialize_cpu_query_counters(MultiCounterQueryP cpu_query, DWORD pdh_counter_idx) { 750 assert(cpu_query != NULL, "invariant"); 751 assert(cpu_query->counters != NULL, "invariant"); 752 char* processor; //'Processor' == PDH_PROCESSOR_IDX 753 if (lookup_name_by_index(PDH_PROCESSOR_IDX, &processor) != OS_OK) { 754 return OS_ERR; 755 } 756 char* counter_name = NULL; 757 if (lookup_name_by_index(pdh_counter_idx, &counter_name) != OS_OK) { 758 return OS_ERR; 759 } 760 if (cpu_query->query.query == NULL) { 761 if (open_query(cpu_query)) { 762 return OS_ERR; 763 } 764 } 765 assert(cpu_query->query.query != NULL, "invariant"); 766 size_t counter_len = strlen(processor); 767 counter_len += strlen(counter_name); 768 counter_len += OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN; // "\\%s(%s)\\%s" 769 770 DWORD index; 771 char* tmp; 772 const char* instances = enumerate_cpu_instances(); 773 for (index = 0, tmp = const_cast<char*>(instances); *tmp != '\0'; tmp = &tmp[strlen(tmp) + 1], index++) { 774 const size_t tmp_len = strlen(tmp); 775 char* counter_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, counter_len + tmp_len + 1); 776 if (counter_path == NULL) { 777 return OS_ERR; 778 } 779 const size_t jio_snprintf_result = jio_snprintf(counter_path, 780 counter_len + tmp_len + 1, 781 OBJECT_WITH_INSTANCES_COUNTER_FMT, 782 processor, 783 tmp, // instance "0", "1", .."_Total" 784 counter_name); 785 assert(counter_len + tmp_len == jio_snprintf_result, "invariant"); 786 if (add_counter(cpu_query, &cpu_query->counters[index], counter_path, false) != OS_OK) { 787 // performance counter is disabled in registry and not accessible via PerfLib 788 log_error_message_on_no_PDH_artifact(counter_path); 789 // return OS_OK to have the system continue to run without the missing counter 790 return OS_OK; 791 } 792 } 793 cpu_query->initialized = true; 794 // Query once to initialize the counters which require at least two samples 795 // (like the % CPU usage) to calculate correctly. 796 collect_query_data(cpu_query); 797 return OS_OK; 798 } 799 800 static int initialize_cpu_query(MultiCounterQueryP cpu_query, DWORD pdh_counter_idx) { 801 assert(cpu_query != NULL, "invariant"); 802 assert(!cpu_query->initialized, "invariant"); 803 const int logical_cpu_count = number_of_logical_cpus(); 804 assert(logical_cpu_count >= os::processor_count(), "invariant"); 805 // we also add another counter for instance "_Total" 806 if (allocate_counters(cpu_query, logical_cpu_count + 1) != OS_OK) { 807 return OS_ERR; 808 } 809 assert(cpu_query->noOfCounters == logical_cpu_count + 1, "invariant"); 810 return initialize_cpu_query_counters(cpu_query, pdh_counter_idx); 811 } 812 813 static int initialize_process_counter(MultiCounterQuerySetP query_set, int slot_index, DWORD pdh_counter_index) { 814 char* localized_process_object; 815 if (lookup_name_by_index(PDH_PROCESS_IDX, &localized_process_object) != OS_OK) { 816 return OS_ERR; 817 } 818 assert(localized_process_object != NULL, "invariant"); 819 char* localized_counter_name; 820 if (lookup_name_by_index(pdh_counter_index, &localized_counter_name) != OS_OK) { 821 return OS_ERR; 822 } 823 assert(localized_counter_name != NULL, "invariant"); 824 for (int i = 0; i < query_set->size; ++i) { 825 char instanceIndexBuffer[32]; 826 const char* counter_path = make_fully_qualified_counter_path(localized_process_object, 827 localized_counter_name, 828 process_image_name, 829 itoa(i, instanceIndexBuffer, 10)); 830 if (counter_path == NULL) { 831 return OS_ERR; 832 } 833 MultiCounterQueryP const query = &query_set->queries[i]; 834 if (add_process_counter(query, slot_index, counter_path, true)) { 835 return OS_ERR; 836 } 837 } 838 return OS_OK; 839 } 840 841 static CounterQueryP create_counter_query(DWORD pdh_object_idx, DWORD pdh_counter_idx) { 842 assert(is_valid_pdh_index(pdh_object_idx), "invariant"); 843 assert(is_valid_pdh_index(pdh_counter_idx), "invariant"); 844 CounterQueryP const query = create_counter_query(); 845 const char* object = pdh_localized_artifact(pdh_object_idx); 846 assert(object != NULL, "invariant"); 847 const char* counter = pdh_localized_artifact(pdh_counter_idx); 848 assert(counter != NULL, "invariant"); 849 const char* full_counter_path = make_fully_qualified_counter_path(object, counter); 850 assert(full_counter_path != NULL, "invariant"); 851 add_counter(query, full_counter_path, true); 852 return query; 853 } 854 855 static void deallocate() { 856 deallocate_pdh_constants(); 857 PdhDll::PdhDetach(); 858 } 859 860 static LONG critical_section = 0; 861 static LONG reference_count = 0; 862 static bool pdh_initialized = false; 863 864 static void on_initialization_failure() { 865 // still holder of critical section 866 deallocate(); 867 InterlockedExchangeAdd(&reference_count, -1); 868 } 869 870 static OSReturn initialize() { 871 ResourceMark rm; 872 if (!PdhDll::PdhAttach()) { 873 return OS_ERR; 874 } 875 if (allocate_pdh_constants() != OS_OK) { 876 on_initialization_failure(); 877 return OS_ERR; 878 } 879 return OS_OK; 880 } 881 882 /* 883 * Helper to initialize the PDH library, function pointers, constants and counters. 884 * 885 * Reference counting allows for unloading of pdh.dll granted all sessions use the pair: 886 * 887 * pdh_acquire(); 888 * pdh_release(); 889 * 890 * @return OS_OK if successful, OS_ERR on failure. 891 */ 892 static bool pdh_acquire() { 893 while (InterlockedCompareExchange(&critical_section, 1, 0) == 1); 894 InterlockedExchangeAdd(&reference_count, 1); 895 if (pdh_initialized) { 896 return true; 897 } 898 const OSReturn ret = initialize(); 899 if (OS_OK == ret) { 900 pdh_initialized = true; 901 } 902 while (InterlockedCompareExchange(&critical_section, 0, 1) == 0); 903 return ret == OS_OK; 904 } 905 906 static void pdh_release() { 907 while (InterlockedCompareExchange(&critical_section, 1, 0) == 1); 908 const LONG prev_ref_count = InterlockedExchangeAdd(&reference_count, -1); 909 if (1 == prev_ref_count) { 910 deallocate(); 911 pdh_initialized = false; 912 } 913 while (InterlockedCompareExchange(&critical_section, 0, 1) == 0); 914 } 915 916 class CPUPerformanceInterface::CPUPerformance : public CHeapObj<mtInternal> { 917 friend class CPUPerformanceInterface; 918 private: 919 CounterQueryP _context_switches; 920 MultiCounterQuerySetP _process_cpu_load; 921 MultiCounterQueryP _machine_cpu_load; 922 923 int cpu_load(int which_logical_cpu, double* cpu_load); 924 int context_switch_rate(double* rate); 925 int cpu_load_total_process(double* cpu_load); 926 int cpu_loads_process(double* jvm_user_load, double* jvm_kernel_load, double* psystemTotalLoad); 927 CPUPerformance(); 928 ~CPUPerformance(); 929 bool initialize(); 930 }; 931 932 class SystemProcessInterface::SystemProcesses : public CHeapObj<mtInternal> { 933 friend class SystemProcessInterface; 934 private: 935 class ProcessIterator : public CHeapObj<mtInternal> { 936 friend class SystemProcessInterface::SystemProcesses; 937 private: 938 HANDLE _hProcessSnap; 939 PROCESSENTRY32 _pe32; 940 BOOL _valid; 941 char _exePath[MAX_PATH]; 942 ProcessIterator(); 943 ~ProcessIterator(); 944 bool initialize(); 945 946 int current(SystemProcess* const process_info); 947 int next_process(); 948 bool is_valid() const { return _valid != FALSE; } 949 char* allocate_string(const char* str) const; 950 int snapshot(); 951 }; 952 953 ProcessIterator* _iterator; 954 SystemProcesses(); 955 ~SystemProcesses(); 956 bool initialize(); 957 958 // information about system processes 959 int system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const; 960 }; 961 962 CPUPerformanceInterface::CPUPerformance::CPUPerformance() : _context_switches(NULL), _process_cpu_load(NULL), _machine_cpu_load(NULL) {} 963 964 bool CPUPerformanceInterface::CPUPerformance::initialize() { 965 if (!pdh_acquire()) { 966 return false; 967 } 968 _context_switches = create_counter_query(PDH_SYSTEM_IDX, PDH_CONTEXT_SWITCH_RATE_IDX); 969 if (_context_switches == NULL) { 970 return false; 971 } 972 _process_cpu_load = create_process_counter_query(); 973 if (_process_cpu_load == NULL) { 974 return false; 975 } 976 if (allocate_counters(_process_cpu_load, 2) != OS_OK) { 977 return false; 978 } 979 if (initialize_process_counter(_process_cpu_load, 0, PDH_PROCESSOR_TIME_IDX) != OS_OK) { 980 return false; 981 } 982 if (initialize_process_counter(_process_cpu_load, 1, PDH_PRIV_PROCESSOR_TIME_IDX) != OS_OK) { 983 return false; 984 } 985 _process_cpu_load->initialized = true; 986 987 _machine_cpu_load = create_multi_counter_query(); 988 if (_machine_cpu_load == NULL) { 989 return false; 990 } 991 if (initialize_cpu_query(_machine_cpu_load, PDH_PROCESSOR_TIME_IDX) != OS_OK) { 992 return false; 993 } 994 return true; 995 } 996 997 CPUPerformanceInterface::CPUPerformance::~CPUPerformance() { 998 if (_context_switches != NULL) { 999 destroy_counter_query(_context_switches); 1000 _context_switches = NULL; 1001 } 1002 if (_process_cpu_load != NULL) { 1003 destroy_counter_query(_process_cpu_load); 1004 _process_cpu_load = NULL; 1005 } 1006 if (_machine_cpu_load != NULL) { 1007 destroy_counter_query(_machine_cpu_load); 1008 _machine_cpu_load = NULL; 1009 } 1010 pdh_release(); 1011 } 1012 1013 CPUPerformanceInterface::CPUPerformanceInterface() { 1014 _impl = NULL; 1015 } 1016 1017 bool CPUPerformanceInterface::initialize() { 1018 _impl = new CPUPerformanceInterface::CPUPerformance(); 1019 return _impl != NULL && _impl->initialize(); 1020 } 1021 1022 CPUPerformanceInterface::~CPUPerformanceInterface() { 1023 if (_impl != NULL) { 1024 delete _impl; 1025 } 1026 } 1027 1028 int CPUPerformanceInterface::cpu_load(int which_logical_cpu, double* cpu_load) const { 1029 return _impl->cpu_load(which_logical_cpu, cpu_load); 1030 } 1031 1032 int CPUPerformanceInterface::context_switch_rate(double* rate) const { 1033 return _impl->context_switch_rate(rate); 1034 } 1035 1036 int CPUPerformanceInterface::cpu_load_total_process(double* cpu_load) const { 1037 return _impl->cpu_load_total_process(cpu_load); 1038 } 1039 1040 int CPUPerformanceInterface::cpu_loads_process(double* pjvmUserLoad, 1041 double* pjvmKernelLoad, 1042 double* psystemTotalLoad) const { 1043 return _impl->cpu_loads_process(pjvmUserLoad, pjvmKernelLoad, psystemTotalLoad); 1044 } 1045 1046 int CPUPerformanceInterface::CPUPerformance::cpu_load(int which_logical_cpu, double* cpu_load) { 1047 assert(_machine_cpu_load != NULL, "invariant"); 1048 assert(which_logical_cpu < _machine_cpu_load->noOfCounters, "invariant"); 1049 *cpu_load = .0; 1050 if (!_machine_cpu_load->initialized) { 1051 return OS_ERR; 1052 } 1053 if (collect_query_data(_machine_cpu_load)) { 1054 return OS_ERR; 1055 } 1056 // -1 is total (all cpus) 1057 const int counter_idx = -1 == which_logical_cpu ? _machine_cpu_load->noOfCounters - 1 : which_logical_cpu; 1058 PDH_FMT_COUNTERVALUE counter_value; 1059 formatted_counter_value(_machine_cpu_load->counters[counter_idx], PDH_FMT_DOUBLE, &counter_value); 1060 *cpu_load = counter_value.doubleValue / 100; 1061 return OS_OK; 1062 } 1063 1064 int CPUPerformanceInterface::CPUPerformance::cpu_load_total_process(double* cpu_load) { 1065 assert(_process_cpu_load != NULL, "invariant"); 1066 *cpu_load = .0; 1067 if (!_process_cpu_load->initialized) { 1068 return OS_ERR; 1069 } 1070 if (collect_process_query_data(_process_cpu_load)) { 1071 return OS_ERR; 1072 } 1073 PDH_FMT_COUNTERVALUE counter_value; 1074 if (query_process_counter(_process_cpu_load, 0, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &counter_value) != OS_OK) { 1075 return OS_ERR; 1076 } 1077 double process_load = counter_value.doubleValue / cpu_factor(); 1078 process_load = MIN2<double>(1, process_load); 1079 process_load = MAX2<double>(0, process_load); 1080 *cpu_load = process_load; 1081 return OS_OK; 1082 } 1083 1084 int CPUPerformanceInterface::CPUPerformance::cpu_loads_process(double* pjvmUserLoad, 1085 double* pjvmKernelLoad, 1086 double* psystemTotalLoad) { 1087 assert(pjvmUserLoad != NULL, "pjvmUserLoad is NULL!"); 1088 assert(pjvmKernelLoad != NULL, "pjvmKernelLoad is NULL!"); 1089 assert(psystemTotalLoad != NULL, "psystemTotalLoad is NULL!"); 1090 *pjvmUserLoad = .0; 1091 *pjvmKernelLoad = .0; 1092 *psystemTotalLoad = .0; 1093 if (!_process_cpu_load->initialized) { 1094 return OS_ERR; 1095 } 1096 if (collect_process_query_data(_process_cpu_load)) { 1097 return OS_ERR; 1098 } 1099 double process_load = .0; 1100 PDH_FMT_COUNTERVALUE counter_value; 1101 // Read PDH_PROCESSOR_TIME_IDX 1102 if (query_process_counter(_process_cpu_load, 0, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &counter_value) != OS_OK) { 1103 return OS_ERR; 1104 } 1105 process_load = counter_value.doubleValue / cpu_factor(); 1106 process_load = MIN2<double>(1, process_load); 1107 process_load = MAX2<double>(0, process_load); 1108 // Read PDH_PRIV_PROCESSOR_TIME_IDX 1109 if (query_process_counter(_process_cpu_load, 1, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &counter_value) != OS_OK) { 1110 return OS_ERR; 1111 } 1112 double kernel_load = counter_value.doubleValue / cpu_factor(); 1113 kernel_load = MIN2<double>(1, kernel_load); 1114 kernel_load = MAX2<double>(0, kernel_load); 1115 *pjvmKernelLoad = kernel_load; 1116 1117 double user_load = process_load - kernel_load; 1118 user_load = MIN2<double>(1, user_load); 1119 user_load = MAX2<double>(0, user_load); 1120 *pjvmUserLoad = user_load; 1121 1122 if (collect_query_data(_machine_cpu_load)) { 1123 return OS_ERR; 1124 } 1125 if (formatted_counter_value(_machine_cpu_load->counters[_machine_cpu_load->noOfCounters - 1], PDH_FMT_DOUBLE, &counter_value) != OS_OK) { 1126 return OS_ERR; 1127 } 1128 double machine_load = counter_value.doubleValue / 100; 1129 assert(machine_load >= 0, "machine_load is negative!"); 1130 // clamp at user+system and 1.0 1131 if (*pjvmKernelLoad + *pjvmUserLoad > machine_load) { 1132 machine_load = MIN2(*pjvmKernelLoad + *pjvmUserLoad, 1.0); 1133 } 1134 *psystemTotalLoad = machine_load; 1135 return OS_OK; 1136 } 1137 1138 int CPUPerformanceInterface::CPUPerformance::context_switch_rate(double* rate) { 1139 assert(rate != NULL, "invariant"); 1140 *rate = .0; 1141 if (!_context_switches->initialized) { 1142 return OS_ERR; 1143 } 1144 if (collect_query_data(_context_switches) != OS_OK) { 1145 return OS_ERR; 1146 } 1147 PDH_FMT_COUNTERVALUE counter_value; 1148 if (formatted_counter_value(_context_switches->counter, PDH_FMT_DOUBLE, &counter_value) != OS_OK) { 1149 return OS_ERR; 1150 } 1151 *rate = counter_value.doubleValue; 1152 return OS_OK; 1153 } 1154 1155 SystemProcessInterface::SystemProcesses::ProcessIterator::ProcessIterator() { 1156 _hProcessSnap = INVALID_HANDLE_VALUE; 1157 _valid = FALSE; 1158 _pe32.dwSize = sizeof(PROCESSENTRY32); 1159 } 1160 1161 bool SystemProcessInterface::SystemProcesses::ProcessIterator::initialize() { 1162 return true; 1163 } 1164 1165 int SystemProcessInterface::SystemProcesses::ProcessIterator::snapshot() { 1166 // take snapshot of all process in the system 1167 _hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 1168 if (_hProcessSnap == INVALID_HANDLE_VALUE) { 1169 return OS_ERR; 1170 } 1171 // step to first process 1172 _valid = Process32First(_hProcessSnap, &_pe32); 1173 return is_valid() ? OS_OK : OS_ERR; 1174 } 1175 1176 SystemProcessInterface::SystemProcesses::ProcessIterator::~ProcessIterator() { 1177 if (_hProcessSnap != INVALID_HANDLE_VALUE) { 1178 CloseHandle(_hProcessSnap); 1179 } 1180 } 1181 1182 int SystemProcessInterface::SystemProcesses::ProcessIterator::current(SystemProcess* process_info) { 1183 assert(is_valid(), "no current process to be fetched!"); 1184 assert(process_info != NULL, "process_info is NULL!"); 1185 char* exePath = NULL; 1186 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, _pe32.th32ProcessID); 1187 if (hProcess != NULL) { 1188 HMODULE hMod; 1189 DWORD cbNeeded; 1190 if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded) != 0) { 1191 if (GetModuleFileNameExA(hProcess, hMod, _exePath, sizeof(_exePath)) != 0) { 1192 exePath = _exePath; 1193 } 1194 } 1195 CloseHandle (hProcess); 1196 } 1197 process_info->set_pid((int)_pe32.th32ProcessID); 1198 process_info->set_name(allocate_string(_pe32.szExeFile)); 1199 process_info->set_path(allocate_string(exePath)); 1200 return OS_OK; 1201 } 1202 1203 char* SystemProcessInterface::SystemProcesses::ProcessIterator::allocate_string(const char* str) const { 1204 if (str != NULL) { 1205 size_t len = strlen(str); 1206 char* tmp = NEW_C_HEAP_ARRAY(char, len+1, mtInternal); 1207 if (NULL == tmp) { 1208 return NULL; 1209 } 1210 strncpy(tmp, str, len); 1211 tmp[len] = '\0'; 1212 return tmp; 1213 } 1214 return NULL; 1215 } 1216 1217 int SystemProcessInterface::SystemProcesses::ProcessIterator::next_process() { 1218 _valid = Process32Next(_hProcessSnap, &_pe32); 1219 return OS_OK; 1220 } 1221 1222 SystemProcessInterface::SystemProcesses::SystemProcesses() { 1223 _iterator = NULL; 1224 } 1225 1226 bool SystemProcessInterface::SystemProcesses::initialize() { 1227 _iterator = new SystemProcessInterface::SystemProcesses::ProcessIterator(); 1228 return _iterator != NULL && _iterator->initialize(); 1229 } 1230 1231 SystemProcessInterface::SystemProcesses::~SystemProcesses() { 1232 if (_iterator != NULL) { 1233 delete _iterator; 1234 _iterator = NULL; 1235 } 1236 } 1237 1238 int SystemProcessInterface::SystemProcesses::system_processes(SystemProcess** system_processes, 1239 int* no_of_sys_processes) const { 1240 assert(system_processes != NULL, "system_processes pointer is NULL!"); 1241 assert(no_of_sys_processes != NULL, "system_processes counter pointers is NULL!"); 1242 assert(_iterator != NULL, "iterator is NULL!"); 1243 1244 // initialize pointers 1245 *no_of_sys_processes = 0; 1246 *system_processes = NULL; 1247 1248 // take process snapshot 1249 if (_iterator->snapshot() != OS_OK) { 1250 return OS_ERR; 1251 } 1252 1253 while (_iterator->is_valid()) { 1254 SystemProcess* tmp = new SystemProcess(); 1255 _iterator->current(tmp); 1256 1257 //if already existing head 1258 if (*system_processes != NULL) { 1259 //move "first to second" 1260 tmp->set_next(*system_processes); 1261 } 1262 // new head 1263 *system_processes = tmp; 1264 // increment 1265 (*no_of_sys_processes)++; 1266 // step forward 1267 _iterator->next_process(); 1268 } 1269 return OS_OK; 1270 } 1271 1272 int SystemProcessInterface::system_processes(SystemProcess** system_procs, 1273 int* no_of_sys_processes) const { 1274 return _impl->system_processes(system_procs, no_of_sys_processes); 1275 } 1276 1277 SystemProcessInterface::SystemProcessInterface() { 1278 _impl = NULL; 1279 } 1280 1281 bool SystemProcessInterface::initialize() { 1282 _impl = new SystemProcessInterface::SystemProcesses(); 1283 return _impl != NULL && _impl->initialize(); 1284 } 1285 1286 SystemProcessInterface::~SystemProcessInterface() { 1287 if (_impl != NULL) { 1288 delete _impl; 1289 } 1290 } 1291 1292 CPUInformationInterface::CPUInformationInterface() { 1293 _cpu_info = NULL; 1294 } 1295 1296 bool CPUInformationInterface::initialize() { 1297 _cpu_info = new CPUInformation(); 1298 if (NULL == _cpu_info) { 1299 return false; 1300 } 1301 _cpu_info->set_number_of_hardware_threads(VM_Version_Ext::number_of_threads()); 1302 _cpu_info->set_number_of_cores(VM_Version_Ext::number_of_cores()); 1303 _cpu_info->set_number_of_sockets(VM_Version_Ext::number_of_sockets()); 1304 _cpu_info->set_cpu_name(VM_Version_Ext::cpu_name()); 1305 _cpu_info->set_cpu_description(VM_Version_Ext::cpu_description()); 1306 return true; 1307 } 1308 1309 CPUInformationInterface::~CPUInformationInterface() { 1310 if (_cpu_info != NULL) { 1311 const char* cpu_name = _cpu_info->cpu_name(); 1312 if (cpu_name != NULL) { 1313 FREE_C_HEAP_ARRAY(char, cpu_name); 1314 _cpu_info->set_cpu_name(NULL); 1315 } 1316 const char* cpu_desc = _cpu_info->cpu_description(); 1317 if (cpu_desc != NULL) { 1318 FREE_C_HEAP_ARRAY(char, cpu_desc); 1319 _cpu_info->set_cpu_description(NULL); 1320 } 1321 delete _cpu_info; 1322 _cpu_info = NULL; 1323 } 1324 } 1325 1326 int CPUInformationInterface::cpu_information(CPUInformation& cpu_info) { 1327 if (NULL == _cpu_info) { 1328 return OS_ERR; 1329 } 1330 cpu_info = *_cpu_info; // shallow copy assignment 1331 return OS_OK; 1332 }