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