2
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
3
* Copyright (c) 2022, 2024, IBM Corp.
4
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6
* This code is free software; you can redistribute it and/or modify it
7
* under the terms of the GNU General Public License version 2 only, as
8
* published by the Free Software Foundation.
10
* This code is distributed in the hope that it will be useful, but WITHOUT
11
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13
* version 2 for more details (a copy is included in the LICENSE file that
14
* accompanied this code).
16
* You should have received a copy of the GNU General Public License version
17
* 2 along with this work; if not, write to the Free Software Foundation,
18
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21
* or visit www.oracle.com if you need additional information or have any
26
#include "precompiled.hpp"
28
#include "libperfstat_aix.hpp"
29
#include "memory/allocation.inline.hpp"
30
#include "memory/resourceArea.hpp"
31
#include "os_aix.inline.hpp"
32
#include "runtime/os.hpp"
33
#include "runtime/os_perf.hpp"
34
#include "runtime/vm_version.hpp"
35
#include "utilities/debug.hpp"
36
#include "utilities/globalDefinitions.hpp"
46
#include <sys/procfs.h>
47
#include <sys/resource.h>
73
* Get info for requested PID from /proc/<pid>/psinfo file
75
static bool read_psinfo(const u_longlong_t& pid, psinfo_t& psinfo) {
76
static size_t BUF_LENGTH = 32 + sizeof(u_longlong_t);
82
jio_snprintf(buf, BUF_LENGTH, "/proc/%llu/psinfo", pid);
89
len = fread(&psinfo, 1, sizeof(psinfo_t), fp);
91
return len == sizeof(psinfo_t);
95
* Get and set ticks for the specified lcpu
97
static OSReturn get_lcpu_ticks(perfstat_id_t* lcpu_name, cpu_tick_store_t* pticks) {
98
perfstat_cpu_t lcpu_stats;
104
// populate cpu_stats
105
if (libperfstat::perfstat_cpu(lcpu_name, &lcpu_stats, sizeof(perfstat_cpu_t), 1) < 1) {
106
memset(pticks, 0, sizeof(cpu_tick_store_t));
110
pticks->user = lcpu_stats.user;
111
pticks->sys = lcpu_stats.sys;
112
pticks->idle = lcpu_stats.idle;
113
pticks->wait = lcpu_stats.wait;
119
* Return CPU load caused by the currently executing process (the jvm).
121
static OSReturn get_jvm_load(double* jvm_uload, double* jvm_sload) {
122
static clock_t ticks_per_sec = sysconf(_SC_CLK_TCK);
123
static u_longlong_t last_timebase = 0;
125
perfstat_process_t jvm_stats;
126
perfstat_id_t name_holder;
127
u_longlong_t timebase_diff;
129
jio_snprintf(name_holder.name, IDENTIFIER_LENGTH, "%d", getpid());
130
if (libperfstat::perfstat_process(&name_holder, &jvm_stats, sizeof(perfstat_process_t), 1) < 1) {
135
timebase_diff = jvm_stats.last_timebase - last_timebase;
136
last_timebase = jvm_stats.last_timebase;
139
*jvm_uload = jvm_stats.ucpu_time / timebase_diff;
142
*jvm_sload = jvm_stats.scpu_time / timebase_diff;
148
static void update_prev_time(jvm_time_store_t* from, jvm_time_store_t* to) {
150
memcpy(to, from, sizeof(jvm_time_store_t));
154
static void update_prev_ticks(cpu_tick_store_t* from, cpu_tick_store_t* to) {
156
memcpy(to, from, sizeof(cpu_tick_store_t));
161
* Calculate the current system load from current ticks using previous ticks as a starting point.
163
static void calculate_updated_load(cpu_tick_store_t* update, cpu_tick_store_t* prev, double* load) {
164
cpu_tick_store_t diff;
166
if (update && prev && load) {
167
diff.user = update->user - prev->user;
168
diff.sys = update->sys - prev->sys;
169
diff.idle = update->idle - prev->idle;
170
diff.wait = update->wait - prev->wait;
172
*load = 1.0 - diff.idle/(diff.sys + diff.user + diff.idle + diff.wait);
177
* Look up lcpu names for later re-use.
179
static bool populate_lcpu_names(int ncpus, perfstat_id_t* lcpu_names) {
181
perfstat_cpu_t* all_lcpu_stats;
182
perfstat_cpu_t* lcpu_stats;
183
perfstat_id_t name_holder;
185
assert(lcpu_names, "Names pointer null");
187
strncpy(name_holder.name, FIRST_CPU, IDENTIFIER_LENGTH);
189
all_lcpu_stats = NEW_RESOURCE_ARRAY(perfstat_cpu_t, ncpus);
191
// If perfstat_cpu does not return the expected number of names, signal error to caller
192
if (ncpus != libperfstat::perfstat_cpu(&name_holder, all_lcpu_stats, sizeof(perfstat_cpu_t), ncpus)) {
196
for (int n = 0; n < ncpus; n++) {
197
strncpy(lcpu_names[n].name, all_lcpu_stats[n].name, IDENTIFIER_LENGTH);
204
* Calculates the context switch rate.
205
* (Context Switches / Tick) * (Tick / s) = Context Switches per second
207
static OSReturn perf_context_switch_rate(double* rate) {
208
static clock_t ticks_per_sec = sysconf(_SC_CLK_TCK);
211
perfstat_cpu_total_t cpu_stats;
213
if (libperfstat::perfstat_cpu_total(nullptr, &cpu_stats, sizeof(perfstat_cpu_total_t), 1) < 0) {
217
ticks = cpu_stats.user + cpu_stats.sys + cpu_stats.idle + cpu_stats.wait;
218
*rate = (cpu_stats.pswitch / ticks) * ticks_per_sec;
223
class CPUPerformanceInterface::CPUPerformance : public CHeapObj<mtInternal> {
226
perfstat_id_t* _lcpu_names;
227
cpu_tick_store_t* _prev_ticks;
234
int cpu_load(int which_logical_cpu, double* cpu_load);
235
int context_switch_rate(double* rate);
236
int cpu_load_total_process(double* cpu_load);
237
int cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad);
240
CPUPerformanceInterface::CPUPerformance::CPUPerformance():
242
_lcpu_names(nullptr),
243
_prev_ticks(nullptr) {}
245
bool CPUPerformanceInterface::CPUPerformance::initialize() {
246
perfstat_cpu_total_t cpu_stats;
248
if (libperfstat::perfstat_cpu_total(nullptr, &cpu_stats, sizeof(perfstat_cpu_total_t), 1) < 0) {
251
if (cpu_stats.ncpus <= 0) {
255
_ncpus = cpu_stats.ncpus;
256
_lcpu_names = NEW_C_HEAP_ARRAY(perfstat_id_t, _ncpus, mtInternal);
258
_prev_ticks = NEW_C_HEAP_ARRAY(cpu_tick_store_t, _ncpus, mtInternal);
259
// Set all prev-tick values to 0
260
memset(_prev_ticks, 0, _ncpus*sizeof(cpu_tick_store_t));
262
if (!populate_lcpu_names(_ncpus, _lcpu_names)) {
269
CPUPerformanceInterface::CPUPerformance::~CPUPerformance() {
271
FREE_C_HEAP_ARRAY(perfstat_id_t, _lcpu_names);
274
FREE_C_HEAP_ARRAY(cpu_tick_store_t, _prev_ticks);
279
* Get CPU load for all processes on specified logical CPU.
281
int CPUPerformanceInterface::CPUPerformance::cpu_load(int lcpu_number, double* lcpu_load) {
282
cpu_tick_store_t ticks;
284
assert(lcpu_load != nullptr, "null pointer passed to cpu_load");
285
assert(lcpu_number < _ncpus, "Invalid lcpu passed to cpu_load");
287
if (get_lcpu_ticks(&_lcpu_names[lcpu_number], &ticks) == OS_ERR) {
292
calculate_updated_load(&ticks, &_prev_ticks[lcpu_number], lcpu_load);
293
update_prev_ticks(&ticks, &_prev_ticks[lcpu_number]);
299
* Get CPU load for all processes on all CPUs.
301
int CPUPerformanceInterface::CPUPerformance::cpu_load_total_process(double* total_load) {
302
cpu_tick_store_t total_ticks;
303
cpu_tick_store_t prev_total_ticks;
305
assert(total_load != nullptr, "null pointer passed to cpu_load_total_process");
307
memset(&total_ticks, 0, sizeof(cpu_tick_store_t));
308
memset(&prev_total_ticks, 0, sizeof(cpu_tick_store_t));
310
for (int lcpu = 0; lcpu < _ncpus; lcpu++) {
311
cpu_tick_store_t lcpu_ticks;
313
if (get_lcpu_ticks(&_lcpu_names[lcpu], &lcpu_ticks) == OS_ERR) {
318
total_ticks.user = lcpu_ticks.user;
319
total_ticks.sys = lcpu_ticks.sys;
320
total_ticks.idle = lcpu_ticks.idle;
321
total_ticks.wait = lcpu_ticks.wait;
323
prev_total_ticks.user += _prev_ticks[lcpu].user;
324
prev_total_ticks.sys += _prev_ticks[lcpu].sys;
325
prev_total_ticks.idle += _prev_ticks[lcpu].idle;
326
prev_total_ticks.wait += _prev_ticks[lcpu].wait;
328
update_prev_ticks(&lcpu_ticks, &_prev_ticks[lcpu]);
331
calculate_updated_load(&total_ticks, &prev_total_ticks, total_load);
337
* Get CPU load for all CPUs.
340
* - pjvmUserLoad: CPU load due to jvm process in user mode. Jvm process assumed to be self process
341
* - pjvmKernelLoad: CPU load due to jvm process in kernel mode. Jvm process assumed to be self process
342
* - psystemTotalLoad: Total CPU load from all process on all logical CPUs
344
* 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
347
int CPUPerformanceInterface::CPUPerformance::cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad) {
351
if (get_jvm_load(&u, &k) == OS_ERR || cpu_load_total_process(&t) == OS_ERR) {
358
if (pjvmKernelLoad) {
361
if (psystemTotalLoad) {
362
*psystemTotalLoad = t;
368
int CPUPerformanceInterface::CPUPerformance::context_switch_rate(double* rate) {
369
return perf_context_switch_rate(rate);
372
CPUPerformanceInterface::CPUPerformanceInterface() {
376
bool CPUPerformanceInterface::initialize() {
377
_impl = new CPUPerformanceInterface::CPUPerformance();
378
return _impl->initialize();
381
CPUPerformanceInterface::~CPUPerformanceInterface() {
382
if (_impl != nullptr) {
387
int CPUPerformanceInterface::cpu_load(int which_logical_cpu, double* cpu_load) const {
388
return _impl->cpu_load(which_logical_cpu, cpu_load);
391
int CPUPerformanceInterface::cpu_load_total_process(double* cpu_load) const {
392
return _impl->cpu_load_total_process(cpu_load);
395
int CPUPerformanceInterface::cpu_loads_process(double* pjvmUserLoad, double* pjvmKernelLoad, double* psystemTotalLoad) const {
396
return _impl->cpu_loads_process(pjvmUserLoad, pjvmKernelLoad, psystemTotalLoad);
399
int CPUPerformanceInterface::context_switch_rate(double* rate) const {
400
return _impl->context_switch_rate(rate);
403
class SystemProcessInterface::SystemProcesses : public CHeapObj<mtInternal> {
405
char* allocate_string(const char* str) const;
411
int system_processes(SystemProcess** system_processes, int* no_of_sys_processes) const;
414
SystemProcessInterface::SystemProcesses::SystemProcesses() {
417
bool SystemProcessInterface::SystemProcesses::initialize() {
421
SystemProcessInterface::SystemProcesses::~SystemProcesses() {
424
char* SystemProcessInterface::SystemProcesses::allocate_string(const char* str) const {
425
if (str != nullptr) {
426
return os::strdup_check_oom(str, mtInternal);
431
int SystemProcessInterface::SystemProcesses::system_processes(SystemProcess** system_processes, int* nprocs) const {
433
perfstat_process_t* proc_stats;
435
perfstat_id_t name_holder;
436
int records_allocated = 0;
438
assert(nprocs != nullptr, "system_processes counter pointers is null!");
442
strncpy(name_holder.name, "", IDENTIFIER_LENGTH);
444
// calling perfstat_<subsystem>(null, null, _, 0) returns number of available records
445
*nprocs = libperfstat::perfstat_process(nullptr, nullptr, sizeof(perfstat_process_t), 0);
447
// expect at least 1 process
451
records_allocated = *nprocs;
452
proc_stats = NEW_RESOURCE_ARRAY(perfstat_process_t, records_allocated);
454
// populate stats && set the actual number of procs that have been populated
455
// should never be higher than requested, but may be lower due to process death
456
*nprocs = libperfstat::perfstat_process(&name_holder, proc_stats, sizeof(perfstat_process_t), records_allocated);
458
for (int n = 0; n < *nprocs; n++) {
460
// Note: SystemProcess with free these in its dtor.
461
char* name = NEW_C_HEAP_ARRAY(char, IDENTIFIER_LENGTH, mtInternal);
462
char* exe_name = NEW_C_HEAP_ARRAY(char, PRFNSZ, mtInternal);
463
char* cmd_line = NEW_C_HEAP_ARRAY(char, PRARGSZ, mtInternal);
465
strncpy(name, proc_stats[n].proc_name, IDENTIFIER_LENGTH);
467
if (read_psinfo(proc_stats[n].pid, psinfo)) {
468
strncpy(exe_name, psinfo.pr_fname, PRFNSZ);
469
strncpy(cmd_line, psinfo.pr_psargs, PRARGSZ);
472
// create a new SystemProcess with next pointing to current head.
473
SystemProcess* sp = new SystemProcess(proc_stats[n].pid,
482
*system_processes = head;
486
int SystemProcessInterface::system_processes(SystemProcess** system_procs, int* no_of_sys_processes) const {
487
return _impl->system_processes(system_procs, no_of_sys_processes);
490
SystemProcessInterface::SystemProcessInterface() {
494
bool SystemProcessInterface::initialize() {
495
_impl = new SystemProcessInterface::SystemProcesses();
496
return _impl->initialize();
499
SystemProcessInterface::~SystemProcessInterface() {
500
if (_impl != nullptr) {
505
CPUInformationInterface::CPUInformationInterface() {
509
bool CPUInformationInterface::initialize() {
510
_cpu_info = new CPUInformation();
511
VM_Version::initialize_cpu_information();
512
_cpu_info->set_number_of_hardware_threads(VM_Version::number_of_threads());
513
_cpu_info->set_number_of_cores(VM_Version::number_of_cores());
514
_cpu_info->set_number_of_sockets(VM_Version::number_of_sockets());
515
_cpu_info->set_cpu_name(VM_Version::cpu_name());
516
_cpu_info->set_cpu_description(VM_Version::cpu_description());
520
CPUInformationInterface::~CPUInformationInterface() {
521
if (_cpu_info != nullptr) {
522
if (_cpu_info->cpu_name() != nullptr) {
523
const char* cpu_name = _cpu_info->cpu_name();
524
FREE_C_HEAP_ARRAY(char, cpu_name);
525
_cpu_info->set_cpu_name(nullptr);
527
if (_cpu_info->cpu_description() != nullptr) {
528
const char* cpu_desc = _cpu_info->cpu_description();
529
FREE_C_HEAP_ARRAY(char, cpu_desc);
530
_cpu_info->set_cpu_description(nullptr);
536
int CPUInformationInterface::cpu_information(CPUInformation& cpu_info) {
537
if (_cpu_info == nullptr) {
541
cpu_info = *_cpu_info; // shallow copy assignment
545
class NetworkPerformanceInterface::NetworkPerformance : public CHeapObj<mtInternal> {
546
NONCOPYABLE(NetworkPerformance);
549
char* allocate_string(const char* str) const;
552
NetworkPerformance();
554
~NetworkPerformance();
555
int network_utilization(NetworkInterface** network_interfaces) const;
558
NetworkPerformanceInterface::NetworkPerformance::NetworkPerformance() {}
560
bool NetworkPerformanceInterface::NetworkPerformance::initialize() {
564
NetworkPerformanceInterface::NetworkPerformance::~NetworkPerformance() {}
566
int NetworkPerformanceInterface::NetworkPerformance::network_utilization(NetworkInterface** network_interfaces) const {
568
perfstat_netinterface_t* net_stats;
569
perfstat_id_t name_holder;
570
int records_allocated = 0;
572
assert(network_interfaces != nullptr, "network_interfaces is null");
574
*network_interfaces = nullptr;
575
strncpy(name_holder.name , FIRST_NETINTERFACE, IDENTIFIER_LENGTH);
577
// calling perfstat_<subsystem>(null, null, _, 0) returns number of available records
578
if ((n_records = libperfstat::perfstat_netinterface(nullptr, nullptr, sizeof(perfstat_netinterface_t), 0)) < 0) {
582
records_allocated = n_records;
583
net_stats = NEW_C_HEAP_ARRAY(perfstat_netinterface_t, records_allocated, mtInternal);
585
n_records = libperfstat::perfstat_netinterface(&name_holder, net_stats, sizeof(perfstat_netinterface_t), n_records);
589
FREE_C_HEAP_ARRAY(perfstat_netinterface_t, net_stats);
593
for (int i = 0; i < n_records; i++) {
594
// Create new Network interface *with current head as next node*
595
// Note: NetworkInterface makes copies of these string values into RA memory
597
// (1) we are free to clean our values upon exiting this proc
598
// (2) we avoid using RA-alloced memory here (ie. do not use NEW_RESOURCE_ARRAY)
599
NetworkInterface* new_interface = new NetworkInterface(net_stats[i].name,
602
*network_interfaces);
603
*network_interfaces = new_interface;
606
FREE_C_HEAP_ARRAY(perfstat_netinterface_t, net_stats);
610
NetworkPerformanceInterface::NetworkPerformanceInterface() {
614
NetworkPerformanceInterface::~NetworkPerformanceInterface() {
615
if (_impl != nullptr) {
620
bool NetworkPerformanceInterface::initialize() {
621
_impl = new NetworkPerformanceInterface::NetworkPerformance();
622
return _impl->initialize();
625
int NetworkPerformanceInterface::network_utilization(NetworkInterface** network_interfaces) const {
626
return _impl->network_utilization(network_interfaces);