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