/*
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, IBM Corp.
* 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 "jvm.h"
#include "libperfstat_aix.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
#include "os_aix.inline.hpp"
#include "runtime/os.hpp"
#include "runtime/os_perf.hpp"
#include "runtime/vm_version.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
#include <dirent.h>
#include <dlfcn.h>
#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <sys/procfs.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct {
u_longlong_t user;
u_longlong_t sys;
u_longlong_t idle;
u_longlong_t wait;
} cpu_tick_store_t;
typedef struct {
double utime;
double stime;
} jvm_time_store_t;
enum {
UNDETECTED,
UNDETECTABLE,
LINUX26_NPTL,
BAREMETAL
};
/**
* Get info for requested PID from /proc/<pid>/psinfo file
*/
static bool read_psinfo(const u_longlong_t& pid, psinfo_t& psinfo) {
static size_t BUF_LENGTH = 32 + sizeof(u_longlong_t);
FILE* fp;
char buf[BUF_LENGTH];
int len;
jio_snprintf(buf, BUF_LENGTH, "/proc/%llu/psinfo", pid);
fp = fopen(buf, "r");
if (!fp) {
return false;
}
len = fread(&psinfo, 1, sizeof(psinfo_t), fp);
return len == sizeof(psinfo_t);
}
/**
* Get and set ticks for the specified lcpu
*/
static OSReturn get_lcpu_ticks(perfstat_id_t* lcpu_name, cpu_tick_store_t* pticks) {
perfstat_cpu_t lcpu_stats;
if (!pticks) {
return OS_ERR;
}
// populate cpu_stats
if (libperfstat::perfstat_cpu(lcpu_name, &lcpu_stats, sizeof(perfstat_cpu_t), 1) < 1) {
memset(pticks, 0, sizeof(cpu_tick_store_t));
return OS_ERR;
}
pticks->user = lcpu_stats.user;
pticks->sys = lcpu_stats.sys;
pticks->idle = lcpu_stats.idle;
pticks->wait = lcpu_stats.wait;
return OS_OK;
}
/**
* Return CPU load caused by the currently executing process (the jvm).
*/
static OSReturn get_jvm_load(double* jvm_uload, double* jvm_sload) {
static clock_t ticks_per_sec = sysconf(_SC_CLK_TCK);
static u_longlong_t last_timebase = 0;
perfstat_process_t jvm_stats;
perfstat_id_t name_holder;
u_longlong_t timebase_diff;
jio_snprintf(name_holder.name, IDENTIFIER_LENGTH, "%d", getpid());
if (libperfstat::perfstat_process(&name_holder, &jvm_stats, sizeof(perfstat_process_t), 1) < 1) {
return OS_ERR;
}
// Update timebase
timebase_diff = jvm_stats.last_timebase - last_timebase;
last_timebase = jvm_stats.last_timebase;
if (jvm_uload) {
*jvm_uload = jvm_stats.ucpu_time / timebase_diff;
}
if (jvm_sload) {
*jvm_sload = jvm_stats.scpu_time / timebase_diff;
}
return OS_OK;
}
static void update_prev_time(jvm_time_store_t* from, jvm_time_store_t* to) {
if (from && to) {
memcpy(to, from, sizeof(jvm_time_store_t));
}
}
static void update_prev_ticks(cpu_tick_store_t* from, cpu_tick_store_t* to) {
if (from && to) {
memcpy(to, from, sizeof(cpu_tick_store_t));
}
}
/**
* Calculate the current system load from current ticks using previous ticks as a starting point.
*/
static void calculate_updated_load(cpu_tick_store_t* update, cpu_tick_store_t* prev, double* load) {
cpu_tick_store_t diff;
if (update && prev && load) {
diff.user = update->user - prev->user;
diff.sys = update->sys - prev->sys;
diff.idle = update->idle - prev->idle;
diff.wait = update->wait - prev->wait;
*load = 1.0 - diff.idle/(diff.sys + diff.user + diff.idle + diff.wait);
}
}
/**
* Look up lcpu names for later re-use.
*/
static bool populate_lcpu_names(int ncpus, perfstat_id_t* lcpu_names) {
ResourceMark rm;
perfstat_cpu_t* all_lcpu_stats;
perfstat_cpu_t* lcpu_stats;
perfstat_id_t name_holder;
assert(lcpu_names, "Names pointer NULL");
strncpy(name_holder.name, FIRST_CPU, IDENTIFIER_LENGTH);
all_lcpu_stats = NEW_RESOURCE_ARRAY(perfstat_cpu_t, ncpus);
// If perfstat_cpu does not return the expected number of names, signal error to caller
if (ncpus != libperfstat::perfstat_cpu(&name_holder, all_lcpu_stats, sizeof(perfstat_cpu_t), ncpus)) {
return false;
}
for (int n = 0; n < ncpus; n++) {
strncpy(lcpu_names[n].name, all_lcpu_stats[n].name, IDENTIFIER_LENGTH);
}
return true;
}
/**
* Calculates the context switch rate.
* (Context Switches / Tick) * (Tick / s) = Context Switches per second
*/
static OSReturn perf_context_switch_rate(double* rate) {
static clock_t ticks_per_sec = sysconf(_SC_CLK_TCK);
u_longlong_t ticks;
perfstat_cpu_total_t cpu_stats;
if (libperfstat::perfstat_cpu_total(NULL, &cpu_stats, sizeof(perfstat_cpu_total_t), 1) < 0) {
return OS_ERR;
}
ticks = cpu_stats.user + cpu_stats.sys + cpu_stats.idle + cpu_stats.wait;
*rate = (cpu_stats.pswitch / ticks) * ticks_per_sec;
return OS_OK;
}
class CPUPerformanceInterface::CPUPerformance : public CHeapObj<mtInternal> {
private:
int _ncpus;
perfstat_id_t* _lcpu_names;
cpu_tick_store_t* _prev_ticks;
public:
CPUPerformance();
bool initialize();
~CPUPerformance();
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* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad);
};
CPUPerformanceInterface::CPUPerformance::CPUPerformance():
_ncpus(0),
_lcpu_names(NULL),
_prev_ticks(NULL) {}
bool CPUPerformanceInterface::CPUPerformance::initialize() {
perfstat_cpu_total_t cpu_stats;
if (libperfstat::perfstat_cpu_total(NULL, &cpu_stats, sizeof(perfstat_cpu_total_t), 1) < 0) {
return false;
}
if (cpu_stats.ncpus <= 0) {
return false;
}
_ncpus = cpu_stats.ncpus;
_lcpu_names = NEW_C_HEAP_ARRAY(perfstat_id_t, _ncpus, mtInternal);
_prev_ticks = NEW_C_HEAP_ARRAY(cpu_tick_store_t, _ncpus, mtInternal);
// Set all prev-tick values to 0
memset(_prev_ticks, 0, _ncpus*sizeof(cpu_tick_store_t));
if (!populate_lcpu_names(_ncpus, _lcpu_names)) {
return false;
}
return true;
}
CPUPerformanceInterface::CPUPerformance::~CPUPerformance() {
if (_lcpu_names) {
FREE_C_HEAP_ARRAY(perfstat_id_t, _lcpu_names);
}
if (_prev_ticks) {
FREE_C_HEAP_ARRAY(cpu_tick_store_t, _prev_ticks);
}
}
/**
* Get CPU load for all processes on specified logical CPU.
*/
int CPUPerformanceInterface::CPUPerformance::cpu_load(int lcpu_number, double* lcpu_load) {
cpu_tick_store_t ticks;
assert(lcpu_load != NULL, "NULL pointer passed to cpu_load");
assert(lcpu_number < _ncpus, "Invalid lcpu passed to cpu_load");
if (get_lcpu_ticks(&_lcpu_names[lcpu_number], &ticks) == OS_ERR) {
*lcpu_load = -1.0;
return OS_ERR;
}
calculate_updated_load(&ticks, &_prev_ticks[lcpu_number], lcpu_load);
update_prev_ticks(&ticks, &_prev_ticks[lcpu_number]);
return OS_OK;
}
/**
* Get CPU load for all processes on all CPUs.
*/
int CPUPerformanceInterface::CPUPerformance::cpu_load_total_process(double* total_load) {
cpu_tick_store_t total_ticks;
cpu_tick_store_t prev_total_ticks;
assert(total_load != NULL, "NULL pointer passed to cpu_load_total_process");
memset(&total_ticks, 0, sizeof(cpu_tick_store_t));
memset(&prev_total_ticks, 0, sizeof(cpu_tick_store_t));
for (int lcpu = 0; lcpu < _ncpus; lcpu++) {
cpu_tick_store_t lcpu_ticks;
if (get_lcpu_ticks(&_lcpu_names[lcpu], &lcpu_ticks) == OS_ERR) {
*total_load = -1.0;
return OS_ERR;
}
total_ticks.user = lcpu_ticks.user;
total_ticks.sys = lcpu_ticks.sys;
total_ticks.idle = lcpu_ticks.idle;
total_ticks.wait = lcpu_ticks.wait;
prev_total_ticks.user += _prev_ticks[lcpu].user;
prev_total_ticks.sys += _prev_ticks[lcpu].sys;
prev_total_ticks.idle += _prev_ticks[lcpu].idle;
prev_total_ticks.wait += _prev_ticks[lcpu].wait;
update_prev_ticks(&lcpu_ticks, &_prev_ticks[lcpu]);
}
calculate_updated_load(&total_ticks, &prev_total_ticks, total_load);
return OS_OK;
}
/**
* Get CPU load for all CPUs.
*
* Set values for:
* - pjvmUserLoad: CPU load due to jvm process in user mode. Jvm process assumed to be self process
* - pjvmKernelLoad: CPU load due to jvm process in kernel mode. Jvm process assumed to be self process
* - psystemTotalLoad: Total CPU load from all process on all logical CPUs
*
* Note: If any of the above loads cannot be calculated, this procedure returns OS_ERR and any load that could not be calculated is set to -1
*
*/
int CPUPerformanceInterface::CPUPerformance::cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad) {
double u, k, t;
int retval = OS_OK;
if (get_jvm_load(&u, &k) == OS_ERR || cpu_load_total_process(&t) == OS_ERR) {
retval = OS_ERR;
}
if (pjvmUserLoad) {
*pjvmUserLoad = u;
}
if (pjvmKernelLoad) {
*pjvmKernelLoad = k;
}
if (psystemTotalLoad) {
*psystemTotalLoad = t;
}
return retval;
}
int CPUPerformanceInterface::CPUPerformance::context_switch_rate(double* rate) {
return perf_context_switch_rate(rate);
}
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::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::context_switch_rate(double* rate) const {
return _impl->context_switch_rate(rate);
}
class SystemProcessInterface::SystemProcesses : public CHeapObj<mtInternal> {
private:
char* allocate_string(const char* str) const;
public:
SystemProcesses();
bool initialize();
~SystemProcesses();
int system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const;
};
SystemProcessInterface::SystemProcesses::SystemProcesses() {
}
bool SystemProcessInterface::SystemProcesses::initialize() {
return true;
}
SystemProcessInterface::SystemProcesses::~SystemProcesses() {
}
char* SystemProcessInterface::SystemProcesses::allocate_string(const char* str) const {
if (str != NULL) {
return os::strdup_check_oom(str, mtInternal);
}
return NULL;
}
int SystemProcessInterface::SystemProcesses::system_processes(SystemProcess** system_processes, int* nprocs) const {
ResourceMark rm;
perfstat_process_t* proc_stats;
SystemProcess* head;
perfstat_id_t name_holder;
int records_allocated = 0;
assert(nprocs != NULL, "system_processes counter pointers is NULL!");
head = NULL;
*nprocs = 0;
strncpy(name_holder.name, "", IDENTIFIER_LENGTH);
// calling perfstat_<subsystem>(NULL, NULL, _, 0) returns number of available records
*nprocs = libperfstat::perfstat_process(NULL, NULL, sizeof(perfstat_process_t), 0);
if(*nprocs < 1) {
// expect at least 1 process
return OS_ERR;
}
records_allocated = *nprocs;
proc_stats = NEW_RESOURCE_ARRAY(perfstat_process_t, records_allocated);
// populate stats && set the actual number of procs that have been populated
// should never be higher than requested, but may be lower due to process death
*nprocs = libperfstat::perfstat_process(&name_holder, proc_stats, sizeof(perfstat_process_t), records_allocated);
for (int n = 0; n < *nprocs; n++) {
psinfo_t psinfo;
// Note: SystemProcess with free these in its dtor.
char* name = NEW_C_HEAP_ARRAY(char, IDENTIFIER_LENGTH, mtInternal);
char* exe_name = NEW_C_HEAP_ARRAY(char, PRFNSZ, mtInternal);
char* cmd_line = NEW_C_HEAP_ARRAY(char, PRARGSZ, mtInternal);
strncpy(name, proc_stats[n].proc_name, IDENTIFIER_LENGTH);
if (read_psinfo(proc_stats[n].pid, psinfo)) {
strncpy(exe_name, psinfo.pr_fname, PRFNSZ);
strncpy(cmd_line, psinfo.pr_psargs, PRARGSZ);
}
// create a new SystemProcess with next pointing to current head.
SystemProcess* sp = new SystemProcess(proc_stats[n].pid,
name,
exe_name,
cmd_line,
head);
// update head.
head = sp;
}
*system_processes = head;
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();
VM_Version::initialize_cpu_information();
_cpu_info->set_number_of_hardware_threads(VM_Version::number_of_threads());
_cpu_info->set_number_of_cores(VM_Version::number_of_cores());
_cpu_info->set_number_of_sockets(VM_Version::number_of_sockets());
_cpu_info->set_cpu_name(VM_Version::cpu_name());
_cpu_info->set_cpu_description(VM_Version::cpu_description());
return true;
}
CPUInformationInterface::~CPUInformationInterface() {
if (_cpu_info != NULL) {
if (_cpu_info->cpu_name() != NULL) {
const char* cpu_name = _cpu_info->cpu_name();
FREE_C_HEAP_ARRAY(char, cpu_name);
_cpu_info->set_cpu_name(NULL);
}
if (_cpu_info->cpu_description() != NULL) {
const char* cpu_desc = _cpu_info->cpu_description();
FREE_C_HEAP_ARRAY(char, cpu_desc);
_cpu_info->set_cpu_description(NULL);
}
delete _cpu_info;
}
}
int CPUInformationInterface::cpu_information(CPUInformation& cpu_info) {
if (_cpu_info == NULL) {
return OS_ERR;
}
cpu_info = *_cpu_info; // shallow copy assignment
return OS_OK;
}
class NetworkPerformanceInterface::NetworkPerformance : public CHeapObj<mtInternal> {
NONCOPYABLE(NetworkPerformance);
private:
char* allocate_string(const char* str) const;
public:
NetworkPerformance();
bool initialize();
~NetworkPerformance();
int network_utilization(NetworkInterface** network_interfaces) const;
};
NetworkPerformanceInterface::NetworkPerformance::NetworkPerformance() {}
bool NetworkPerformanceInterface::NetworkPerformance::initialize() {
return true;
}
NetworkPerformanceInterface::NetworkPerformance::~NetworkPerformance() {}
int NetworkPerformanceInterface::NetworkPerformance::network_utilization(NetworkInterface** network_interfaces) const {
int n_records = 0;
perfstat_netinterface_t* net_stats;
perfstat_id_t name_holder;
int records_allocated = 0;
assert(network_interfaces != NULL, "network_interfaces is NULL");
*network_interfaces = NULL;
strncpy(name_holder.name , FIRST_NETINTERFACE, IDENTIFIER_LENGTH);
// calling perfstat_<subsystem>(NULL, NULL, _, 0) returns number of available records
if ((n_records = libperfstat::perfstat_netinterface(NULL, NULL, sizeof(perfstat_netinterface_t), 0)) < 0) {
return OS_ERR;
}
records_allocated = n_records;
net_stats = NEW_C_HEAP_ARRAY(perfstat_netinterface_t, records_allocated, mtInternal);
n_records = libperfstat::perfstat_netinterface(&name_holder, net_stats, sizeof(perfstat_netinterface_t), n_records);
// check for error
if (n_records < 0) {
FREE_C_HEAP_ARRAY(perfstat_netinterface_t, net_stats);
return OS_ERR;
}
for (int i = 0; i < n_records; i++) {
// Create new Network interface *with current head as next node*
// Note: NetworkInterface makes copies of these string values into RA memory
// this means:
// (1) we are free to clean our values upon exiting this proc
// (2) we avoid using RA-alloced memory here (ie. do not use NEW_RESOURCE_ARRAY)
NetworkInterface* new_interface = new NetworkInterface(net_stats[i].name,
net_stats[i].ibytes,
net_stats[i].obytes,
*network_interfaces);
*network_interfaces = new_interface;
}
FREE_C_HEAP_ARRAY(perfstat_netinterface_t, net_stats);
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);
}
¤ Dauer der Verarbeitung: 0.24 Sekunden
(vorverarbeitet)
¤
|
Haftungshinweis
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.
|