/*
* 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);
}
Messung V0.5 in Prozent C=88 H=92 G=89
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet am 2026-09-09)
¤
*© Formatika GbR, Deutschland