mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-16 10:53:31 +00:00
1426 lines
50 KiB
C++
1426 lines
50 KiB
C++
/*
|
|
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*
|
|
*/
|
|
|
|
#include "precompiled.hpp"
|
|
#include "iphlp_interface.hpp"
|
|
#include "logging/log.hpp"
|
|
#include "memory/allocation.inline.hpp"
|
|
#include "memory/resourceArea.hpp"
|
|
#include "pdh_interface.hpp"
|
|
#include "runtime/os_perf.hpp"
|
|
#include "runtime/os.hpp"
|
|
#include "utilities/globalDefinitions.hpp"
|
|
#include "utilities/macros.hpp"
|
|
#include CPU_HEADER(vm_version_ext)
|
|
#include <math.h>
|
|
#include <psapi.h>
|
|
#include <TlHelp32.h>
|
|
|
|
/*
|
|
* Windows provides a vast plethora of performance objects and counters,
|
|
* consumption of which is assisted using the Performance Data Helper (PDH) interface.
|
|
* We import a selected few api entry points from PDH, see pdh_interface.hpp.
|
|
*
|
|
* The code located in this file is to a large extent an abstraction over much of the
|
|
* plumbing needed to start consuming an object and/or counter of choice.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* How to use:
|
|
* 1. Create query
|
|
* 2. Add counters to the query
|
|
* 3. Collect the performance data using the query
|
|
* 4. Display the performance data using the counters associated with the query
|
|
* 5. Destroy query (counter destruction implied)
|
|
*/
|
|
|
|
/*
|
|
* Every PDH artifact, like processor, process, thread, memory, and so forth are
|
|
* identified with an index that is always the same irrespective
|
|
* of the localized version of the operating system or service pack installed.
|
|
* INFO: Using PDH APIs Correctly in a Localized Language (Q287159)
|
|
* http://support.microsoft.com/default.aspx?scid=kb;EN-US;q287159
|
|
*
|
|
* To find the correct index for an object or counter, inspect the registry key / value:
|
|
* [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009\Counter]
|
|
*
|
|
* some common PDH indexes
|
|
*/
|
|
static const DWORD PDH_PROCESSOR_IDX = 238;
|
|
static const DWORD PDH_PROCESSOR_TIME_IDX = 6;
|
|
static const DWORD PDH_PRIV_PROCESSOR_TIME_IDX = 144;
|
|
static const DWORD PDH_PROCESS_IDX = 230;
|
|
static const DWORD PDH_ID_PROCESS_IDX = 784;
|
|
static const DWORD PDH_CONTEXT_SWITCH_RATE_IDX = 146;
|
|
static const DWORD PDH_SYSTEM_IDX = 2;
|
|
|
|
/* useful pdh fmt's */
|
|
static const char* const OBJECT_COUNTER_FMT = "\\%s\\%s";
|
|
static const size_t OBJECT_COUNTER_FMT_LEN = 2;
|
|
static const char* const OBJECT_WITH_INSTANCES_COUNTER_FMT = "\\%s(%s)\\%s";
|
|
static const size_t OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN = 4;
|
|
static const char* const PROCESS_OBJECT_INSTANCE_COUNTER_FMT = "\\%s(%s#%s)\\%s";
|
|
static const size_t PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN = 5;
|
|
|
|
static const char* process_image_name = NULL; // for example, "java" but could have another image name
|
|
static char* pdh_IDProcess_counter_fmt = NULL; // "\Process(java#%d)\ID Process" */
|
|
|
|
// Need to limit how often we update a query to minimize the heisenberg effect.
|
|
// (PDH behaves erratically if the counters are queried too often, especially counters that
|
|
// store and use values from two consecutive updates, like cpu load.)
|
|
static const int min_update_interval_millis = 500;
|
|
|
|
/*
|
|
* Structs for PDH queries.
|
|
*/
|
|
typedef struct {
|
|
HQUERY query;
|
|
s8 lastUpdate; // Last time query was updated (current millis).
|
|
} UpdateQueryS, *UpdateQueryP;
|
|
|
|
|
|
typedef struct {
|
|
UpdateQueryS query;
|
|
HCOUNTER counter;
|
|
bool initialized;
|
|
} CounterQueryS, *CounterQueryP;
|
|
|
|
typedef struct {
|
|
UpdateQueryS query;
|
|
HCOUNTER* counters;
|
|
int noOfCounters;
|
|
bool initialized;
|
|
} MultiCounterQueryS, *MultiCounterQueryP;
|
|
|
|
typedef struct {
|
|
MultiCounterQueryP queries;
|
|
int size;
|
|
bool initialized;
|
|
} MultiCounterQuerySetS, *MultiCounterQuerySetP;
|
|
|
|
typedef struct {
|
|
MultiCounterQuerySetS set;
|
|
int process_index;
|
|
} ProcessQueryS, *ProcessQueryP;
|
|
|
|
static void pdh_cleanup(HQUERY* const query, HCOUNTER* const counter) {
|
|
if (counter != NULL && *counter != NULL) {
|
|
PdhDll::PdhRemoveCounter(*counter);
|
|
*counter = NULL;
|
|
}
|
|
if (query != NULL && *query != NULL) {
|
|
PdhDll::PdhCloseQuery(*query);
|
|
*query = NULL;
|
|
}
|
|
}
|
|
|
|
static CounterQueryP create_counter_query() {
|
|
CounterQueryP const query = NEW_C_HEAP_OBJ(CounterQueryS, mtInternal);
|
|
memset(query, 0, sizeof(CounterQueryS));
|
|
return query;
|
|
}
|
|
|
|
static void destroy_counter_query(CounterQueryP query) {
|
|
assert(query != NULL, "invariant");
|
|
pdh_cleanup(&query->query.query, &query->counter);
|
|
FREE_C_HEAP_OBJ(query);
|
|
}
|
|
|
|
static MultiCounterQueryP create_multi_counter_query() {
|
|
MultiCounterQueryP const query = NEW_C_HEAP_ARRAY(MultiCounterQueryS, 1, mtInternal);
|
|
memset(query, 0, sizeof(MultiCounterQueryS));
|
|
return query;
|
|
}
|
|
|
|
static void destroy_counter_query(MultiCounterQueryP counter_query) {
|
|
if (counter_query != NULL) {
|
|
for (int i = 0; i < counter_query->noOfCounters; ++i) {
|
|
pdh_cleanup(NULL, &counter_query->counters[i]);
|
|
}
|
|
FREE_C_HEAP_ARRAY(char, counter_query->counters);
|
|
pdh_cleanup(&counter_query->query.query, NULL);
|
|
FREE_C_HEAP_ARRAY(MultiCounterQueryS, counter_query);
|
|
}
|
|
}
|
|
|
|
static void destroy_multi_counter_query(MultiCounterQuerySetP counter_query_set) {
|
|
for (int i = 0; i < counter_query_set->size; i++) {
|
|
for (int j = 0; j < counter_query_set->queries[i].noOfCounters; ++j) {
|
|
pdh_cleanup(NULL, &counter_query_set->queries[i].counters[j]);
|
|
}
|
|
FREE_C_HEAP_ARRAY(char, counter_query_set->queries[i].counters);
|
|
pdh_cleanup(&counter_query_set->queries[i].query.query, NULL);
|
|
}
|
|
FREE_C_HEAP_ARRAY(MultiCounterQueryS, counter_query_set->queries);
|
|
}
|
|
|
|
static void destroy_counter_query(MultiCounterQuerySetP counter_query_set) {
|
|
destroy_multi_counter_query(counter_query_set);
|
|
FREE_C_HEAP_ARRAY(MultiCounterQuerySetS, counter_query_set);
|
|
}
|
|
|
|
static void destroy_counter_query(ProcessQueryP process_query) {
|
|
destroy_multi_counter_query(&process_query->set);
|
|
FREE_C_HEAP_OBJ(process_query);
|
|
}
|
|
|
|
static int open_query(HQUERY* query) {
|
|
return PdhDll::PdhOpenQuery(NULL, 0, query);
|
|
}
|
|
|
|
template <typename QueryP>
|
|
static int open_query(QueryP query) {
|
|
return open_query(&query->query);
|
|
}
|
|
|
|
static void allocate_counters(MultiCounterQueryP query, size_t nofCounters) {
|
|
assert(query != NULL, "invariant");
|
|
assert(!query->initialized, "invariant");
|
|
assert(0 == query->noOfCounters, "invariant");
|
|
assert(query->counters == NULL, "invariant");
|
|
query->counters = NEW_C_HEAP_ARRAY(HCOUNTER, nofCounters, mtInternal);
|
|
memset(query->counters, 0, nofCounters * sizeof(HCOUNTER));
|
|
query->noOfCounters = (int)nofCounters;
|
|
}
|
|
|
|
static void allocate_counters(MultiCounterQuerySetP query_set, size_t nofCounters) {
|
|
assert(query_set != NULL, "invariant");
|
|
assert(!query_set->initialized, "invariant");
|
|
for (int i = 0; i < query_set->size; ++i) {
|
|
allocate_counters(&query_set->queries[i], nofCounters);
|
|
}
|
|
}
|
|
|
|
static void allocate_counters(ProcessQueryP process_query, size_t nofCounters) {
|
|
assert(process_query != NULL, "invariant");
|
|
allocate_counters(&process_query->set, nofCounters);
|
|
}
|
|
|
|
static void deallocate_counters(MultiCounterQueryP query) {
|
|
FREE_C_HEAP_ARRAY(char, query->counters);
|
|
query->counters = NULL;
|
|
query->noOfCounters = 0;
|
|
}
|
|
|
|
static OSReturn add_counter(UpdateQueryP query, HCOUNTER* counter, const char* path, bool first_sample_on_init) {
|
|
assert(query != NULL, "invariant");
|
|
assert(counter != NULL, "invariant");
|
|
assert(path != NULL, "invariant");
|
|
if (query->query == NULL) {
|
|
if (open_query(query) != ERROR_SUCCESS) {
|
|
return OS_ERR;
|
|
}
|
|
}
|
|
assert(query->query != NULL, "invariant");
|
|
PDH_STATUS status = PdhDll::PdhAddCounter(query->query, path, 0, counter);
|
|
if (PDH_CSTATUS_NO_OBJECT == status || PDH_CSTATUS_NO_COUNTER == status) {
|
|
return OS_ERR;
|
|
}
|
|
/*
|
|
* According to the MSDN documentation, rate counters must be read twice:
|
|
*
|
|
* "Obtaining the value of rate counters such as Page faults/sec requires that
|
|
* PdhCollectQueryData be called twice, with a specific time interval between
|
|
* the two calls, before calling PdhGetFormattedCounterValue. Call Sleep to
|
|
* implement the waiting period between the two calls to PdhCollectQueryData."
|
|
*
|
|
* Take the first sample here already to allow for the next "real" sample
|
|
* to succeed.
|
|
*/
|
|
if (first_sample_on_init) {
|
|
PdhDll::PdhCollectQueryData(query->query);
|
|
}
|
|
return OS_OK;
|
|
}
|
|
|
|
template <typename QueryP>
|
|
static OSReturn add_counter(QueryP counter_query, HCOUNTER* counter, const char* path, bool first_sample_on_init) {
|
|
assert(counter_query != NULL, "invariant");
|
|
assert(counter != NULL, "invariant");
|
|
assert(path != NULL, "invariant");
|
|
return add_counter(&counter_query->query, counter, path, first_sample_on_init);
|
|
}
|
|
|
|
static OSReturn add_counter(CounterQueryP counter_query, const char* path, bool first_sample_on_init) {
|
|
if (add_counter(counter_query, &counter_query->counter, path, first_sample_on_init) != OS_OK) {
|
|
// performance counter might be disabled in the registry
|
|
return OS_ERR;
|
|
}
|
|
counter_query->initialized = true;
|
|
return OS_OK;
|
|
}
|
|
|
|
static OSReturn add_process_counter(MultiCounterQueryP query, int slot_index, const char* path, bool first_sample_on_init) {
|
|
assert(query != NULL, "invariant");
|
|
assert(slot_index < query->noOfCounters, "invariant");
|
|
assert(query->counters[slot_index] == NULL, "invariant");
|
|
const OSReturn ret = add_counter(query, &query->counters[slot_index], path, first_sample_on_init);
|
|
if (OS_OK == ret) {
|
|
if (slot_index + 1 == query->noOfCounters) {
|
|
query->initialized = true;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int collect_query_data(UpdateQueryP update_query) {
|
|
assert(update_query != NULL, "invariant");
|
|
const s8 now = os::javaTimeMillis();
|
|
if (now - update_query->lastUpdate > min_update_interval_millis) {
|
|
if (PdhDll::PdhCollectQueryData(update_query->query) != ERROR_SUCCESS) {
|
|
return OS_ERR;
|
|
}
|
|
update_query->lastUpdate = now;
|
|
}
|
|
return OS_OK;
|
|
}
|
|
|
|
template <typename Query>
|
|
static int collect_query_data(Query* counter_query) {
|
|
assert(counter_query != NULL, "invariant");
|
|
return collect_query_data(&counter_query->query);
|
|
}
|
|
|
|
static int formatted_counter_value(HCOUNTER counter, DWORD format, PDH_FMT_COUNTERVALUE* const value) {
|
|
assert(value != NULL, "invariant");
|
|
if (PdhDll::PdhGetFormattedCounterValue(counter, format, NULL, value) != ERROR_SUCCESS) {
|
|
return OS_ERR;
|
|
}
|
|
return OS_OK;
|
|
}
|
|
|
|
/*
|
|
* Working against the Process object and it's related counters is inherently problematic
|
|
* when using the PDH API:
|
|
*
|
|
* Using PDH, a process is not primarily identified by the process id,
|
|
* but with a sequential number, for example \Process(java#0), \Process(java#1), ...
|
|
* The really bad part is that this list is reset as soon as a process exits:
|
|
* If \Process(java#1) exits, \Process(java#3) now becomes \Process(java#2) etc.
|
|
*
|
|
* The PDH api requires a process identifier to be submitted when registering
|
|
* a query, but as soon as the list resets, the query is invalidated (since the name changed).
|
|
*
|
|
* Solution:
|
|
* The #number identifier for a Process query can only decrease after process creation.
|
|
*
|
|
* We therefore create an array of counter queries for all process object instances
|
|
* up to and including ourselves:
|
|
*
|
|
* Ex. we come in as third process instance (java#2), we then create and register
|
|
* queries for the following Process object instances:
|
|
* java#0, java#1, java#2
|
|
*
|
|
* current_query_index_for_process() keeps track of the current "correct" query
|
|
* (in order to keep this index valid when the list resets from underneath,
|
|
* ensure to call current_query_index_for_process() before every query involving
|
|
* Process object instance data).
|
|
*
|
|
* if unable to query, returns OS_ERR(-1)
|
|
*/
|
|
static int current_query_index_for_process() {
|
|
assert(process_image_name != NULL, "invariant");
|
|
assert(pdh_IDProcess_counter_fmt != NULL, "invariant");
|
|
HQUERY tmpQuery = NULL;
|
|
if (open_query(&tmpQuery) != ERROR_SUCCESS) {
|
|
return OS_ERR;
|
|
}
|
|
char counter[512];
|
|
HCOUNTER handle_counter = NULL;
|
|
// iterate over all instance indexes and try to find our own pid
|
|
for (int index = 0; index < max_intx; index++) {
|
|
jio_snprintf(counter, sizeof(counter) - 1, pdh_IDProcess_counter_fmt, index);
|
|
assert(strlen(counter) < sizeof(counter), "invariant");
|
|
if (PdhDll::PdhAddCounter(tmpQuery, counter, 0, &handle_counter) != ERROR_SUCCESS) {
|
|
pdh_cleanup(&tmpQuery, &handle_counter);
|
|
return OS_ERR;
|
|
}
|
|
const PDH_STATUS res = PdhDll::PdhCollectQueryData(tmpQuery);
|
|
if (res == PDH_INVALID_HANDLE || res == PDH_NO_DATA) {
|
|
pdh_cleanup(&tmpQuery, &handle_counter);
|
|
return OS_ERR;
|
|
} else {
|
|
PDH_FMT_COUNTERVALUE counter_value;
|
|
formatted_counter_value(handle_counter, PDH_FMT_LONG, &counter_value);
|
|
pdh_cleanup(NULL, &handle_counter);
|
|
if ((LONG)os::current_process_id() == counter_value.longValue) {
|
|
pdh_cleanup(&tmpQuery, NULL);
|
|
return index;
|
|
}
|
|
}
|
|
}
|
|
pdh_cleanup(&tmpQuery, NULL);
|
|
return OS_ERR;
|
|
}
|
|
|
|
static ProcessQueryP create_process_query() {
|
|
const int current_process_idx = current_query_index_for_process();
|
|
if (OS_ERR == current_process_idx) {
|
|
return NULL;
|
|
}
|
|
ProcessQueryP const process_query = NEW_C_HEAP_OBJ(ProcessQueryS, mtInternal);
|
|
memset(process_query, 0, sizeof(ProcessQueryS));
|
|
process_query->set.queries = NEW_C_HEAP_ARRAY(MultiCounterQueryS, current_process_idx + 1, mtInternal);
|
|
memset(process_query->set.queries, 0, sizeof(MultiCounterQueryS) * (current_process_idx + 1));
|
|
process_query->process_index = current_process_idx;
|
|
process_query->set.size = current_process_idx + 1;
|
|
assert(process_query->set.size > process_query->process_index, "invariant");
|
|
return process_query;
|
|
}
|
|
|
|
static MultiCounterQueryP current_process_counter_query(ProcessQueryP process_query) {
|
|
assert(process_query != NULL, "invariant");
|
|
assert(process_query->process_index < process_query->set.size, "invariant");
|
|
return &process_query->set.queries[process_query->process_index];
|
|
}
|
|
|
|
static void clear_multi_counter(MultiCounterQueryP query) {
|
|
for (int i = 0; i < query->noOfCounters; ++i) {
|
|
pdh_cleanup(NULL, &query->counters[i]);
|
|
}
|
|
pdh_cleanup(&query->query.query, NULL);
|
|
query->initialized = false;
|
|
}
|
|
|
|
static int ensure_valid_process_query_index(ProcessQueryP process_query) {
|
|
assert(process_query != NULL, "invariant");
|
|
const int previous_process_idx = process_query->process_index;
|
|
if (previous_process_idx == 0) {
|
|
return previous_process_idx;
|
|
}
|
|
const int current_process_idx = current_query_index_for_process();
|
|
if (current_process_idx == previous_process_idx || OS_ERR == current_process_idx ||
|
|
current_process_idx >= process_query->set.size) {
|
|
return previous_process_idx;
|
|
}
|
|
|
|
assert(current_process_idx >= 0 && current_process_idx < process_query->set.size, "out of bounds!");
|
|
while (current_process_idx < process_query->set.size - 1) {
|
|
const int new_size = --process_query->set.size;
|
|
clear_multi_counter(&process_query->set.queries[new_size]);
|
|
}
|
|
assert(current_process_idx < process_query->set.size, "invariant");
|
|
process_query->process_index = current_process_idx;
|
|
return current_process_idx;
|
|
}
|
|
|
|
static MultiCounterQueryP current_process_query(ProcessQueryP process_query) {
|
|
assert(process_query != NULL, "invariant");
|
|
const int current_process_idx = ensure_valid_process_query_index(process_query);
|
|
assert(current_process_idx == process_query->process_index, "invariant");
|
|
assert(current_process_idx < process_query->set.size, "invariant");
|
|
return &process_query->set.queries[current_process_idx];
|
|
}
|
|
|
|
static int collect_process_query_data(ProcessQueryP process_query) {
|
|
assert(process_query != NULL, "invariant");
|
|
return collect_query_data(current_process_query(process_query));
|
|
}
|
|
|
|
static int query_process_counter(ProcessQueryP process_query, int slot_index, DWORD format, PDH_FMT_COUNTERVALUE* const value) {
|
|
MultiCounterQueryP const current_query = current_process_counter_query(process_query);
|
|
assert(current_query != NULL, "invariant");
|
|
assert(slot_index < current_query->noOfCounters, "invariant");
|
|
assert(current_query->counters[slot_index] != NULL, "invariant");
|
|
return formatted_counter_value(current_query->counters[slot_index], format, value);
|
|
}
|
|
|
|
/*
|
|
* Construct a fully qualified PDH path
|
|
*
|
|
* @param objectName a PDH Object string representation(required)
|
|
* @param counterName a PDH Counter string representation(required)
|
|
* @param imageName a process image name string, ex. "java" (opt)
|
|
* @param instance an instance string, ex. "0", "1", ... (opt)
|
|
* @return the fully qualified PDH path.
|
|
*
|
|
* Caller will need a ResourceMark.
|
|
*
|
|
* (PdhMakeCounterPath() seems buggy on concatenating instances, hence this function instead)
|
|
*/
|
|
static const char* make_fully_qualified_counter_path(const char* object_name,
|
|
const char* counter_name,
|
|
const char* image_name = NULL,
|
|
const char* instance = NULL) {
|
|
assert(object_name != NULL, "invariant");
|
|
assert(counter_name != NULL, "invariant");
|
|
size_t full_counter_path_len = strlen(object_name) + strlen(counter_name);
|
|
|
|
char* full_counter_path;
|
|
size_t jio_snprintf_result = 0;
|
|
if (image_name) {
|
|
/*
|
|
* For paths using the "Process" Object.
|
|
*
|
|
* Examples:
|
|
* form: "\object_name(image_name#instance)\counter_name"
|
|
* actual: "\Process(java#2)\ID Process"
|
|
*/
|
|
full_counter_path_len += PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN;
|
|
full_counter_path_len += strlen(image_name);
|
|
/*
|
|
* image_name must be passed together with an associated
|
|
* instance "number" ("0", "1", "2", ...).
|
|
* This is required in order to create valid "Process" Object paths.
|
|
*
|
|
* Examples: "\Process(java#0)", \Process(java#1"), ...
|
|
*/
|
|
assert(instance != NULL, "invariant");
|
|
full_counter_path_len += strlen(instance);
|
|
full_counter_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, full_counter_path_len + 1);
|
|
if (full_counter_path == NULL) {
|
|
return NULL;
|
|
}
|
|
jio_snprintf_result = jio_snprintf(full_counter_path,
|
|
full_counter_path_len + 1,
|
|
PROCESS_OBJECT_INSTANCE_COUNTER_FMT,
|
|
object_name,
|
|
image_name,
|
|
instance,
|
|
counter_name);
|
|
} else {
|
|
if (instance) {
|
|
/*
|
|
* For paths where the Object has multiple instances.
|
|
*
|
|
* Examples:
|
|
* form: "\object_name(instance)\counter_name"
|
|
* actual: "\Processor(0)\% Privileged Time"
|
|
*/
|
|
full_counter_path_len += strlen(instance);
|
|
full_counter_path_len += OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN;
|
|
} else {
|
|
/*
|
|
* For "normal" paths.
|
|
*
|
|
* Examples:
|
|
* form: "\object_name\counter_name"
|
|
* actual: "\Memory\Available Mbytes"
|
|
*/
|
|
full_counter_path_len += OBJECT_COUNTER_FMT_LEN;
|
|
}
|
|
full_counter_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, full_counter_path_len + 1);
|
|
if (full_counter_path == NULL) {
|
|
return NULL;
|
|
}
|
|
if (instance) {
|
|
jio_snprintf_result = jio_snprintf(full_counter_path,
|
|
full_counter_path_len + 1,
|
|
OBJECT_WITH_INSTANCES_COUNTER_FMT,
|
|
object_name,
|
|
instance,
|
|
counter_name);
|
|
} else {
|
|
jio_snprintf_result = jio_snprintf(full_counter_path,
|
|
full_counter_path_len + 1,
|
|
OBJECT_COUNTER_FMT,
|
|
object_name,
|
|
counter_name);
|
|
}
|
|
}
|
|
assert(full_counter_path_len == jio_snprintf_result, "invariant");
|
|
return full_counter_path;
|
|
}
|
|
|
|
static void log_invalid_pdh_index(DWORD index) {
|
|
log_warning(os)("Unable to resolve PDH index: (%ld)", index);
|
|
log_warning(os)("Please check the registry if this performance object/counter is disabled");
|
|
}
|
|
|
|
static bool is_valid_pdh_index(DWORD index) {
|
|
DWORD dummy = 0;
|
|
if (PdhDll::PdhLookupPerfNameByIndex(NULL, index, NULL, &dummy) != PDH_MORE_DATA) {
|
|
log_invalid_pdh_index(index);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Maps an index to a resource area allocated string for the localized PDH artifact.
|
|
*
|
|
* Caller will need a ResourceMark.
|
|
*
|
|
* @param index the counter index as specified in the registry
|
|
* @param ppBuffer pointer to a char*
|
|
* @return OS_OK if successful, OS_ERR on failure.
|
|
*/
|
|
static OSReturn lookup_name_by_index(DWORD index, char** p_string) {
|
|
assert(p_string != NULL, "invariant");
|
|
if (!is_valid_pdh_index(index)) {
|
|
return OS_ERR;
|
|
}
|
|
// determine size needed
|
|
DWORD size = 0;
|
|
PDH_STATUS status = PdhDll::PdhLookupPerfNameByIndex(NULL, index, NULL, &size);
|
|
assert(status == PDH_MORE_DATA, "invariant");
|
|
*p_string = NEW_RESOURCE_ARRAY_RETURN_NULL(char, size);
|
|
if (*p_string== NULL) {
|
|
return OS_ERR;
|
|
}
|
|
if (PdhDll::PdhLookupPerfNameByIndex(NULL, index, *p_string, &size) != ERROR_SUCCESS) {
|
|
return OS_ERR;
|
|
}
|
|
if (0 == size || *p_string == NULL) {
|
|
return OS_ERR;
|
|
}
|
|
// windows vista does not null-terminate the string (although the docs says it will)
|
|
(*p_string)[size - 1] = '\0';
|
|
return OS_OK;
|
|
}
|
|
|
|
static const char* copy_string_to_c_heap(const char* string) {
|
|
assert(string != NULL, "invariant");
|
|
const size_t len = strlen(string);
|
|
char* const cheap_allocated_string = NEW_C_HEAP_ARRAY_RETURN_NULL(char, len + 1, mtInternal);
|
|
if (NULL == cheap_allocated_string) {
|
|
return NULL;
|
|
}
|
|
strncpy(cheap_allocated_string, string, len + 1);
|
|
return cheap_allocated_string;
|
|
}
|
|
|
|
/*
|
|
* Maps an index to a resource area allocated string for the localized PDH artifact.
|
|
*
|
|
* Caller will need a ResourceMark.
|
|
*
|
|
* @param index the counter index as specified in the registry
|
|
* @return localized pdh artifact string if successful, NULL on failure.
|
|
*/
|
|
static const char* pdh_localized_artifact(DWORD pdh_artifact_index) {
|
|
char* pdh_localized_artifact_string = NULL;
|
|
// get localized name from pdh artifact index
|
|
if (lookup_name_by_index(pdh_artifact_index, &pdh_localized_artifact_string) != OS_OK) {
|
|
return NULL;
|
|
}
|
|
return pdh_localized_artifact_string;
|
|
}
|
|
|
|
/*
|
|
* Returns the PDH string identifying the current process image name.
|
|
* Use this prefix when getting counters from the PDH process object
|
|
* representing your process.
|
|
* Ex. "Process(java#0)\Virtual Bytes" - where "java" is the PDH process
|
|
* image description.
|
|
*
|
|
* Caller needs ResourceMark.
|
|
*
|
|
* @return the process image description. NULL if the call failed.
|
|
*/
|
|
static const char* pdh_process_image_name() {
|
|
char* module_name = NEW_RESOURCE_ARRAY_RETURN_NULL(char, MAX_PATH);
|
|
if (NULL == module_name) {
|
|
return NULL;
|
|
}
|
|
// Find our module name and use it to extract the image name used by PDH
|
|
DWORD getmfn_return = GetModuleFileName(NULL, module_name, MAX_PATH);
|
|
if (getmfn_return >= MAX_PATH || 0 == getmfn_return) {
|
|
return NULL;
|
|
}
|
|
if (os::get_last_error() == ERROR_INSUFFICIENT_BUFFER) {
|
|
return NULL;
|
|
}
|
|
char* process_image_name = strrchr(module_name, '\\'); //drop path
|
|
process_image_name++; //skip slash
|
|
char* dot_pos = strrchr(process_image_name, '.'); //drop .exe
|
|
dot_pos[0] = '\0';
|
|
return process_image_name;
|
|
}
|
|
|
|
static void deallocate_pdh_constants() {
|
|
FREE_C_HEAP_ARRAY(char, process_image_name);
|
|
process_image_name = NULL;
|
|
FREE_C_HEAP_ARRAY(char, pdh_IDProcess_counter_fmt);
|
|
pdh_IDProcess_counter_fmt = NULL;
|
|
}
|
|
|
|
static int allocate_pdh_constants() {
|
|
assert(process_image_name == NULL, "invariant");
|
|
const char* pdh_image_name = pdh_process_image_name();
|
|
if (pdh_image_name == NULL) {
|
|
return OS_ERR;
|
|
}
|
|
process_image_name = copy_string_to_c_heap(pdh_image_name);
|
|
|
|
const char* pdh_localized_process_object = pdh_localized_artifact(PDH_PROCESS_IDX);
|
|
if (pdh_localized_process_object == NULL) {
|
|
return OS_ERR;
|
|
}
|
|
|
|
const char* pdh_localized_IDProcess_counter = pdh_localized_artifact(PDH_ID_PROCESS_IDX);
|
|
if (pdh_localized_IDProcess_counter == NULL) {
|
|
return OS_ERR;
|
|
}
|
|
|
|
size_t pdh_IDProcess_counter_fmt_len = strlen(process_image_name);
|
|
pdh_IDProcess_counter_fmt_len += strlen(pdh_localized_process_object);
|
|
pdh_IDProcess_counter_fmt_len += strlen(pdh_localized_IDProcess_counter);
|
|
pdh_IDProcess_counter_fmt_len += PROCESS_OBJECT_INSTANCE_COUNTER_FMT_LEN;
|
|
pdh_IDProcess_counter_fmt_len += 2; // "%d"
|
|
|
|
assert(pdh_IDProcess_counter_fmt == NULL, "invariant");
|
|
pdh_IDProcess_counter_fmt = NEW_C_HEAP_ARRAY_RETURN_NULL(char, pdh_IDProcess_counter_fmt_len + 1, mtInternal);
|
|
if (pdh_IDProcess_counter_fmt == NULL) {
|
|
return OS_ERR;
|
|
}
|
|
|
|
/* "\Process(java#%d)\ID Process" */
|
|
const size_t len = jio_snprintf(pdh_IDProcess_counter_fmt,
|
|
pdh_IDProcess_counter_fmt_len + 1,
|
|
PROCESS_OBJECT_INSTANCE_COUNTER_FMT,
|
|
pdh_localized_process_object,
|
|
process_image_name,
|
|
"%d",
|
|
pdh_localized_IDProcess_counter);
|
|
|
|
assert(pdh_IDProcess_counter_fmt != NULL, "invariant");
|
|
assert(len == pdh_IDProcess_counter_fmt_len, "invariant");
|
|
return OS_OK;
|
|
}
|
|
|
|
/*
|
|
* Enuerate the Processor PDH object and returns a buffer containing the enumerated instances.
|
|
* Caller needs ResourceMark;
|
|
*
|
|
* @return buffer if successful, NULL on failure.
|
|
*/
|
|
static const char* enumerate_cpu_instances() {
|
|
char* processor; //'Processor' == PDH_PROCESSOR_IDX
|
|
if (lookup_name_by_index(PDH_PROCESSOR_IDX, &processor) != OS_OK) {
|
|
return NULL;
|
|
}
|
|
DWORD c_size = 0;
|
|
DWORD i_size = 0;
|
|
// enumerate all processors.
|
|
PDH_STATUS pdhStat = PdhDll::PdhEnumObjectItems(NULL, // reserved
|
|
NULL, // local machine
|
|
processor, // object to enumerate
|
|
NULL,
|
|
&c_size,
|
|
NULL, // instance buffer is NULL and
|
|
&i_size, // pass 0 length in order to get the required size
|
|
PERF_DETAIL_WIZARD, // counter detail level
|
|
0);
|
|
if (PdhDll::PdhStatusFail((pdhStat))) {
|
|
return NULL;
|
|
}
|
|
char* const instances = NEW_RESOURCE_ARRAY_RETURN_NULL(char, i_size);
|
|
if (instances == NULL) {
|
|
return NULL;
|
|
}
|
|
c_size = 0;
|
|
pdhStat = PdhDll::PdhEnumObjectItems(NULL, // reserved
|
|
NULL, // local machine
|
|
processor, // object to enumerate
|
|
NULL,
|
|
&c_size,
|
|
instances, // now instance buffer is allocated to be filled in
|
|
&i_size, // and the required size is known
|
|
PERF_DETAIL_WIZARD, // counter detail level
|
|
0);
|
|
if (PdhDll::PdhStatusFail((pdhStat))) {
|
|
return NULL;
|
|
}
|
|
return instances;
|
|
}
|
|
|
|
static int count_logical_cpus(const char* instances) {
|
|
assert(instances != NULL, "invariant");
|
|
// count logical instances.
|
|
DWORD count;
|
|
char* tmp;
|
|
for (count = 0, tmp = const_cast<char*>(instances); *tmp != '\0'; tmp = &tmp[strlen(tmp) + 1], count++);
|
|
// PDH reports an instance for each logical processor plus an instance for the total (_Total)
|
|
assert(count == os::processor_count() + 1, "invalid enumeration!");
|
|
return count - 1;
|
|
}
|
|
|
|
static int number_of_logical_cpus() {
|
|
static int numberOfCPUS = 0;
|
|
if (numberOfCPUS == 0) {
|
|
const char* instances = enumerate_cpu_instances();
|
|
if (instances == NULL) {
|
|
return OS_ERR;
|
|
}
|
|
numberOfCPUS = count_logical_cpus(instances);
|
|
}
|
|
return numberOfCPUS;
|
|
}
|
|
|
|
static double cpu_factor() {
|
|
static DWORD numCpus = 0;
|
|
static double cpuFactor = .0;
|
|
if (numCpus == 0) {
|
|
numCpus = number_of_logical_cpus();
|
|
assert(os::processor_count() <= (int)numCpus, "invariant");
|
|
cpuFactor = numCpus * 100;
|
|
}
|
|
return cpuFactor;
|
|
}
|
|
|
|
static void log_error_message_on_no_PDH_artifact(const char* full_counter_name) {
|
|
log_warning(os)("Unable to register PDH query for \"%s\"", full_counter_name);
|
|
log_warning(os)("Please check the registry if this performance object/counter is disabled");
|
|
}
|
|
|
|
static int initialize_cpu_query_counters(MultiCounterQueryP cpu_query, DWORD pdh_counter_idx) {
|
|
assert(cpu_query != NULL, "invariant");
|
|
assert(cpu_query->counters != NULL, "invariant");
|
|
char* processor; //'Processor' == PDH_PROCESSOR_IDX
|
|
if (lookup_name_by_index(PDH_PROCESSOR_IDX, &processor) != OS_OK) {
|
|
return OS_ERR;
|
|
}
|
|
char* counter_name = NULL;
|
|
if (lookup_name_by_index(pdh_counter_idx, &counter_name) != OS_OK) {
|
|
return OS_ERR;
|
|
}
|
|
if (cpu_query->query.query == NULL) {
|
|
if (open_query(cpu_query)) {
|
|
return OS_ERR;
|
|
}
|
|
}
|
|
assert(cpu_query->query.query != NULL, "invariant");
|
|
size_t counter_len = strlen(processor);
|
|
counter_len += strlen(counter_name);
|
|
counter_len += OBJECT_WITH_INSTANCES_COUNTER_FMT_LEN; // "\\%s(%s)\\%s"
|
|
|
|
DWORD index;
|
|
char* tmp;
|
|
const char* instances = enumerate_cpu_instances();
|
|
for (index = 0, tmp = const_cast<char*>(instances); *tmp != '\0'; tmp = &tmp[strlen(tmp) + 1], index++) {
|
|
const size_t tmp_len = strlen(tmp);
|
|
char* counter_path = NEW_RESOURCE_ARRAY_RETURN_NULL(char, counter_len + tmp_len + 1);
|
|
if (counter_path == NULL) {
|
|
return OS_ERR;
|
|
}
|
|
const size_t jio_snprintf_result = jio_snprintf(counter_path,
|
|
counter_len + tmp_len + 1,
|
|
OBJECT_WITH_INSTANCES_COUNTER_FMT,
|
|
processor,
|
|
tmp, // instance "0", "1", .."_Total"
|
|
counter_name);
|
|
assert(counter_len + tmp_len == jio_snprintf_result, "invariant");
|
|
if (add_counter(cpu_query, &cpu_query->counters[index], counter_path, false) != OS_OK) {
|
|
// performance counter is disabled in registry and not accessible via PerfLib
|
|
log_error_message_on_no_PDH_artifact(counter_path);
|
|
// return OS_OK to have the system continue to run without the missing counter
|
|
return OS_OK;
|
|
}
|
|
}
|
|
cpu_query->initialized = true;
|
|
// Query once to initialize the counters which require at least two samples
|
|
// (like the % CPU usage) to calculate correctly.
|
|
collect_query_data(cpu_query);
|
|
return OS_OK;
|
|
}
|
|
|
|
static int initialize_cpu_query(MultiCounterQueryP cpu_query, DWORD pdh_counter_idx) {
|
|
assert(cpu_query != NULL, "invariant");
|
|
assert(!cpu_query->initialized, "invariant");
|
|
const int logical_cpu_count = number_of_logical_cpus();
|
|
assert(logical_cpu_count >= os::processor_count(), "invariant");
|
|
// we also add another counter for instance "_Total"
|
|
allocate_counters(cpu_query, logical_cpu_count + 1);
|
|
assert(cpu_query->noOfCounters == logical_cpu_count + 1, "invariant");
|
|
return initialize_cpu_query_counters(cpu_query, pdh_counter_idx);
|
|
}
|
|
|
|
static int initialize_process_counter(ProcessQueryP process_query, int slot_index, DWORD pdh_counter_index) {
|
|
char* localized_process_object;
|
|
if (lookup_name_by_index(PDH_PROCESS_IDX, &localized_process_object) != OS_OK) {
|
|
return OS_ERR;
|
|
}
|
|
assert(localized_process_object != NULL, "invariant");
|
|
char* localized_counter_name;
|
|
if (lookup_name_by_index(pdh_counter_index, &localized_counter_name) != OS_OK) {
|
|
return OS_ERR;
|
|
}
|
|
assert(localized_counter_name != NULL, "invariant");
|
|
for (int i = 0; i < process_query->set.size; ++i) {
|
|
char instanceIndexBuffer[32];
|
|
const char* counter_path = make_fully_qualified_counter_path(localized_process_object,
|
|
localized_counter_name,
|
|
process_image_name,
|
|
itoa(i, instanceIndexBuffer, 10));
|
|
if (counter_path == NULL) {
|
|
return OS_ERR;
|
|
}
|
|
MultiCounterQueryP const query = &process_query->set.queries[i];
|
|
if (add_process_counter(query, slot_index, counter_path, true)) {
|
|
return OS_ERR;
|
|
}
|
|
}
|
|
return OS_OK;
|
|
}
|
|
|
|
static CounterQueryP create_counter_query(DWORD pdh_object_idx, DWORD pdh_counter_idx) {
|
|
if (!((is_valid_pdh_index(pdh_object_idx) && is_valid_pdh_index(pdh_counter_idx)))) {
|
|
return NULL;
|
|
}
|
|
CounterQueryP const query = create_counter_query();
|
|
const char* object = pdh_localized_artifact(pdh_object_idx);
|
|
assert(object != NULL, "invariant");
|
|
const char* counter = pdh_localized_artifact(pdh_counter_idx);
|
|
assert(counter != NULL, "invariant");
|
|
const char* full_counter_path = make_fully_qualified_counter_path(object, counter);
|
|
assert(full_counter_path != NULL, "invariant");
|
|
add_counter(query, full_counter_path, true);
|
|
return query;
|
|
}
|
|
|
|
static void deallocate() {
|
|
deallocate_pdh_constants();
|
|
PdhDll::PdhDetach();
|
|
}
|
|
|
|
static LONG critical_section = 0;
|
|
static LONG reference_count = 0;
|
|
static bool pdh_initialized = false;
|
|
|
|
static void on_initialization_failure() {
|
|
// still holder of critical section
|
|
deallocate();
|
|
InterlockedExchangeAdd(&reference_count, -1);
|
|
}
|
|
|
|
static OSReturn initialize() {
|
|
ResourceMark rm;
|
|
if (!PdhDll::PdhAttach()) {
|
|
return OS_ERR;
|
|
}
|
|
if (allocate_pdh_constants() != OS_OK) {
|
|
on_initialization_failure();
|
|
return OS_ERR;
|
|
}
|
|
return OS_OK;
|
|
}
|
|
|
|
/*
|
|
* Helper to initialize the PDH library, function pointers, constants and counters.
|
|
*
|
|
* Reference counting allows for unloading of pdh.dll granted all sessions use the pair:
|
|
*
|
|
* pdh_acquire();
|
|
* pdh_release();
|
|
*
|
|
* @return OS_OK if successful, OS_ERR on failure.
|
|
*/
|
|
static bool pdh_acquire() {
|
|
while (InterlockedCompareExchange(&critical_section, 1, 0) == 1);
|
|
InterlockedExchangeAdd(&reference_count, 1);
|
|
if (pdh_initialized) {
|
|
return true;
|
|
}
|
|
const OSReturn ret = initialize();
|
|
if (OS_OK == ret) {
|
|
pdh_initialized = true;
|
|
}
|
|
while (InterlockedCompareExchange(&critical_section, 0, 1) == 0);
|
|
return ret == OS_OK;
|
|
}
|
|
|
|
static void pdh_release() {
|
|
while (InterlockedCompareExchange(&critical_section, 1, 0) == 1);
|
|
const LONG prev_ref_count = InterlockedExchangeAdd(&reference_count, -1);
|
|
if (1 == prev_ref_count) {
|
|
deallocate();
|
|
pdh_initialized = false;
|
|
}
|
|
while (InterlockedCompareExchange(&critical_section, 0, 1) == 0);
|
|
}
|
|
|
|
class CPUPerformanceInterface::CPUPerformance : public CHeapObj<mtInternal> {
|
|
friend class CPUPerformanceInterface;
|
|
private:
|
|
CounterQueryP _context_switches;
|
|
ProcessQueryP _process_cpu_load;
|
|
MultiCounterQueryP _machine_cpu_load;
|
|
|
|
int cpu_load(int which_logical_cpu, double* cpu_load);
|
|
int context_switch_rate(double* rate);
|
|
int cpu_load_total_process(double* cpu_load);
|
|
int cpu_loads_process(double* jvm_user_load, double* jvm_kernel_load, double* psystemTotalLoad);
|
|
CPUPerformance();
|
|
~CPUPerformance();
|
|
bool initialize();
|
|
};
|
|
|
|
class SystemProcessInterface::SystemProcesses : public CHeapObj<mtInternal> {
|
|
friend class SystemProcessInterface;
|
|
private:
|
|
class ProcessIterator : public CHeapObj<mtInternal> {
|
|
friend class SystemProcessInterface::SystemProcesses;
|
|
private:
|
|
HANDLE _hProcessSnap;
|
|
PROCESSENTRY32 _pe32;
|
|
BOOL _valid;
|
|
char _exePath[MAX_PATH];
|
|
ProcessIterator();
|
|
~ProcessIterator();
|
|
bool initialize();
|
|
|
|
int current(SystemProcess* const process_info);
|
|
int next_process();
|
|
bool is_valid() const { return _valid != FALSE; }
|
|
char* allocate_string(const char* str) const;
|
|
int snapshot();
|
|
};
|
|
|
|
ProcessIterator* _iterator;
|
|
SystemProcesses();
|
|
~SystemProcesses();
|
|
bool initialize();
|
|
|
|
// information about system processes
|
|
int system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const;
|
|
};
|
|
|
|
CPUPerformanceInterface::CPUPerformance::CPUPerformance() : _context_switches(NULL), _process_cpu_load(NULL), _machine_cpu_load(NULL) {}
|
|
|
|
bool CPUPerformanceInterface::CPUPerformance::initialize() {
|
|
if (!pdh_acquire()) {
|
|
return true;
|
|
}
|
|
_context_switches = create_counter_query(PDH_SYSTEM_IDX, PDH_CONTEXT_SWITCH_RATE_IDX);
|
|
_process_cpu_load = create_process_query();
|
|
if (_process_cpu_load == NULL) {
|
|
return true;
|
|
}
|
|
allocate_counters(_process_cpu_load, 2);
|
|
if (initialize_process_counter(_process_cpu_load, 0, PDH_PROCESSOR_TIME_IDX) != OS_OK) {
|
|
return true;
|
|
}
|
|
if (initialize_process_counter(_process_cpu_load, 1, PDH_PRIV_PROCESSOR_TIME_IDX) != OS_OK) {
|
|
return true;
|
|
}
|
|
_process_cpu_load->set.initialized = true;
|
|
_machine_cpu_load = create_multi_counter_query();
|
|
if (_machine_cpu_load == NULL) {
|
|
return true;
|
|
}
|
|
initialize_cpu_query(_machine_cpu_load, PDH_PROCESSOR_TIME_IDX);
|
|
return true;
|
|
}
|
|
|
|
CPUPerformanceInterface::CPUPerformance::~CPUPerformance() {
|
|
if (_context_switches != NULL) {
|
|
destroy_counter_query(_context_switches);
|
|
_context_switches = NULL;
|
|
}
|
|
if (_process_cpu_load != NULL) {
|
|
destroy_counter_query(_process_cpu_load);
|
|
_process_cpu_load = NULL;
|
|
}
|
|
if (_machine_cpu_load != NULL) {
|
|
destroy_counter_query(_machine_cpu_load);
|
|
_machine_cpu_load = NULL;
|
|
}
|
|
pdh_release();
|
|
}
|
|
|
|
CPUPerformanceInterface::CPUPerformanceInterface() {
|
|
_impl = NULL;
|
|
}
|
|
|
|
bool CPUPerformanceInterface::initialize() {
|
|
_impl = new CPUPerformanceInterface::CPUPerformance();
|
|
return _impl->initialize();
|
|
}
|
|
|
|
CPUPerformanceInterface::~CPUPerformanceInterface() {
|
|
if (_impl != NULL) {
|
|
delete _impl;
|
|
}
|
|
}
|
|
|
|
int CPUPerformanceInterface::cpu_load(int which_logical_cpu, double* cpu_load) const {
|
|
return _impl->cpu_load(which_logical_cpu, cpu_load);
|
|
}
|
|
|
|
int CPUPerformanceInterface::context_switch_rate(double* rate) const {
|
|
return _impl->context_switch_rate(rate);
|
|
}
|
|
|
|
int CPUPerformanceInterface::cpu_load_total_process(double* cpu_load) const {
|
|
return _impl->cpu_load_total_process(cpu_load);
|
|
}
|
|
|
|
int CPUPerformanceInterface::cpu_loads_process(double* pjvmUserLoad,
|
|
double* pjvmKernelLoad,
|
|
double* psystemTotalLoad) const {
|
|
return _impl->cpu_loads_process(pjvmUserLoad, pjvmKernelLoad, psystemTotalLoad);
|
|
}
|
|
|
|
int CPUPerformanceInterface::CPUPerformance::cpu_load(int which_logical_cpu, double* cpu_load) {
|
|
*cpu_load = .0;
|
|
if (_machine_cpu_load == NULL || !_machine_cpu_load->initialized) {
|
|
return OS_ERR;
|
|
}
|
|
assert(_machine_cpu_load != NULL, "invariant");
|
|
assert(which_logical_cpu < _machine_cpu_load->noOfCounters, "invariant");
|
|
|
|
if (collect_query_data(_machine_cpu_load)) {
|
|
return OS_ERR;
|
|
}
|
|
// -1 is total (all cpus)
|
|
const int counter_idx = -1 == which_logical_cpu ? _machine_cpu_load->noOfCounters - 1 : which_logical_cpu;
|
|
PDH_FMT_COUNTERVALUE counter_value;
|
|
formatted_counter_value(_machine_cpu_load->counters[counter_idx], PDH_FMT_DOUBLE, &counter_value);
|
|
*cpu_load = counter_value.doubleValue / 100;
|
|
return OS_OK;
|
|
}
|
|
|
|
int CPUPerformanceInterface::CPUPerformance::cpu_load_total_process(double* cpu_load) {
|
|
*cpu_load = .0;
|
|
if (_process_cpu_load == NULL || !_process_cpu_load->set.initialized) {
|
|
return OS_ERR;
|
|
}
|
|
assert(_process_cpu_load != NULL, "invariant");
|
|
if (collect_process_query_data(_process_cpu_load)) {
|
|
return OS_ERR;
|
|
}
|
|
PDH_FMT_COUNTERVALUE counter_value;
|
|
if (query_process_counter(_process_cpu_load, 0, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &counter_value) != OS_OK) {
|
|
return OS_ERR;
|
|
}
|
|
double process_load = counter_value.doubleValue / cpu_factor();
|
|
process_load = MIN2<double>(1, process_load);
|
|
process_load = MAX2<double>(0, process_load);
|
|
*cpu_load = process_load;
|
|
return OS_OK;
|
|
}
|
|
|
|
int CPUPerformanceInterface::CPUPerformance::cpu_loads_process(double* pjvmUserLoad,
|
|
double* pjvmKernelLoad,
|
|
double* psystemTotalLoad) {
|
|
assert(pjvmUserLoad != NULL, "pjvmUserLoad is NULL!");
|
|
assert(pjvmKernelLoad != NULL, "pjvmKernelLoad is NULL!");
|
|
assert(psystemTotalLoad != NULL, "psystemTotalLoad is NULL!");
|
|
*pjvmUserLoad = .0;
|
|
*pjvmKernelLoad = .0;
|
|
*psystemTotalLoad = .0;
|
|
|
|
if (_process_cpu_load == NULL || !_process_cpu_load->set.initialized) {
|
|
return OS_ERR;
|
|
}
|
|
assert(_process_cpu_load != NULL, "invariant");
|
|
if (collect_process_query_data(_process_cpu_load)) {
|
|
return OS_ERR;
|
|
}
|
|
double process_load = .0;
|
|
PDH_FMT_COUNTERVALUE counter_value;
|
|
// Read PDH_PROCESSOR_TIME_IDX
|
|
if (query_process_counter(_process_cpu_load, 0, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &counter_value) != OS_OK) {
|
|
return OS_ERR;
|
|
}
|
|
process_load = counter_value.doubleValue / cpu_factor();
|
|
process_load = MIN2<double>(1, process_load);
|
|
process_load = MAX2<double>(0, process_load);
|
|
// Read PDH_PRIV_PROCESSOR_TIME_IDX
|
|
if (query_process_counter(_process_cpu_load, 1, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &counter_value) != OS_OK) {
|
|
return OS_ERR;
|
|
}
|
|
double kernel_load = counter_value.doubleValue / cpu_factor();
|
|
kernel_load = MIN2<double>(1, kernel_load);
|
|
kernel_load = MAX2<double>(0, kernel_load);
|
|
*pjvmKernelLoad = kernel_load;
|
|
|
|
double user_load = process_load - kernel_load;
|
|
user_load = MIN2<double>(1, user_load);
|
|
user_load = MAX2<double>(0, user_load);
|
|
*pjvmUserLoad = user_load;
|
|
|
|
if (collect_query_data(_machine_cpu_load)) {
|
|
return OS_ERR;
|
|
}
|
|
if (formatted_counter_value(_machine_cpu_load->counters[_machine_cpu_load->noOfCounters - 1], PDH_FMT_DOUBLE, &counter_value) != OS_OK) {
|
|
return OS_ERR;
|
|
}
|
|
double machine_load = counter_value.doubleValue / 100;
|
|
assert(machine_load >= 0, "machine_load is negative!");
|
|
// clamp at user+system and 1.0
|
|
if (*pjvmKernelLoad + *pjvmUserLoad > machine_load) {
|
|
machine_load = MIN2(*pjvmKernelLoad + *pjvmUserLoad, 1.0);
|
|
}
|
|
*psystemTotalLoad = machine_load;
|
|
return OS_OK;
|
|
}
|
|
|
|
int CPUPerformanceInterface::CPUPerformance::context_switch_rate(double* rate) {
|
|
assert(rate != NULL, "invariant");
|
|
*rate = .0;
|
|
if (_context_switches == NULL || !_context_switches->initialized) {
|
|
return OS_ERR;
|
|
}
|
|
assert(_context_switches != NULL, "invariant");
|
|
if (collect_query_data(_context_switches) != OS_OK) {
|
|
return OS_ERR;
|
|
}
|
|
PDH_FMT_COUNTERVALUE counter_value;
|
|
if (formatted_counter_value(_context_switches->counter, PDH_FMT_DOUBLE, &counter_value) != OS_OK) {
|
|
return OS_ERR;
|
|
}
|
|
*rate = counter_value.doubleValue;
|
|
return OS_OK;
|
|
}
|
|
|
|
SystemProcessInterface::SystemProcesses::ProcessIterator::ProcessIterator() {
|
|
_hProcessSnap = INVALID_HANDLE_VALUE;
|
|
_valid = FALSE;
|
|
_pe32.dwSize = sizeof(PROCESSENTRY32);
|
|
}
|
|
|
|
bool SystemProcessInterface::SystemProcesses::ProcessIterator::initialize() {
|
|
return true;
|
|
}
|
|
|
|
int SystemProcessInterface::SystemProcesses::ProcessIterator::snapshot() {
|
|
// take snapshot of all process in the system
|
|
_hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
|
if (_hProcessSnap == INVALID_HANDLE_VALUE) {
|
|
return OS_ERR;
|
|
}
|
|
// step to first process
|
|
_valid = Process32First(_hProcessSnap, &_pe32);
|
|
return is_valid() ? OS_OK : OS_ERR;
|
|
}
|
|
|
|
SystemProcessInterface::SystemProcesses::ProcessIterator::~ProcessIterator() {
|
|
if (_hProcessSnap != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(_hProcessSnap);
|
|
}
|
|
}
|
|
|
|
int SystemProcessInterface::SystemProcesses::ProcessIterator::current(SystemProcess* process_info) {
|
|
assert(is_valid(), "no current process to be fetched!");
|
|
assert(process_info != NULL, "process_info is NULL!");
|
|
char* exePath = NULL;
|
|
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, _pe32.th32ProcessID);
|
|
if (hProcess != NULL) {
|
|
HMODULE hMod;
|
|
DWORD cbNeeded;
|
|
if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded) != 0) {
|
|
if (GetModuleFileNameExA(hProcess, hMod, _exePath, sizeof(_exePath)) != 0) {
|
|
exePath = _exePath;
|
|
}
|
|
}
|
|
CloseHandle (hProcess);
|
|
}
|
|
process_info->set_pid((int)_pe32.th32ProcessID);
|
|
process_info->set_name(allocate_string(_pe32.szExeFile));
|
|
process_info->set_path(allocate_string(exePath));
|
|
return OS_OK;
|
|
}
|
|
|
|
char* SystemProcessInterface::SystemProcesses::ProcessIterator::allocate_string(const char* str) const {
|
|
if (str != NULL) {
|
|
return os::strdup_check_oom(str, mtInternal);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int SystemProcessInterface::SystemProcesses::ProcessIterator::next_process() {
|
|
_valid = Process32Next(_hProcessSnap, &_pe32);
|
|
return OS_OK;
|
|
}
|
|
|
|
SystemProcessInterface::SystemProcesses::SystemProcesses() {
|
|
_iterator = NULL;
|
|
}
|
|
|
|
bool SystemProcessInterface::SystemProcesses::initialize() {
|
|
_iterator = new SystemProcessInterface::SystemProcesses::ProcessIterator();
|
|
return _iterator->initialize();
|
|
}
|
|
|
|
SystemProcessInterface::SystemProcesses::~SystemProcesses() {
|
|
if (_iterator != NULL) {
|
|
delete _iterator;
|
|
_iterator = NULL;
|
|
}
|
|
}
|
|
|
|
int SystemProcessInterface::SystemProcesses::system_processes(SystemProcess** system_processes,
|
|
int* no_of_sys_processes) const {
|
|
assert(system_processes != NULL, "system_processes pointer is NULL!");
|
|
assert(no_of_sys_processes != NULL, "system_processes counter pointers is NULL!");
|
|
assert(_iterator != NULL, "iterator is NULL!");
|
|
|
|
// initialize pointers
|
|
*no_of_sys_processes = 0;
|
|
*system_processes = NULL;
|
|
|
|
// take process snapshot
|
|
if (_iterator->snapshot() != OS_OK) {
|
|
return OS_ERR;
|
|
}
|
|
|
|
while (_iterator->is_valid()) {
|
|
SystemProcess* tmp = new SystemProcess();
|
|
_iterator->current(tmp);
|
|
|
|
//if already existing head
|
|
if (*system_processes != NULL) {
|
|
//move "first to second"
|
|
tmp->set_next(*system_processes);
|
|
}
|
|
// new head
|
|
*system_processes = tmp;
|
|
// increment
|
|
(*no_of_sys_processes)++;
|
|
// step forward
|
|
_iterator->next_process();
|
|
}
|
|
return OS_OK;
|
|
}
|
|
|
|
int SystemProcessInterface::system_processes(SystemProcess** system_procs,
|
|
int* no_of_sys_processes) const {
|
|
return _impl->system_processes(system_procs, no_of_sys_processes);
|
|
}
|
|
|
|
SystemProcessInterface::SystemProcessInterface() {
|
|
_impl = NULL;
|
|
}
|
|
|
|
bool SystemProcessInterface::initialize() {
|
|
_impl = new SystemProcessInterface::SystemProcesses();
|
|
return _impl->initialize();
|
|
}
|
|
|
|
SystemProcessInterface::~SystemProcessInterface() {
|
|
if (_impl != NULL) {
|
|
delete _impl;
|
|
}
|
|
}
|
|
|
|
CPUInformationInterface::CPUInformationInterface() {
|
|
_cpu_info = NULL;
|
|
}
|
|
|
|
bool CPUInformationInterface::initialize() {
|
|
_cpu_info = new CPUInformation();
|
|
_cpu_info->set_number_of_hardware_threads(VM_Version_Ext::number_of_threads());
|
|
_cpu_info->set_number_of_cores(VM_Version_Ext::number_of_cores());
|
|
_cpu_info->set_number_of_sockets(VM_Version_Ext::number_of_sockets());
|
|
_cpu_info->set_cpu_name(VM_Version_Ext::cpu_name());
|
|
_cpu_info->set_cpu_description(VM_Version_Ext::cpu_description());
|
|
return true;
|
|
}
|
|
|
|
CPUInformationInterface::~CPUInformationInterface() {
|
|
if (_cpu_info != NULL) {
|
|
FREE_C_HEAP_ARRAY(char, _cpu_info->cpu_name());
|
|
_cpu_info->set_cpu_name(NULL);
|
|
FREE_C_HEAP_ARRAY(char, _cpu_info->cpu_description());
|
|
_cpu_info->set_cpu_description(NULL);
|
|
delete _cpu_info;
|
|
_cpu_info = NULL;
|
|
}
|
|
}
|
|
|
|
int CPUInformationInterface::cpu_information(CPUInformation& cpu_info) {
|
|
if (NULL == _cpu_info) {
|
|
return OS_ERR;
|
|
}
|
|
cpu_info = *_cpu_info; // shallow copy assignment
|
|
return OS_OK;
|
|
}
|
|
|
|
class NetworkPerformanceInterface::NetworkPerformance : public CHeapObj<mtInternal> {
|
|
friend class NetworkPerformanceInterface;
|
|
private:
|
|
bool _iphlp_attached;
|
|
|
|
NetworkPerformance();
|
|
NONCOPYABLE(NetworkPerformance);
|
|
bool initialize();
|
|
~NetworkPerformance();
|
|
int network_utilization(NetworkInterface** network_interfaces) const;
|
|
};
|
|
|
|
NetworkPerformanceInterface::NetworkPerformance::NetworkPerformance()
|
|
: _iphlp_attached(false) {
|
|
}
|
|
|
|
bool NetworkPerformanceInterface::NetworkPerformance::initialize() {
|
|
_iphlp_attached = IphlpDll::IphlpAttach();
|
|
return _iphlp_attached;
|
|
}
|
|
|
|
NetworkPerformanceInterface::NetworkPerformance::~NetworkPerformance() {
|
|
if (_iphlp_attached) {
|
|
IphlpDll::IphlpDetach();
|
|
}
|
|
}
|
|
|
|
int NetworkPerformanceInterface::NetworkPerformance::network_utilization(NetworkInterface** network_interfaces) const {
|
|
MIB_IF_TABLE2* table;
|
|
|
|
if (IphlpDll::GetIfTable2(&table) != NO_ERROR) {
|
|
return OS_ERR;
|
|
}
|
|
|
|
NetworkInterface* ret = NULL;
|
|
for (ULONG i = 0; i < table->NumEntries; ++i) {
|
|
if (table->Table[i].InterfaceAndOperStatusFlags.FilterInterface) {
|
|
continue;
|
|
}
|
|
|
|
char buf[256];
|
|
if (WideCharToMultiByte(CP_UTF8, 0, table->Table[i].Description, -1, buf, sizeof(buf), NULL, NULL) == 0) {
|
|
continue;
|
|
}
|
|
|
|
NetworkInterface* cur = new NetworkInterface(buf, table->Table[i].InOctets, table->Table[i].OutOctets, ret);
|
|
ret = cur;
|
|
}
|
|
|
|
IphlpDll::FreeMibTable(table);
|
|
*network_interfaces = ret;
|
|
|
|
return OS_OK;
|
|
}
|
|
|
|
NetworkPerformanceInterface::NetworkPerformanceInterface() {
|
|
_impl = NULL;
|
|
}
|
|
|
|
NetworkPerformanceInterface::~NetworkPerformanceInterface() {
|
|
if (_impl != NULL) {
|
|
delete _impl;
|
|
}
|
|
}
|
|
|
|
bool NetworkPerformanceInterface::initialize() {
|
|
_impl = new NetworkPerformanceInterface::NetworkPerformance();
|
|
return _impl->initialize();
|
|
}
|
|
|
|
int NetworkPerformanceInterface::network_utilization(NetworkInterface** network_interfaces) const {
|
|
return _impl->network_utilization(network_interfaces);
|
|
}
|