/* ** Zabbix ** Copyright (C) 2001-2021 Zabbix SIA ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program 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 for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ #include "common.h" #include "vmstats.h" #include "log.h" #ifdef _AIX #ifndef XINTFRAC /* defined in IBM AIX 7.1 libperfstat.h, not defined in AIX 6.1 */ #include #define XINTFRAC ((double)_system_configuration.Xint / _system_configuration.Xfrac) /* Example of XINTFRAC = 125.000000 / 64.000000 = 1.953125. Apparently XINTFRAC is a period (in nanoseconds) */ /* of CPU ticks on a machine. For example, 1.953125 could mean there is 1.953125 nanoseconds between ticks */ /* and number of ticks in second is 1.0 / (1.953125 * 10^-9) = 512000000. So, tick frequency is 512 MHz. */ #endif static int last_clock = 0; /* --- kthr --- */ static zbx_uint64_t last_runque = 0; /* length of the run queue (processes ready) */ static zbx_uint64_t last_swpque = 0; /* length of the swap queue (processes waiting to be paged in) */ /* --- page --- */ static zbx_uint64_t last_pgins = 0; /* number of pages paged in */ static zbx_uint64_t last_pgouts = 0; /* number of pages paged out */ static zbx_uint64_t last_pgspins = 0; /* number of page ins from paging space */ static zbx_uint64_t last_pgspouts = 0; /* number of page outs from paging space */ static zbx_uint64_t last_cycles = 0; /* number of page replacement cycles */ static zbx_uint64_t last_scans = 0; /* number of page scans by clock */ /* -- faults -- */ static zbx_uint64_t last_devintrs = 0; /* number of device interrupts */ static zbx_uint64_t last_syscall = 0; /* number of system calls executed */ static zbx_uint64_t last_pswitch = 0; /* number of process switches (change in currently running */ /* process) */ /* --- cpu ---- */ /* Raw numbers of ticks are readings from forward-ticking counters. */ /* Only difference between 2 readings is meaningful. */ static zbx_uint64_t last_puser = 0; /* raw number of physical processor ticks in user mode */ static zbx_uint64_t last_psys = 0; /* raw number of physical processor ticks in system mode */ static zbx_uint64_t last_pidle = 0; /* raw number of physical processor ticks idle */ static zbx_uint64_t last_pwait = 0; /* raw number of physical processor ticks waiting for I/O */ static zbx_uint64_t last_user = 0; /* raw total number of clock ticks spent in user mode */ static zbx_uint64_t last_sys = 0; /* raw total number of clock ticks spent in system mode */ static zbx_uint64_t last_idle = 0; /* raw total number of clock ticks spent idle */ static zbx_uint64_t last_wait = 0; /* raw total number of clock ticks spent waiting for I/O */ static zbx_uint64_t last_timebase_last = 0; /* most recent processor time base timestamp */ static zbx_uint64_t last_pool_idle_time = 0; /* number of clock ticks a processor in the shared pool was */ /* idle */ static zbx_uint64_t last_idle_donated_purr = 0; /* number of idle cycles donated by a dedicated partition */ /* enabled for donation */ static zbx_uint64_t last_busy_donated_purr = 0; /* number of busy cycles donated by a dedicated partition */ /* enabled for donation */ static zbx_uint64_t last_idle_stolen_purr = 0; /* number of idle cycles stolen by the hypervisor from */ /* a dedicated partition */ static zbx_uint64_t last_busy_stolen_purr = 0; /* number of busy cycles stolen by the hypervisor from */ /* a dedicated partition */ /* --- disk --- */ static zbx_uint64_t last_xfers = 0; /* total number of transfers to/from disk */ static zbx_uint64_t last_wblks = 0; /* 512 bytes blocks written to all disks */ static zbx_uint64_t last_rblks = 0; /* 512 bytes blocks read from all disks */ /****************************************************************************** * * * Function: update_vmstat * * * * Purpose: update vmstat values at most once per second * * * * Parameters: vmstat - a structure containing vmstat data * * * * Comments: on first iteration only save last data, on second - set vmstat * * data and indicate that it is available * * * ******************************************************************************/ static void update_vmstat(ZBX_VMSTAT_DATA *vmstat) { #if defined(HAVE_LIBPERFSTAT) int now; zbx_uint64_t dlcpu_us, dlcpu_sy, dlcpu_id, dlcpu_wa, lcputime; perfstat_memory_total_t memstats; perfstat_cpu_total_t cpustats; perfstat_disk_total_t diskstats; #ifdef _AIXVERSION_530 zbx_uint64_t dpcpu_us, dpcpu_sy, dpcpu_id, dpcpu_wa, pcputime, dtimebase; zbx_uint64_t delta_purr, entitled_purr, unused_purr, r1, r2; perfstat_partition_total_t lparstats; #ifdef HAVE_AIXOSLEVEL_530006 zbx_uint64_t didle_donated_purr, dbusy_donated_purr, didle_stolen_purr, dbusy_stolen_purr; #endif /* HAVE_AIXOSLEVEL_530006 */ #endif /* _AIXVERSION_530 */ now = (int)time(NULL); /* Retrieve metrics from AIX libperfstat APIs. * Upon successful completion, the number of structures filled is returned. * If unsuccessful, a value of -1 is returned and the errno global variable is set. */ #ifdef _AIXVERSION_530 if (-1 == perfstat_partition_total(NULL, &lparstats, sizeof(lparstats), 1)) { zabbix_log(LOG_LEVEL_DEBUG, "perfstat_partition_total: %s", zbx_strerror(errno)); return; } #endif if (-1 == perfstat_cpu_total(NULL, &cpustats, sizeof(cpustats), 1)) { zabbix_log(LOG_LEVEL_DEBUG, "perfstat_cpu_total: %s", zbx_strerror(errno)); return; } if (-1 == perfstat_memory_total(NULL, &memstats, sizeof(memstats), 1)) { zabbix_log(LOG_LEVEL_DEBUG, "perfstat_memory_total: %s", zbx_strerror(errno)); return; } if (-1 == perfstat_disk_total(NULL, &diskstats, sizeof(diskstats), 1)) { zabbix_log(LOG_LEVEL_DEBUG, "perfstat_disk_total: %s", zbx_strerror(errno)); return; } /* set static vmstat values on first iteration, dynamic on next iterations (at most once per second) */ if (0 == last_clock) { #ifdef _AIXVERSION_530 vmstat->shared_enabled = (unsigned char)lparstats.type.b.shared_enabled; vmstat->pool_util_authority = (unsigned char)lparstats.type.b.pool_util_authority; #endif #ifdef HAVE_AIXOSLEVEL_520004 vmstat->aix52stats = 1; #endif } else if (now > last_clock) { /* --- kthr --- */ vmstat->kthr_r = (double)(cpustats.runque - last_runque) / (now - last_clock); vmstat->kthr_b = (double)(cpustats.swpque - last_swpque) / (now - last_clock); /* --- page --- */ vmstat->fi = (double)(memstats.pgins - last_pgins) / (now - last_clock); vmstat->fo = (double)(memstats.pgouts - last_pgouts) / (now - last_clock); vmstat->pi = (double)(memstats.pgspins - last_pgspins) / (now - last_clock); vmstat->po = (double)(memstats.pgspouts - last_pgspouts) / (now - last_clock); vmstat->fr = (double)(memstats.cycles - last_cycles) / (now - last_clock); vmstat->sr = (double)(memstats.scans - last_scans) / (now - last_clock); /* -- faults -- */ vmstat->in = (double)(cpustats.devintrs - last_devintrs) / (now - last_clock); vmstat->sy = (double)(cpustats.syscall - last_syscall) / (now - last_clock); vmstat->cs = (double)(cpustats.pswitch - last_pswitch) / (now - last_clock); #ifdef _AIXVERSION_530 /* number of CPU ticks since the last measurement by mode */ dpcpu_us = lparstats.puser - last_puser; dpcpu_sy = lparstats.psys - last_psys; dpcpu_id = lparstats.pidle - last_pidle; dpcpu_wa = lparstats.pwait - last_pwait; /* total number of CPU ticks since the last measurement */ delta_purr = dpcpu_us + dpcpu_sy + dpcpu_id + dpcpu_wa; pcputime = delta_purr; #endif /* _AIXVERSION_530 */ dlcpu_us = cpustats.user - last_user; dlcpu_sy = cpustats.sys - last_sys; dlcpu_id = cpustats.idle - last_idle; dlcpu_wa = cpustats.wait - last_wait; lcputime = dlcpu_us + dlcpu_sy + dlcpu_id + dlcpu_wa; #ifdef _AIXVERSION_530 /* Distribute the donated and stolen purr to the existing purr buckets in case if donation is enabled. */ #ifdef HAVE_AIXOSLEVEL_530006 if (lparstats.type.b.donate_enabled) { didle_donated_purr = lparstats.idle_donated_purr - last_idle_donated_purr; dbusy_donated_purr = lparstats.busy_donated_purr - last_busy_donated_purr; didle_stolen_purr = lparstats.idle_stolen_purr - last_idle_stolen_purr; dbusy_stolen_purr = lparstats.busy_stolen_purr - last_busy_stolen_purr; if (0 != dlcpu_id + dlcpu_wa) { r1 = dlcpu_id / (dlcpu_id + dlcpu_wa); r2 = dlcpu_wa / (dlcpu_id + dlcpu_wa); } else r1 = r2 = 0; dpcpu_us += didle_donated_purr * r1 + didle_stolen_purr * r1; dpcpu_wa += didle_donated_purr * r2 + didle_stolen_purr * r2; dpcpu_sy += dbusy_donated_purr + dbusy_stolen_purr; delta_purr += didle_donated_purr + dbusy_donated_purr + didle_stolen_purr + dbusy_stolen_purr; pcputime = delta_purr; } #endif /* HAVE_AIXOSLEVEL_530006 */ /* number of physical processor tics between current and previous measurement */ dtimebase = lparstats.timebase_last - last_timebase_last; /* 'perfstat_partition_total_t' element 'entitled_proc_capacity' is "number of processor units this */ /* partition is entitled to receive". It is expressed as multiplied by 100 and rounded to integer, */ /* therefore we divide it by 100 and convert to floating point number to get its real value, as */ /* shown by 'lparstat' command. */ vmstat->ent = lparstats.entitled_proc_capacity / 100.0; if (lparstats.type.b.shared_enabled) { entitled_purr = dtimebase * vmstat->ent; if (entitled_purr < delta_purr) { /* when above entitlement, use consumption in percentages */ entitled_purr = delta_purr; } unused_purr = entitled_purr - delta_purr; /* distribute unused purr in wait and idle proportionally to logical wait and idle */ if (0 != dlcpu_wa + dlcpu_id) { dpcpu_wa += unused_purr * ((double)dlcpu_wa / (dlcpu_wa + dlcpu_id)); dpcpu_id += unused_purr * ((double)dlcpu_id / (dlcpu_wa + dlcpu_id)); } pcputime = entitled_purr; } /* Physical Processor Utilization */ vmstat->cpu_us = dpcpu_us * 100.0 / pcputime; vmstat->cpu_sy = dpcpu_sy * 100.0 / pcputime; vmstat->cpu_id = dpcpu_id * 100.0 / pcputime; vmstat->cpu_wa = dpcpu_wa * 100.0 / pcputime; /* Physical Processor Consumed */ /* Interesting values only for "shared" LPARs. */ /* For "dedicated" LPARs expect approximately the same value as assigned to the LPAR through HMC. */ vmstat->cpu_pc = (double)delta_purr / dtimebase; if (lparstats.type.b.shared_enabled) { /* Percentage of Entitlement Consumed */ vmstat->cpu_ec = (vmstat->cpu_pc / vmstat->ent) * 100.0; /* Logical Processor Utilization */ vmstat->cpu_lbusy = (dlcpu_us + dlcpu_sy) * 100.0 / lcputime; if (lparstats.type.b.pool_util_authority) { /* Available Pool Processor (app) */ vmstat->cpu_app = (lparstats.pool_idle_time - last_pool_idle_time) / (XINTFRAC * dtimebase); } } else vmstat->cpu_ec = 100.0; /* trivial value for LPAR type "dedicated" */ #else /* not _AIXVERSION_530 */ /* Physical Processor Utilization */ vmstat->cpu_us = dlcpu_us * 100.0 / lcputime; vmstat->cpu_sy = dlcpu_sy * 100.0 / lcputime; vmstat->cpu_id = dlcpu_id * 100.0 / lcputime; vmstat->cpu_wa = dlcpu_wa * 100.0 / lcputime; #endif /* _AIXVERSION_530 */ /* --- disk --- */ vmstat->disk_bps = 512 * ((diskstats.wblks - last_wblks) + (diskstats.rblks - last_rblks)) / (now - last_clock); vmstat->disk_tps = (double)(diskstats.xfers - last_xfers) / (now - last_clock); /* -- memory -- */ #ifdef HAVE_AIXOSLEVEL_520004 vmstat->mem_avm = (zbx_uint64_t)memstats.virt_active; /* Active virtual pages. Virtual pages are considered active if they have been accessed */ #endif vmstat->mem_fre = (zbx_uint64_t)memstats.real_free; /* free real memory (in 4KB pages) */ /* indicate that vmstat data is available */ vmstat->data_available = 1; } /* saving last values */ last_clock = now; /* --- kthr -- */ last_runque = (zbx_uint64_t)cpustats.runque; last_swpque = (zbx_uint64_t)cpustats.swpque; /* --- page --- */ last_pgins = (zbx_uint64_t)memstats.pgins; last_pgouts = (zbx_uint64_t)memstats.pgouts; last_pgspins = (zbx_uint64_t)memstats.pgspins; last_pgspouts = (zbx_uint64_t)memstats.pgspouts; last_cycles = (zbx_uint64_t)memstats.cycles; last_scans = (zbx_uint64_t)memstats.scans; /* -- faults -- */ last_devintrs = (zbx_uint64_t)cpustats.devintrs; last_syscall = (zbx_uint64_t)cpustats.syscall; last_pswitch = (zbx_uint64_t)cpustats.pswitch; /* --- cpu ---- */ #ifdef _AIXVERSION_530 last_puser = (zbx_uint64_t)lparstats.puser; last_psys = (zbx_uint64_t)lparstats.psys; last_pidle = (zbx_uint64_t)lparstats.pidle; last_pwait = (zbx_uint64_t)lparstats.pwait; last_timebase_last = (zbx_uint64_t)lparstats.timebase_last; last_pool_idle_time = (zbx_uint64_t)lparstats.pool_idle_time; #ifdef HAVE_AIXOSLEVEL_530006 last_idle_donated_purr = (zbx_uint64_t)lparstats.idle_donated_purr; last_busy_donated_purr = (zbx_uint64_t)lparstats.busy_donated_purr; last_idle_stolen_purr = (zbx_uint64_t)lparstats.idle_stolen_purr; last_busy_stolen_purr = (zbx_uint64_t)lparstats.busy_stolen_purr; #endif /* HAVE_AIXOSLEVEL_530006 */ #endif /* _AIXVERSION_530 */ last_user = (zbx_uint64_t)cpustats.user; last_sys = (zbx_uint64_t)cpustats.sys; last_idle = (zbx_uint64_t)cpustats.idle; last_wait = (zbx_uint64_t)cpustats.wait; last_xfers = (zbx_uint64_t)diskstats.xfers; last_wblks = (zbx_uint64_t)diskstats.wblks; last_rblks = (zbx_uint64_t)diskstats.rblks; #endif /* HAVE_LIBPERFSTAT */ } void collect_vmstat_data(ZBX_VMSTAT_DATA *vmstat) { update_vmstat(vmstat); } #endif /* _AIX */