/*
* rd_stats.c: Read system statistics
- * (C) 1999-2017 by Sebastien GODARD (sysstat <at> orange.fr)
+ * (C) 1999-2019 by Sebastien GODARD (sysstat <at> orange.fr)
*
***************************************************************************
* This program is free software; you can redistribute it and/or modify it *
#include "common.h"
#include "rd_stats.h"
-#include "ioconf.h"
#ifdef USE_NLS
#include <locale.h>
i = strcspn(line + pos + 1, " ");
pos += i + 1;
}
- while ((i > 0) && (pos < sizeof(line)));
+ while ((i > 0) && (pos < (sizeof(line) - 1)));
break;
}
if (!strncmp(line, "MemTotal:", 9)) {
/* Read the total amount of memory in kB */
- sscanf(line + 9, "%lu", &st_memory->tlmkb);
+ sscanf(line + 9, "%llu", &st_memory->tlmkb);
}
else if (!strncmp(line, "MemFree:", 8)) {
/* Read the amount of free memory in kB */
- sscanf(line + 8, "%lu", &st_memory->frmkb);
+ sscanf(line + 8, "%llu", &st_memory->frmkb);
}
else if (!strncmp(line, "MemAvailable:", 13)) {
/* Read the amount of available memory in kB */
- sscanf(line + 13, "%lu", &st_memory->availablekb);
+ sscanf(line + 13, "%llu", &st_memory->availablekb);
}
else if (!strncmp(line, "Buffers:", 8)) {
/* Read the amount of buffered memory in kB */
- sscanf(line + 8, "%lu", &st_memory->bufkb);
+ sscanf(line + 8, "%llu", &st_memory->bufkb);
}
else if (!strncmp(line, "Cached:", 7)) {
/* Read the amount of cached memory in kB */
- sscanf(line + 7, "%lu", &st_memory->camkb);
+ sscanf(line + 7, "%llu", &st_memory->camkb);
}
else if (!strncmp(line, "SwapCached:", 11)) {
/* Read the amount of cached swap in kB */
- sscanf(line + 11, "%lu", &st_memory->caskb);
+ sscanf(line + 11, "%llu", &st_memory->caskb);
}
else if (!strncmp(line, "Active:", 7)) {
/* Read the amount of active memory in kB */
- sscanf(line + 7, "%lu", &st_memory->activekb);
+ sscanf(line + 7, "%llu", &st_memory->activekb);
}
else if (!strncmp(line, "Inactive:", 9)) {
/* Read the amount of inactive memory in kB */
- sscanf(line + 9, "%lu", &st_memory->inactkb);
+ sscanf(line + 9, "%llu", &st_memory->inactkb);
}
else if (!strncmp(line, "SwapTotal:", 10)) {
/* Read the total amount of swap memory in kB */
- sscanf(line + 10, "%lu", &st_memory->tlskb);
+ sscanf(line + 10, "%llu", &st_memory->tlskb);
}
else if (!strncmp(line, "SwapFree:", 9)) {
/* Read the amount of free swap memory in kB */
- sscanf(line + 9, "%lu", &st_memory->frskb);
+ sscanf(line + 9, "%llu", &st_memory->frskb);
}
else if (!strncmp(line, "Dirty:", 6)) {
/* Read the amount of dirty memory in kB */
- sscanf(line + 6, "%lu", &st_memory->dirtykb);
+ sscanf(line + 6, "%llu", &st_memory->dirtykb);
}
else if (!strncmp(line, "Committed_AS:", 13)) {
/* Read the amount of commited memory in kB */
- sscanf(line + 13, "%lu", &st_memory->comkb);
+ sscanf(line + 13, "%llu", &st_memory->comkb);
}
else if (!strncmp(line, "AnonPages:", 10)) {
/* Read the amount of pages mapped into userspace page tables in kB */
- sscanf(line + 10, "%lu", &st_memory->anonpgkb);
+ sscanf(line + 10, "%llu", &st_memory->anonpgkb);
}
else if (!strncmp(line, "Slab:", 5)) {
/* Read the amount of in-kernel data structures cache in kB */
- sscanf(line + 5, "%lu", &st_memory->slabkb);
+ sscanf(line + 5, "%llu", &st_memory->slabkb);
}
else if (!strncmp(line, "KernelStack:", 12)) {
/* Read the kernel stack utilization in kB */
- sscanf(line + 12, "%lu", &st_memory->kstackkb);
+ sscanf(line + 12, "%llu", &st_memory->kstackkb);
}
else if (!strncmp(line, "PageTables:", 11)) {
/* Read the amount of memory dedicated to the lowest level of page tables in kB */
- sscanf(line + 11, "%lu", &st_memory->pgtblkb);
+ sscanf(line + 11, "%llu", &st_memory->pgtblkb);
}
else if (!strncmp(line, "VmallocUsed:", 12)) {
/* Read the amount of vmalloc area which is used in kB */
- sscanf(line + 12, "%lu", &st_memory->vmusedkb);
+ sscanf(line + 12, "%llu", &st_memory->vmusedkb);
}
}
}
}
+/*
+ ***************************************************************************
+ * Compute "extended" device statistics (service time, etc.).
+ *
+ * IN:
+ * @sdc Structure with current device statistics.
+ * @sdp Structure with previous device statistics.
+ * @itv Interval of time in 1/100th of a second.
+ *
+ * OUT:
+ * @xds Structure with extended statistics.
+ ***************************************************************************
+*/
+void compute_ext_disk_stats(struct stats_disk *sdc, struct stats_disk *sdp,
+ unsigned long long itv, struct ext_disk_stats *xds)
+{
+ xds->util = S_VALUE(sdp->tot_ticks, sdc->tot_ticks, itv);
+ /*
+ * Kernel gives ticks already in milliseconds for all platforms
+ * => no need for further scaling.
+ */
+ xds->await = (sdc->nr_ios - sdp->nr_ios) ?
+ ((sdc->rd_ticks - sdp->rd_ticks) + (sdc->wr_ticks - sdp->wr_ticks) + (sdc->dc_ticks - sdp->dc_ticks)) /
+ ((double) (sdc->nr_ios - sdp->nr_ios)) : 0.0;
+ xds->arqsz = (sdc->nr_ios - sdp->nr_ios) ?
+ ((sdc->rd_sect - sdp->rd_sect) + (sdc->wr_sect - sdp->wr_sect) + (sdc->dc_sect - sdp->dc_sect)) /
+ ((double) (sdc->nr_ios - sdp->nr_ios)) : 0.0;
+}
+
+/*
+ ***************************************************************************
+ * Since ticks may vary slightly from CPU to CPU, we'll want
+ * to recalculate itv based on this CPU's tick count, rather
+ * than that reported by the "cpu" line. Otherwise we
+ * occasionally end up with slightly skewed figures, with
+ * the skew being greater as the time interval grows shorter.
+ *
+ * IN:
+ * @scc Current sample statistics for current CPU.
+ * @scp Previous sample statistics for current CPU.
+ *
+ * RETURNS:
+ * Interval of time based on current CPU, expressed in jiffies.
+ ***************************************************************************
+ */
+unsigned long long get_per_cpu_interval(struct stats_cpu *scc,
+ struct stats_cpu *scp)
+{
+ unsigned long long ishift = 0LL;
+
+ if ((scc->cpu_user - scc->cpu_guest) < (scp->cpu_user - scp->cpu_guest)) {
+ /*
+ * Sometimes the nr of jiffies spent in guest mode given by the guest
+ * counter in /proc/stat is slightly higher than that included in
+ * the user counter. Update the interval value accordingly.
+ */
+ ishift += (scp->cpu_user - scp->cpu_guest) -
+ (scc->cpu_user - scc->cpu_guest);
+ }
+ if ((scc->cpu_nice - scc->cpu_guest_nice) < (scp->cpu_nice - scp->cpu_guest_nice)) {
+ /*
+ * Idem for nr of jiffies spent in guest_nice mode.
+ */
+ ishift += (scp->cpu_nice - scp->cpu_guest_nice) -
+ (scc->cpu_nice - scc->cpu_guest_nice);
+ }
+
+ /*
+ * Workaround for CPU coming back online: With recent kernels
+ * some fields (user, nice, system) restart from their previous value,
+ * whereas others (idle, iowait) restart from zero.
+ * For the latter we need to set their previous value to zero to
+ * avoid getting an interval value < 0.
+ * (I don't know how the other fields like hardirq, steal... behave).
+ * Don't assume the CPU has come back from offline state if previous
+ * value was greater than ULLONG_MAX - 0x7ffff (the counter probably
+ * overflew).
+ */
+ if ((scc->cpu_idle < scp->cpu_idle) && (scp->cpu_idle < (ULLONG_MAX - 0x7ffff))) {
+ scp->cpu_idle = 0;
+ }
+ if ((scc->cpu_iowait < scp->cpu_iowait) && (scp->cpu_iowait < (ULLONG_MAX - 0x7ffff))) {
+ scp->cpu_iowait = 0;
+ }
+
+ /*
+ * Don't take cpu_guest and cpu_guest_nice into account
+ * because cpu_user and cpu_nice already include them.
+ */
+ return ((scc->cpu_user + scc->cpu_nice +
+ scc->cpu_sys + scc->cpu_iowait +
+ scc->cpu_idle + scc->cpu_steal +
+ scc->cpu_hardirq + scc->cpu_softirq) -
+ (scp->cpu_user + scp->cpu_nice +
+ scp->cpu_sys + scp->cpu_iowait +
+ scp->cpu_idle + scp->cpu_steal +
+ scp->cpu_hardirq + scp->cpu_softirq) +
+ ishift);
+}
+
#ifdef SOURCE_SADC
/*---------------- BEGIN: FUNCTIONS USED BY SADC ONLY ---------------------*/
{
FILE *fp;
char line[8192];
- int load_tmp[3];
+ unsigned int load_tmp[3];
int rc;
if ((fp = fopen(LOADAVG, "r")) == NULL)
return 0;
/* Read load averages and queue length */
- rc = fscanf(fp, "%d.%u %d.%u %d.%u %lu/%u %*d\n",
+ rc = fscanf(fp, "%u.%u %u.%u %u.%u %llu/%llu %*d\n",
&load_tmp[0], &st_queue->load_avg_1,
&load_tmp[1], &st_queue->load_avg_5,
&load_tmp[2], &st_queue->load_avg_15,
if (!strncmp(line, "procs_blocked ", 14)) {
/* Read number of processes blocked */
- sscanf(line + 14, "%lu", &st_queue->procs_blocked);
+ sscanf(line + 14, "%llu", &st_queue->procs_blocked);
break;
}
}
char line[1024];
char dev_name[MAX_NAME_LEN];
unsigned int major, minor;
- unsigned long rd_ios, wr_ios, rd_sec, wr_sec;
+ unsigned long rd_ios, wr_ios, dc_ios;
+ unsigned long rd_sec, wr_sec, dc_sec;
if ((fp = fopen(DISKSTATS, "r")) == NULL)
return 0;
while (fgets(line, sizeof(line), fp) != NULL) {
- if (sscanf(line, "%u %u %s %lu %*u %lu %*u %lu %*u %lu",
+ /* Discard I/O stats may be not available */
+ dc_ios = dc_sec = 0;
+
+ if (sscanf(line,
+ "%u %u %s "
+ "%lu %*u %lu %*u "
+ "%lu %*u %lu %*u "
+ "%*u %*u %*u "
+ "%lu %*u %lu",
&major, &minor, dev_name,
- &rd_ios, &rd_sec, &wr_ios, &wr_sec) == 7) {
+ &rd_ios, &rd_sec,
+ &wr_ios, &wr_sec,
+ &dc_ios, &dc_sec) >= 7) {
if (is_device(dev_name, IGNORE_VIRTUAL_DEVICES)) {
/*
* OK: It's a (real) device and not a partition.
* Note: Structure should have been initialized first!
*/
- st_io->dk_drive += (unsigned long long) rd_ios + (unsigned long long) wr_ios;
+ st_io->dk_drive += (unsigned long long) rd_ios +
+ (unsigned long long) wr_ios +
+ (unsigned long long) dc_ios;
st_io->dk_drive_rio += rd_ios;
st_io->dk_drive_rblk += rd_sec;
st_io->dk_drive_wio += wr_ios;
st_io->dk_drive_wblk += wr_sec;
+ st_io->dk_drive_dio += dc_ios;
+ st_io->dk_drive_dblk += dc_sec;
}
}
}
char line[1024];
char dev_name[MAX_NAME_LEN];
struct stats_disk *st_disk_i;
- unsigned int major, minor, rd_ticks, wr_ticks, tot_ticks, rq_ticks;
- unsigned long rd_ios, wr_ios, rd_sec, wr_sec;
+ unsigned int major, minor, rd_ticks, wr_ticks, dc_ticks, tot_ticks, rq_ticks, part_nr;
+ unsigned long rd_ios, wr_ios, dc_ios, rd_sec, wr_sec, dc_sec;
+ unsigned long long wwn[2];
__nr_t dsk_read = 0;
if ((fp = fopen(DISKSTATS, "r")) == NULL)
while (fgets(line, sizeof(line), fp) != NULL) {
- if (sscanf(line, "%u %u %s %lu %*u %lu %u %lu %*u %lu"
- " %u %*u %u %u",
+ /* Discard I/O stats may be not available */
+ dc_ios = dc_sec = dc_ticks = 0;
+
+ if (sscanf(line,
+ "%u %u %s "
+ "%lu %*u %lu %u "
+ "%lu %*u %lu %u "
+ "%*u %u %u "
+ "%lu %*u %lu %u",
&major, &minor, dev_name,
- &rd_ios, &rd_sec, &rd_ticks, &wr_ios, &wr_sec, &wr_ticks,
- &tot_ticks, &rq_ticks) == 11) {
+ &rd_ios, &rd_sec, &rd_ticks,
+ &wr_ios, &wr_sec, &wr_ticks,
+ &tot_ticks, &rq_ticks,
+ &dc_ios, &dc_sec, &dc_ticks) >= 11) {
- if (!rd_ios && !wr_ios)
+ if (!rd_ios && !wr_ios && !dc_ios)
/* Unused device: Ignore it */
continue;
if (read_part || is_device(dev_name, ACCEPT_VIRTUAL_DEVICES)) {
st_disk_i = st_disk + dsk_read++;
st_disk_i->major = major;
st_disk_i->minor = minor;
- st_disk_i->nr_ios = (unsigned long long) rd_ios + (unsigned long long) wr_ios;
+ st_disk_i->nr_ios = (unsigned long long) rd_ios +
+ (unsigned long long) wr_ios +
+ (unsigned long long) dc_ios;
st_disk_i->rd_sect = rd_sec;
st_disk_i->wr_sect = wr_sec;
+ st_disk_i->dc_sect = dc_sec;
st_disk_i->rd_ticks = rd_ticks;
st_disk_i->wr_ticks = wr_ticks;
+ st_disk_i->dc_ticks = dc_ticks;
st_disk_i->tot_ticks = tot_ticks;
st_disk_i->rq_ticks = rq_ticks;
+
+ if (get_wwnid_from_pretty(dev_name, wwn, &part_nr) < 0) {
+ st_disk_i->wwn[0] = 0ULL;
+ }
+ else {
+ st_disk_i->wwn[0] = wwn[0];
+ st_disk_i->wwn[1] = wwn[1];
+ st_disk_i->part_nr = part_nr;
+ }
}
}
}
__nr_t read_kernel_tables(struct stats_ktables *st_ktables)
{
FILE *fp;
- unsigned int parm;
+ unsigned long long parm;
int rc = 0;
/* Open /proc/sys/fs/dentry-state file */
if ((fp = fopen(FDENTRY_STATE, "r")) != NULL) {
- rc = fscanf(fp, "%*d %u",
+ rc = fscanf(fp, "%*d %llu",
&st_ktables->dentry_stat);
fclose(fp);
if (rc == 0) {
/* Open /proc/sys/fs/file-nr file */
if ((fp = fopen(FFILE_NR, "r")) != NULL) {
- rc = fscanf(fp, "%u %u",
+ rc = fscanf(fp, "%llu %llu",
&st_ktables->file_used, &parm);
fclose(fp);
/*
/* Open /proc/sys/fs/inode-state file */
if ((fp = fopen(FINODE_STATE, "r")) != NULL) {
- rc = fscanf(fp, "%u %u",
+ rc = fscanf(fp, "%llu %llu",
&st_ktables->inode_used, &parm);
fclose(fp);
/*
/* Open /proc/sys/kernel/pty/nr file */
if ((fp = fopen(PTY_NR, "r")) != NULL) {
- rc = fscanf(fp, "%u",
+ rc = fscanf(fp, "%llu",
&st_ktables->pty_nr);
fclose(fp);
if (rc == 0) {
{
FILE *fp;
char line[1024];
+ static char format[256] = "";
int sw = FALSE;
if ((fp = fopen(NET_SNMP, "r")) == NULL)
if (!strncmp(line, "Icmp:", 5)) {
if (sw) {
- sscanf(line + 5, "%*u %lu %lu %lu %lu %lu %lu %*u %*u "
- "%*u %*u %*u %*u %*u %lu %lu %lu %lu %lu %lu",
+ sscanf(line + 5, format,
&st_net_eicmp->InErrors,
&st_net_eicmp->InDestUnreachs,
&st_net_eicmp->InTimeExcds,
break;
}
else {
+ if (!strlen(format)) {
+ if (strstr(line, "InCsumErrors")) {
+ /*
+ * New format: InCsumErrors field exists at position #3.
+ * Capture: 2,4,5,6,7,8,16,17,18,19,20,21
+ */
+ strcpy(format, "%*u %lu %*u %lu %lu %lu %lu %lu %*u %*u "
+ "%*u %*u %*u %*u %*u %lu %lu %lu %lu %lu %lu");
+ }
+ else {
+ /*
+ * Old format: InCsumErrors field doesn't exist.
+ * Capture: 2,3,4,5,6,7,15,16,17,18,19,20
+ */
+ strcpy(format, "%*u %lu %lu %lu %lu %lu %lu %*u %*u "
+ "%*u %*u %*u %*u %*u %lu %lu %lu %lu %lu %lu");
+
+ }
+ }
sw = TRUE;
}
}
if (!strncmp(line, "HugePages_Total:", 16)) {
/* Read the total number of huge pages */
- sscanf(line + 16, "%lu", &st_huge->tlhkb);
+ sscanf(line + 16, "%llu", &st_huge->tlhkb);
}
else if (!strncmp(line, "HugePages_Free:", 15)) {
/* Read the number of free huge pages */
- sscanf(line + 15, "%lu", &st_huge->frhkb);
+ sscanf(line + 15, "%llu", &st_huge->frhkb);
+ }
+ else if (!strncmp(line, "HugePages_Rsvd:", 15)) {
+ /* Read the number of reserved huge pages */
+ sscanf(line + 15, "%llu", &st_huge->rsvdhkb);
+ }
+ else if (!strncmp(line, "HugePages_Surp:", 15)) {
+ /* Read the number of surplus huge pages */
+ sscanf(line + 15, "%llu", &st_huge->surphkb);
}
else if (!strncmp(line, "Hugepagesize:", 13)) {
/* Read the default size of a huge page in kB */
/* We want huge pages stats in kB and not expressed in a number of pages */
st_huge->tlhkb *= szhkb;
st_huge->frhkb *= szhkb;
+ st_huge->rsvdhkb *= szhkb;
+ st_huge->surphkb *= szhkb;
+
return 1;
}
__nr_t read_filesystem(struct stats_filesystem *st_filesystem, __nr_t nr_alloc)
{
FILE *fp;
- char line[512], fs_name[128], mountp[256];
+ char line[512], fs_name[MAX_FS_LEN], mountp[256], type[128];
int skip = 0, skip_next = 0;
- char *pos = 0;
+ char *pos = 0, *pos2 = 0;
__nr_t fs_read = 0;
struct stats_filesystem *st_filesystem_i;
struct statvfs buf;
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 current filesystem name */
sscanf(line, "%127s", fs_name);
/*
* It's important to have read the whole mount point name
* for statvfs() to work properly (see above).
*/
- if ((statvfs(mountp, &buf) < 0) || (!buf.f_blocks))
+ if ((__statvfs(mountp, &buf) < 0) || (!buf.f_blocks))
continue;
if (fs_read + 1 > nr_alloc) {
unsigned long rx_frames, tx_frames, rx_words, tx_words;
/* Each host, if present, will have its own hostX entry within SYSFS_FCHOST */
- if ((dir = opendir(SYSFS_FCHOST)) == NULL)
+ if ((dir = __opendir(SYSFS_FCHOST)) == NULL)
return 0; /* No FC hosts */
/*
* Read each of the counters via sysfs, where they are
* returned as hex values (e.g. 0x72400).
*/
- while ((drd = readdir(dir)) != NULL) {
+ while ((drd = __readdir(dir)) != NULL) {
rx_frames = tx_frames = rx_words = tx_words = 0;
if (!strncmp(drd->d_name, "host", 4)) {
st_fc_i->f_txframes = tx_frames;
st_fc_i->f_rxwords = rx_words;
st_fc_i->f_txwords = tx_words;
- strncpy(st_fc_i->fchost_name, drd->d_name, MAX_FCH_LEN);
+ memcpy(st_fc_i->fchost_name, drd->d_name, MAX_FCH_LEN);
st_fc_i->fchost_name[MAX_FCH_LEN - 1] = '\0';
}
}
- closedir(dir);
+ __closedir(dir);
return fch_read;
}
* IN:
* @st_softnet Structure where stats will be saved.
* @nr_alloc Total number of structures allocated. Value is >= 0.
+ * @online_cpu_bitmap
+ * Bitmap listing online CPU.
*
* OUT:
* @st_softnet Structure with statistics.
*
* RETURNS:
- * Number of CPU for which statistics have been read.
- * 1 means CPU "all", 2 means CPU 0, 3 means CPU 1, etc.
- * Or -1 if the buffer was too small and needs to be reallocated.
+ * 1 if stats have been sucessfully read, or 0 otherwise.
***************************************************************************
*/
-__nr_t read_softnet(struct stats_softnet *st_softnet, __nr_t nr_alloc)
+int read_softnet(struct stats_softnet *st_softnet, __nr_t nr_alloc,
+ unsigned char online_cpu_bitmap[])
{
FILE *fp;
struct stats_softnet *st_softnet_i;
char line[1024];
- __nr_t cpu_read = 1; /* For CPU "all" */
+ int cpu;
/* Open /proc/net/softnet_stat file */
if ((fp = fopen(NET_SOFTNET, "r")) == NULL)
return 0;
- /*
- * Init a structure that will contain the values for CPU "all".
- * CPU "all" doesn't exist in /proc/net/softnet_stat file, so
- * we compute its values as the sum of the values of each CPU.
- */
- memset(st_softnet, 0, sizeof(struct stats_softnet));
-
- while (fgets(line, sizeof(line), fp) != NULL) {
+ for (cpu = 1; cpu < nr_alloc; cpu++) {
+ if (!(online_cpu_bitmap[(cpu - 1) >> 3] & (1 << ((cpu - 1) & 0x07))))
+ /* CPU is offline */
+ continue;
- if (cpu_read + 1 > nr_alloc) {
- cpu_read = -1;
+ if (fgets(line, sizeof(line), fp) == NULL)
break;
- }
- st_softnet_i = st_softnet + cpu_read++;
+ st_softnet_i = st_softnet + cpu;
sscanf(line, "%x %x %x %*x %*x %*x %*x %*x %*x %x %x",
&st_softnet_i->processed,
&st_softnet_i->dropped,
&st_softnet_i->time_squeeze,
&st_softnet_i->received_rps,
&st_softnet_i->flow_limit);
-
- st_softnet->processed += st_softnet_i->processed;
- st_softnet->dropped += st_softnet_i->dropped;
- st_softnet->time_squeeze += st_softnet_i->time_squeeze;
- st_softnet->received_rps += st_softnet_i->received_rps;
- st_softnet->flow_limit += st_softnet_i->flow_limit;
}
fclose(fp);
- return cpu_read;
+ return 1;
}
/*------------------ END: FUNCTIONS USED BY SADC ONLY ---------------------*/