mirror of https://gitee.com/openkylin/sysstat.git
538 lines
13 KiB
C
538 lines
13 KiB
C
/*
|
|
* count.c: Count items for which statistics will be collected.
|
|
* (C) 1999-2022 by Sebastien GODARD (sysstat <at> orange.fr)
|
|
*
|
|
***************************************************************************
|
|
* 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 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-1335 USA *
|
|
***************************************************************************
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/statvfs.h>
|
|
#include <unistd.h>
|
|
|
|
#include "common.h"
|
|
#include "rd_stats.h"
|
|
|
|
#ifdef USE_NLS
|
|
#include <locale.h>
|
|
#include <libintl.h>
|
|
#define _(string) gettext(string)
|
|
#else
|
|
#define _(string) (string)
|
|
#endif
|
|
|
|
|
|
/*
|
|
***************************************************************************
|
|
* Count number of processors in /sys.
|
|
*
|
|
* IN:
|
|
* @highest If set to TRUE, then look for the highest processor number.
|
|
* This is used when eg. the machine has 4 CPU numbered 0, 1, 4
|
|
* and 5. In this case, this procedure will return 6.
|
|
*
|
|
* RETURNS:
|
|
* Number of processors (online and offline).
|
|
* A value of 0 means that /sys was not mounted.
|
|
* A value of N (!=0) means N processor(s) (cpu0 .. cpu(N-1)).
|
|
***************************************************************************
|
|
*/
|
|
int get_sys_cpu_nr(int highest)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *drd;
|
|
struct stat buf;
|
|
char line[MAX_PF_NAME];
|
|
int num_proc, proc_nr = -1;
|
|
|
|
/* Open relevant /sys directory */
|
|
if ((dir = opendir(SYSFS_DEVCPU)) == NULL)
|
|
return 0;
|
|
|
|
/* Get current file entry */
|
|
while ((drd = readdir(dir)) != NULL) {
|
|
|
|
if (!strncmp(drd->d_name, "cpu", 3) && isdigit(drd->d_name[3])) {
|
|
snprintf(line, sizeof(line), "%s/%s", SYSFS_DEVCPU, drd->d_name);
|
|
line[sizeof(line) - 1] = '\0';
|
|
if (stat(line, &buf) < 0)
|
|
continue;
|
|
if (S_ISDIR(buf.st_mode)) {
|
|
if (highest) {
|
|
sscanf(drd->d_name + 3, "%d", &num_proc);
|
|
if (num_proc > proc_nr) {
|
|
proc_nr = num_proc;
|
|
}
|
|
}
|
|
else {
|
|
proc_nr++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Close directory */
|
|
closedir(dir);
|
|
|
|
return (proc_nr + 1);
|
|
}
|
|
|
|
/*
|
|
***************************************************************************
|
|
* Count number of processors in /proc/stat.
|
|
*
|
|
* RETURNS:
|
|
* Number of processors. The returned value is greater than or equal to the
|
|
* number of online processors.
|
|
* A value of 0 means one processor and non SMP kernel.
|
|
* A value of N (!=0) means N processor(s) (0 .. N-1) with SMP kernel.
|
|
***************************************************************************
|
|
*/
|
|
int get_proc_cpu_nr(void)
|
|
{
|
|
FILE *fp;
|
|
char line[16];
|
|
int num_proc, proc_nr = -1;
|
|
|
|
if ((fp = fopen(STAT, "r")) == NULL) {
|
|
fprintf(stderr, _("Cannot open %s: %s\n"), STAT, strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
|
|
|
if (strncmp(line, "cpu ", 4) && !strncmp(line, "cpu", 3)) {
|
|
sscanf(line + 3, "%d", &num_proc);
|
|
if (num_proc > proc_nr) {
|
|
proc_nr = num_proc;
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
proc_nr++;
|
|
return proc_nr;
|
|
}
|
|
|
|
/*
|
|
***************************************************************************
|
|
* Count the number of processors on the machine, or look for the
|
|
* highest processor number.
|
|
* Try to use /sys for that, or /proc/stat if /sys doesn't exist.
|
|
*
|
|
* IN:
|
|
* @max_nr_cpus Maximum number of proc that sysstat can handle.
|
|
* @highest If set to TRUE, then look for the highest processor number.
|
|
* This is used when eg. the machine has 4 CPU numbered 0, 1, 4
|
|
* and 5. In this case, this procedure will return 6.
|
|
*
|
|
* RETURNS:
|
|
* Number of processors.
|
|
* 0: one proc and non SMP kernel.
|
|
* 1: one proc and SMP kernel (NB: On SMP machines where all the CPUs but
|
|
* one have been disabled, we get the total number of proc since we use
|
|
* /sys to count them).
|
|
* 2: two proc...
|
|
*
|
|
* USED BY:
|
|
* sadc, cifsiostat, iostat, mpstat, pidstat, tapestat
|
|
***************************************************************************
|
|
*/
|
|
__nr_t get_cpu_nr(unsigned int max_nr_cpus, int highest)
|
|
{
|
|
__nr_t cpu_nr;
|
|
|
|
if ((cpu_nr = get_sys_cpu_nr(highest)) == 0) {
|
|
/* /sys may be not mounted. Use /proc/stat instead */
|
|
cpu_nr = get_proc_cpu_nr();
|
|
}
|
|
|
|
if (cpu_nr > max_nr_cpus) {
|
|
fprintf(stderr, _("Cannot handle so many processors!\n"));
|
|
exit(1);
|
|
}
|
|
|
|
return cpu_nr;
|
|
}
|
|
|
|
/*
|
|
***************************************************************************
|
|
* Find number of interrupts available per processor (use
|
|
* /proc/interrupts file or /proc/softirqs).
|
|
*
|
|
* IN:
|
|
* @file /proc file to read (interrupts or softirqs).
|
|
* @max_nr_irqcpu Maximum number of interrupts per processor that
|
|
* sadc can handle.
|
|
* @cpu_nr Number of processors.
|
|
*
|
|
* RETURNS:
|
|
* Number of interrupts per processor.
|
|
*
|
|
* USED BY:
|
|
* sadc, mpstat
|
|
***************************************************************************
|
|
*/
|
|
__nr_t get_irqcpu_nr(char *file, int max_nr_irqcpu, int cpu_nr)
|
|
{
|
|
FILE *fp;
|
|
char *line = NULL;
|
|
__nr_t irq = 0;
|
|
int p;
|
|
|
|
if ((fp = fopen(file, "r")) == NULL)
|
|
return 0; /* No interrupts file */
|
|
|
|
SREALLOC(line, char, INTERRUPTS_LINE + 11 * cpu_nr);
|
|
|
|
while ((fgets(line, INTERRUPTS_LINE + 11 * cpu_nr , fp) != NULL) &&
|
|
(irq < max_nr_irqcpu)) {
|
|
|
|
p = strcspn(line, ":");
|
|
if ((p > 0) && (p < 16)) {
|
|
irq++;
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
free(line);
|
|
|
|
return irq;
|
|
}
|
|
|
|
#ifdef SOURCE_SADC
|
|
/*---------------- BEGIN: FUNCTIONS USED BY SADC ONLY ---------------------*/
|
|
|
|
/*
|
|
***************************************************************************
|
|
* Find number of devices and partitions available in /proc/diskstats.
|
|
*
|
|
* IN:
|
|
* @count_part Set to TRUE if devices _and_ partitions are to be
|
|
* counted.
|
|
* @only_used_dev When counting devices, set to TRUE if only devices
|
|
* with non zero stats must be counted.
|
|
*
|
|
* RETURNS:
|
|
* Number of devices (and partitions).
|
|
***************************************************************************
|
|
*/
|
|
__nr_t get_diskstats_dev_nr(int count_part, int only_used_dev)
|
|
{
|
|
FILE *fp;
|
|
char line[256];
|
|
char dev_name[MAX_NAME_LEN];
|
|
__nr_t dev = 0;
|
|
int i;
|
|
unsigned long rd_ios, wr_ios;
|
|
|
|
if ((fp = fopen(DISKSTATS, "r")) == NULL)
|
|
/* File non-existent */
|
|
return 0;
|
|
|
|
/*
|
|
* Counting devices and partitions is simply a matter of counting
|
|
* the number of lines...
|
|
*/
|
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
|
|
|
if (!count_part) {
|
|
i = sscanf(line, "%*d %*d %s %lu %*u %*u %*u %lu",
|
|
dev_name, &rd_ios, &wr_ios);
|
|
if ((i == 2) || !is_device(SLASH_SYS, dev_name, ACCEPT_VIRTUAL_DEVICES))
|
|
/* It was a partition and not a device */
|
|
continue;
|
|
if (only_used_dev && !rd_ios && !wr_ios)
|
|
/* Unused device */
|
|
continue;
|
|
}
|
|
dev++;
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
return dev;
|
|
}
|
|
|
|
/*
|
|
***************************************************************************
|
|
* Find number of serial lines that support tx/rx accounting
|
|
* in /proc/tty/driver/serial file.
|
|
*
|
|
* RETURNS:
|
|
* Number of serial lines supporting tx/rx accouting.
|
|
***************************************************************************
|
|
*/
|
|
__nr_t get_serial_nr(void)
|
|
{
|
|
FILE *fp;
|
|
char line[256];
|
|
__nr_t sl = 0;
|
|
|
|
if ((fp = fopen(SERIAL, "r")) == NULL)
|
|
return 0; /* No SERIAL file */
|
|
|
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
|
/*
|
|
* tx/rx statistics are always present,
|
|
* except when serial line is unknown.
|
|
*/
|
|
if (strstr(line, "tx:") != NULL) {
|
|
sl++;
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
return sl;
|
|
}
|
|
|
|
/*
|
|
***************************************************************************
|
|
* Find number of interfaces (network devices) that are in /proc/net/dev
|
|
* file.
|
|
*
|
|
* RETURNS:
|
|
* Number of network interfaces.
|
|
***************************************************************************
|
|
*/
|
|
__nr_t get_iface_nr(void)
|
|
{
|
|
FILE *fp;
|
|
char line[128];
|
|
__nr_t iface = 0;
|
|
|
|
if ((fp = fopen(NET_DEV, "r")) == NULL)
|
|
return 0; /* No network device file */
|
|
|
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
|
if (strchr(line, ':')) {
|
|
iface++;
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
return iface;
|
|
}
|
|
|
|
/*
|
|
***************************************************************************
|
|
* Get number of devices in /proc/diskstats.
|
|
*
|
|
* IN:
|
|
* @f Non zero (true) if disks *and* partitions should be counted, and
|
|
* zero (false) if only disks must be counted.
|
|
*
|
|
* RETURNS:
|
|
* Number of devices.
|
|
***************************************************************************
|
|
*/
|
|
__nr_t get_disk_nr(unsigned int f)
|
|
{
|
|
__nr_t disk_nr;
|
|
|
|
/*
|
|
* Partitions are taken into account by sar -d only with
|
|
* kernels 2.6.25 and later.
|
|
*/
|
|
disk_nr = get_diskstats_dev_nr(f, CNT_USED_DEV);
|
|
|
|
return disk_nr;
|
|
}
|
|
|
|
/*
|
|
***************************************************************************
|
|
* Count number of possible frequencies for CPU#0.
|
|
*
|
|
* RETURNS:
|
|
* Number of frequencies.
|
|
***************************************************************************
|
|
*/
|
|
__nr_t get_freq_nr(void)
|
|
{
|
|
FILE *fp;
|
|
char filename[MAX_PF_NAME];
|
|
char line[128];
|
|
__nr_t freq = 0;
|
|
|
|
snprintf(filename, MAX_PF_NAME, "%s/cpu0/%s",
|
|
SYSFS_DEVCPU, SYSFS_TIME_IN_STATE);
|
|
if ((fp = fopen(filename, "r")) == NULL)
|
|
return 0; /* No time_in_state file for CPU#0 */
|
|
|
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
|
freq++;
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
return freq;
|
|
}
|
|
|
|
/*
|
|
***************************************************************************
|
|
* Count number of USB devices in /sys/bus/usb/devices.
|
|
*
|
|
* RETURNS:
|
|
* Number of USB devices plugged into the system.
|
|
* Don't count USB root hubs.
|
|
* Return -1 if directory doesn't exist in sysfs.
|
|
***************************************************************************
|
|
*/
|
|
__nr_t get_usb_nr(void)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *drd;
|
|
__nr_t usb = 0;
|
|
|
|
/* Open relevant /sys directory */
|
|
if ((dir = opendir(SYSFS_USBDEV)) == NULL)
|
|
return -1;
|
|
|
|
/* Get current file entry */
|
|
while ((drd = readdir(dir)) != NULL) {
|
|
|
|
if (isdigit(drd->d_name[0]) && !strchr(drd->d_name, ':')) {
|
|
usb++;
|
|
}
|
|
}
|
|
|
|
/* Close directory */
|
|
closedir(dir);
|
|
|
|
return usb;
|
|
}
|
|
|
|
/*
|
|
***************************************************************************
|
|
* Find number of filesystems in /etc/mtab. Pseudo-filesystems are ignored.
|
|
*
|
|
* RETURNS:
|
|
* Number of filesystems.
|
|
***************************************************************************
|
|
*/
|
|
__nr_t get_filesystem_nr(void)
|
|
{
|
|
FILE *fp;
|
|
char line[512], fs_name[MAX_FS_LEN], mountp[256], type[128];
|
|
char *pos = 0, *pos2 = 0;
|
|
__nr_t fs = 0;
|
|
int skip = 0, skip_next = 0;
|
|
struct statvfs buf;
|
|
|
|
if ((fp = fopen(MTAB, "r")) == NULL)
|
|
/* File non-existent */
|
|
return 0;
|
|
|
|
/* Get current filesystem */
|
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
|
/*
|
|
* Ignore line if the preceding line did not contain '\n'.
|
|
* (Some very long lines may be found for instance when
|
|
* overlay2 filesystem with docker is used).
|
|
*/
|
|
skip = skip_next;
|
|
skip_next = (strchr(line, '\n') == NULL);
|
|
if (skip)
|
|
continue;
|
|
|
|
if (line[0] == '/') {
|
|
/* Find field separator position */
|
|
pos = strchr(line, ' ');
|
|
if (pos == NULL)
|
|
continue;
|
|
|
|
/*
|
|
* Find second field separator position,
|
|
* read filesystem type,
|
|
* if filesystem type is autofs, skip it
|
|
*/
|
|
pos2 = strchr(pos + 1, ' ');
|
|
if (pos2 == NULL)
|
|
continue;
|
|
|
|
sscanf(pos2 + 1, "%127s", type);
|
|
if (strcmp(type, "autofs") == 0)
|
|
continue;
|
|
|
|
/* Read filesystem name and mount point */
|
|
sscanf(line, "%127s", fs_name);
|
|
sscanf(pos + 1, "%255s", mountp);
|
|
|
|
/* Replace octal codes */
|
|
oct2chr(mountp);
|
|
|
|
/* Check that total size is not zero */
|
|
if (__statvfs(mountp, &buf) < 0)
|
|
continue;
|
|
|
|
if (buf.f_blocks) {
|
|
fs++;
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
return fs;
|
|
}
|
|
|
|
/*
|
|
***************************************************************************
|
|
* Find number of fibre channel hosts in /sys/class/fc_host/.
|
|
*
|
|
* RETURNS:
|
|
* Number of FC hosts.
|
|
* Return -1 if directory doesn't exist in sysfs.
|
|
***************************************************************************
|
|
*/
|
|
__nr_t get_fchost_nr(void)
|
|
{
|
|
DIR *dir;
|
|
struct dirent *drd;
|
|
__nr_t fc = 0;
|
|
|
|
if ((dir = opendir(SYSFS_FCHOST)) == NULL) {
|
|
/* Directory non-existent */
|
|
return -1;
|
|
}
|
|
|
|
while ((drd = readdir(dir)) != NULL) {
|
|
|
|
if (!strncmp(drd->d_name, "host", 4)) {
|
|
fc++;
|
|
}
|
|
}
|
|
|
|
/* Close directory */
|
|
closedir(dir);
|
|
|
|
return fc;
|
|
}
|
|
|
|
/*------------------ END: FUNCTIONS USED BY SADC ONLY ---------------------*/
|
|
#endif /* SOURCE_SADC */
|