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