/* * rd_stats.c: Read system statistics * (C) 1999-2022 by Sebastien GODARD (sysstat 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 #include #include #include #include #include #include #include #include #include #include "common.h" #include "rd_stats.h" #ifdef USE_NLS #include #include #define _(string) gettext(string) #else #define _(string) (string) #endif /* Generic PSI structure */ struct stats_psi { unsigned long long total; unsigned long avg10; unsigned long avg60; unsigned long avg300; }; /* *************************************************************************** * Read CPU statistics. * Remember that this function is used by several sysstat commands! * * IN: * @st_cpu Buffer where structures containing stats will be saved. * @nr_alloc Total number of structures allocated. Value is >= 1. * * OUT: * @st_cpu Buffer with statistics. * * RETURNS: * Highest CPU number(*) 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. * * (*)This doesn't account for all processors in the machine in the case * where some CPU are offline and located at the end of the list. * * USED BY: * sadc, iostat, mpstat, pidstat *************************************************************************** */ __nr_t read_stat_cpu(struct stats_cpu *st_cpu, __nr_t nr_alloc) { FILE *fp; struct stats_cpu *st_cpu_i; struct stats_cpu sc; char line[8192]; int proc_nr; __nr_t cpu_read = 0; if ((fp = fopen(STAT, "r")) == NULL) { fprintf(stderr, _("Cannot open %s: %s\n"), STAT, strerror(errno)); exit(2); } while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "cpu ", 4)) { /* * All the fields don't necessarily exist, * depending on the kernel version used. */ memset(st_cpu, 0, STATS_CPU_SIZE); /* * Read the number of jiffies spent in the different modes * (user, nice, etc.) among all proc. CPU usage is not reduced * to one processor to avoid rounding problems. */ sscanf(line + 5, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", &st_cpu->cpu_user, &st_cpu->cpu_nice, &st_cpu->cpu_sys, &st_cpu->cpu_idle, &st_cpu->cpu_iowait, &st_cpu->cpu_hardirq, &st_cpu->cpu_softirq, &st_cpu->cpu_steal, &st_cpu->cpu_guest, &st_cpu->cpu_guest_nice); if (!cpu_read) { cpu_read = 1; } if (nr_alloc == 1) /* We just want to read stats for CPU "all" */ break; } else if (!strncmp(line, "cpu", 3)) { /* All the fields don't necessarily exist */ memset(&sc, 0, STATS_CPU_SIZE); /* * Read the number of jiffies spent in the different modes * (user, nice, etc) for current proc. * This is done only on SMP machines. */ sscanf(line + 3, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", &proc_nr, &sc.cpu_user, &sc.cpu_nice, &sc.cpu_sys, &sc.cpu_idle, &sc.cpu_iowait, &sc.cpu_hardirq, &sc.cpu_softirq, &sc.cpu_steal, &sc.cpu_guest, &sc.cpu_guest_nice); if (proc_nr + 2 > nr_alloc) { cpu_read = -1; break; } st_cpu_i = st_cpu + proc_nr + 1; *st_cpu_i = sc; if (proc_nr + 2 > cpu_read) { cpu_read = proc_nr + 2; } } } fclose(fp); return cpu_read; } /* *************************************************************************** * Read interrupts statistics from /proc/interrupts. * * IN: * @st_irq Structure where stats will be saved. * @nr_alloc Number of CPU structures allocated. Value is >= 1. * @nr_int Number of interrupts, including sum. value is >= 1. * * OUT: * @st_irq Structure with statistics. * * RETURNS: * Highest CPU number for which stats have been successfully read (2 for CPU0, * 3 for CPU 1, etc.) Same logic than for softnet statistics. This number will * be saved in a->_nr0. See wrap_read_stat_irq(). * Returns 0 if no statistics have been read. * Returns -1 if the buffer was too small and needs to be reallocated (we * mean here, too small for all the CPU, not for the interrupts whose number * is considered to be a constant. Remember that only the number of items is * saved in file preceding each sample, not the number of sub-items). *************************************************************************** */ __nr_t read_stat_irq(struct stats_irq *st_irq, __nr_t nr_alloc, __nr_t nr_int) { FILE *fp; struct stats_irq *st_cpuall_sum, *st_cpu_irq, *st_cpu_sum, *st_cpuall_irq; char *line = NULL, *li; int rc = 0, irq_read = 0; int cpu, len; int cpu_nr = nr_alloc - 1; int *cpu_index = NULL, index = 0; char *cp, *next; if (!cpu_nr) { /* We have only one proc and a non SMP kernel */ cpu_nr = 1; } SREALLOC(cpu_index, int, sizeof(int) * cpu_nr); if ((fp = fopen(INTERRUPTS, "r")) != NULL) { SREALLOC(line, char, INTERRUPTS_LINE + 11 * cpu_nr); /* * Parse header line to see which CPUs are online */ while (fgets(line, INTERRUPTS_LINE + 11 * cpu_nr, fp) != NULL) { next = line; while (((cp = strstr(next, "CPU")) != NULL) && (index < cpu_nr)) { cpu = strtol(cp + 3, &next, 10); if (cpu + 2 > nr_alloc) { rc = -1; goto out; } cpu_index[index++] = cpu; } if (index) /* Header line found */ break; } st_cpuall_sum = st_irq; /* Save name "sum" for total number of interrupts */ strcpy(st_cpuall_sum->irq_name, K_LOWERSUM); /* Parse each line of interrupts statistics data */ while ((fgets(line, INTERRUPTS_LINE + 11 * cpu_nr, fp) != NULL) && (irq_read < nr_int - 1)) { /* Skip over ":" */ if ((cp = strchr(line, ':')) == NULL) /* Chr ':' not found */ continue; cp++; irq_read++; st_cpuall_irq = st_irq + irq_read; /* Remove possible heading spaces in interrupt's name... */ li = line; while (*li == ' ') li++; len = strcspn(li, ":"); if (len >= MAX_SA_IRQ_LEN) { len = MAX_SA_IRQ_LEN - 1; } /* ...then save its name */ strncpy(st_cpuall_irq->irq_name, li, len); st_cpuall_irq->irq_name[len] = '\0'; /* For each interrupt: Get number received by each CPU */ for (cpu = 0; cpu < index; cpu++) { st_cpu_sum = st_irq + (cpu_index[cpu] + 1) * nr_int; st_cpu_irq = st_irq + (cpu_index[cpu] + 1) * nr_int + irq_read; /* * Interrupt name is saved only for CPU "all". * Now save current interrupt value for current CPU * and total number of interrupts received by current CPU * and number of current interrupt received by all CPU. */ st_cpu_irq->irq_nr = strtoul(cp, &next, 10); st_cpuall_irq->irq_nr += st_cpu_irq->irq_nr; st_cpu_sum->irq_nr += st_cpu_irq->irq_nr; cp = next; } st_cpuall_sum->irq_nr += st_cpuall_irq->irq_nr; } out: free(line); fclose(fp); } if (index && !rc) { rc = cpu_index[index - 1] + 2; } free(cpu_index); return rc; } /* *************************************************************************** * Read memory statistics from /proc/meminfo. * * IN: * @st_memory Structure where stats will be saved. * * OUT: * @st_memory Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. * * USED BY: * sadc, pidstat *************************************************************************** */ __nr_t read_meminfo(struct stats_memory *st_memory) { FILE *fp; char line[128]; if ((fp = fopen(MEMINFO, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "MemTotal:", 9)) { /* Read the total amount of memory in kB */ sscanf(line + 9, "%llu", &st_memory->tlmkb); } else if (!strncmp(line, "MemFree:", 8)) { /* Read the amount of free memory in kB */ sscanf(line + 8, "%llu", &st_memory->frmkb); } else if (!strncmp(line, "MemAvailable:", 13)) { /* Read the amount of available memory in kB */ sscanf(line + 13, "%llu", &st_memory->availablekb); } else if (!strncmp(line, "Buffers:", 8)) { /* Read the amount of buffered memory in kB */ sscanf(line + 8, "%llu", &st_memory->bufkb); } else if (!strncmp(line, "Cached:", 7)) { /* Read the amount of cached memory in kB */ sscanf(line + 7, "%llu", &st_memory->camkb); } else if (!strncmp(line, "SwapCached:", 11)) { /* Read the amount of cached swap in kB */ sscanf(line + 11, "%llu", &st_memory->caskb); } else if (!strncmp(line, "Active:", 7)) { /* Read the amount of active memory in kB */ sscanf(line + 7, "%llu", &st_memory->activekb); } else if (!strncmp(line, "Inactive:", 9)) { /* Read the amount of inactive memory in kB */ 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, "%llu", &st_memory->tlskb); } else if (!strncmp(line, "SwapFree:", 9)) { /* Read the amount of free swap memory in kB */ sscanf(line + 9, "%llu", &st_memory->frskb); } else if (!strncmp(line, "Dirty:", 6)) { /* Read the amount of dirty memory in kB */ 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, "%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, "%llu", &st_memory->anonpgkb); } else if (!strncmp(line, "Slab:", 5)) { /* Read the amount of in-kernel data structures cache in kB */ sscanf(line + 5, "%llu", &st_memory->slabkb); } else if (!strncmp(line, "KernelStack:", 12)) { /* Read the kernel stack utilization in kB */ 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, "%llu", &st_memory->pgtblkb); } else if (!strncmp(line, "VmallocUsed:", 12)) { /* Read the amount of vmalloc area which is used in kB */ sscanf(line + 12, "%llu", &st_memory->vmusedkb); } } fclose(fp); return 1; } /* *************************************************************************** * Read machine uptime, independently of the number of processors. * * OUT: * @uptime Uptime value in hundredths of a second. * * USED BY: * sadc, cifsiostat, iostat, mpstat, pidstat *************************************************************************** */ void read_uptime(unsigned long long *uptime) { FILE *fp = NULL; char line[128]; unsigned long up_sec, up_cent; int err = FALSE; if ((fp = fopen(UPTIME, "r")) == NULL) { err = TRUE; } else if (fgets(line, sizeof(line), fp) == NULL) { err = TRUE; } else if (sscanf(line, "%lu.%lu", &up_sec, &up_cent) == 2) { *uptime = (unsigned long long) up_sec * 100 + (unsigned long long) up_cent; } else { err = TRUE; } if (fp != NULL) { fclose(fp); } if (err) { fprintf(stderr, _("Cannot read %s\n"), UPTIME); exit(2); } } /* *************************************************************************** * 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. * * USED BY: * sar, sadf, iostat *************************************************************************** */ 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. * Origin (unmerged) flush operations are counted as writes. */ 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. * * USED BY: * sar, sadf, mpstat *************************************************************************** */ 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_iowait < scp->cpu_iowait) && (scp->cpu_iowait < (ULLONG_MAX - 0x7ffff))) { /* * The iowait value reported by the kernel can also decrement as * a result of inaccurate iowait tracking. Waiting on IO can be * first accounted as iowait but then instead as idle. * Therefore if the idle value during the same period did not * decrease then consider this is a problem with the iowait * reporting and correct the previous value according to the new * reading. Otherwise, treat this as CPU coming back online. */ if ((scc->cpu_idle > scp->cpu_idle) || (scp->cpu_idle >= (ULLONG_MAX - 0x7ffff))) { scp->cpu_iowait = scc->cpu_iowait; } else { scp->cpu_iowait = 0; } } if ((scc->cpu_idle < scp->cpu_idle) && (scp->cpu_idle < (ULLONG_MAX - 0x7ffff))) { scp->cpu_idle = 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 ---------------------*/ /* *************************************************************************** * Replace octal codes in string with their corresponding characters. * * IN: * @str String to parse. * * OUT: * @str String with octal codes replaced with characters. *************************************************************************** */ void oct2chr(char *str) { int i = 0; int j, len; len = strlen(str); while (i < len - 3) { if ((str[i] == '\\') && (str[i + 1] >= '0') && (str[i + 1] <= '3') && (str[i + 2] >= '0') && (str[i + 2] <= '7') && (str[i + 3] >= '0') && (str[i + 3] <= '7')) { /* Octal code found */ str[i] = (str[i + 1] - 48) * 64 + (str[i + 2] - 48) * 8 + (str[i + 3] - 48); for (j = i + 4; j <= len; j++) { str[j - 3] = str[j]; } len -= 3; } i++; } } /* *************************************************************************** * Read processes (tasks) creation and context switches statistics * from /proc/stat. * * IN: * @st_pcsw Structure where stats will be saved. * * OUT: * @st_pcsw Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_stat_pcsw(struct stats_pcsw *st_pcsw) { FILE *fp; char line[8192]; if ((fp = fopen(STAT, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "ctxt ", 5)) { /* Read number of context switches */ sscanf(line + 5, "%llu", &st_pcsw->context_switch); } else if (!strncmp(line, "processes ", 10)) { /* Read number of processes created since system boot */ sscanf(line + 10, "%lu", &st_pcsw->processes); } } fclose(fp); return 1; } /* *************************************************************************** * Read queue and load statistics from /proc/loadavg and /proc/stat. * * IN: * @st_queue Structure where stats will be saved. * * OUT: * @st_queue Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_loadavg(struct stats_queue *st_queue) { FILE *fp; char line[8192]; unsigned int load_tmp[3]; int rc; if ((fp = fopen(LOADAVG, "r")) == NULL) return 0; /* Read load averages and queue length */ 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, &st_queue->nr_running, &st_queue->nr_threads); fclose(fp); if (rc < 8) return 0; st_queue->load_avg_1 += load_tmp[0] * 100; st_queue->load_avg_5 += load_tmp[1] * 100; st_queue->load_avg_15 += load_tmp[2] * 100; if (st_queue->nr_running) { /* Do not take current process into account */ st_queue->nr_running--; } /* Read nr of tasks blocked from /proc/stat */ if ((fp = fopen(STAT, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "procs_blocked ", 14)) { /* Read number of processes blocked */ sscanf(line + 14, "%llu", &st_queue->procs_blocked); break; } } fclose(fp); return 1; } /* *************************************************************************** * Read swapping statistics from /proc/vmstat. * * IN: * @st_swap Structure where stats will be saved. * * OUT: * @st_swap Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_vmstat_swap(struct stats_swap *st_swap) { FILE *fp; char line[128]; if ((fp = fopen(VMSTAT, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "pswpin ", 7)) { /* Read number of swap pages brought in */ sscanf(line + 7, "%lu", &st_swap->pswpin); } else if (!strncmp(line, "pswpout ", 8)) { /* Read number of swap pages brought out */ sscanf(line + 8, "%lu", &st_swap->pswpout); } } fclose(fp); return 1; } /* *************************************************************************** * Read paging statistics from /proc/vmstat. * * IN: * @st_paging Structure where stats will be saved. * * OUT: * @st_paging Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_vmstat_paging(struct stats_paging *st_paging) { FILE *fp; char line[128]; unsigned long pgtmp; if ((fp = fopen(VMSTAT, "r")) == NULL) return 0; st_paging->pgsteal = 0; st_paging->pgscan_kswapd = st_paging->pgscan_direct = 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "pgpgin ", 7)) { /* Read number of pages the system paged in */ sscanf(line + 7, "%lu", &st_paging->pgpgin); } else if (!strncmp(line, "pgpgout ", 8)) { /* Read number of pages the system paged out */ sscanf(line + 8, "%lu", &st_paging->pgpgout); } else if (!strncmp(line, "pgfault ", 8)) { /* Read number of faults (major+minor) made by the system */ sscanf(line + 8, "%lu", &st_paging->pgfault); } else if (!strncmp(line, "pgmajfault ", 11)) { /* Read number of faults (major only) made by the system */ sscanf(line + 11, "%lu", &st_paging->pgmajfault); } else if (!strncmp(line, "pgfree ", 7)) { /* Read number of pages freed by the system */ sscanf(line + 7, "%lu", &st_paging->pgfree); } else if (!strncmp(line, "pgsteal_", 8)) { /* Read number of pages stolen by the system */ sscanf(strchr(line, ' '), "%lu", &pgtmp); st_paging->pgsteal += pgtmp; } else if (!strncmp(line, "pgscan_kswapd", 13)) { /* Read number of pages scanned by the kswapd daemon */ sscanf(strchr(line, ' '), "%lu", &pgtmp); st_paging->pgscan_kswapd += pgtmp; } else if (!strncmp(line, "pgscan_direct", 13)) { /* Read number of pages scanned directly */ sscanf(strchr(line, ' '), "%lu", &pgtmp); st_paging->pgscan_direct += pgtmp; } } fclose(fp); return 1; } /* *************************************************************************** * Read I/O and transfer rates statistics from /proc/diskstats. * * IN: * @st_io Structure where stats will be saved. * * OUT: * @st_io Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_diskstats_io(struct stats_io *st_io) { FILE *fp; char line[1024]; char dev_name[MAX_NAME_LEN]; unsigned int major, minor; 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) { /* 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, &dc_ios, &dc_sec) >= 7) { if (is_device(SLASH_SYS, 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 + (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; } } } fclose(fp); return 1; } /* *************************************************************************** * Read block devices statistics from /proc/diskstats. * * IN: * @st_disk Structure where stats will be saved. * @nr_alloc Total number of structures allocated. Value is >= 1. * @read_part True if disks *and* partitions should be read; False if only * disks are read. * * OUT: * @st_disk Structure with statistics. * * RETURNS: * Number of block devices read, or -1 if the buffer was too small and * needs to be reallocated. *************************************************************************** */ __nr_t read_diskstats_disk(struct stats_disk *st_disk, __nr_t nr_alloc, int read_part) { FILE *fp; char line[1024]; char dev_name[MAX_NAME_LEN]; struct stats_disk *st_disk_i; 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) return 0; while (fgets(line, sizeof(line), fp) != NULL) { /* 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, &dc_ios, &dc_sec, &dc_ticks) >= 11) { if (!rd_ios && !wr_ios && !dc_ios) /* Unused device: Ignore it */ continue; if (read_part || is_device(SLASH_SYS, dev_name, ACCEPT_VIRTUAL_DEVICES)) { if (dsk_read + 1 > nr_alloc) { dsk_read = -1; break; } 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 + (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; } } } } fclose(fp); return dsk_read; } /* *************************************************************************** * Read serial lines statistics from /proc/tty/driver/serial. * * IN: * @st_serial Structure where stats will be saved. * @nr_alloc Total number of structures allocated. Value is >= 1. * * OUT: * @st_serial Structure with statistics. * * RETURNS: * Number of serial lines read, or -1 if the buffer was too small and * needs to be reallocated. *************************************************************************** */ __nr_t read_tty_driver_serial(struct stats_serial *st_serial, __nr_t nr_alloc) { FILE *fp; struct stats_serial *st_serial_i; char line[256]; char *p; __nr_t sl_read = 0; if ((fp = fopen(SERIAL, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL ) { if ((p = strstr(line, "tx:")) != NULL) { if (sl_read + 1 > nr_alloc) { sl_read = -1; break; } st_serial_i = st_serial + sl_read++; /* Read serial line number */ sscanf(line, "%u", &st_serial_i->line); /* * Read the number of chars transmitted and received by * current serial line. */ sscanf(p + 3, "%u", &st_serial_i->tx); if ((p = strstr(line, "rx:")) != NULL) { sscanf(p + 3, "%u", &st_serial_i->rx); } if ((p = strstr(line, "fe:")) != NULL) { sscanf(p + 3, "%u", &st_serial_i->frame); } if ((p = strstr(line, "pe:")) != NULL) { sscanf(p + 3, "%u", &st_serial_i->parity); } if ((p = strstr(line, "brk:")) != NULL) { sscanf(p + 4, "%u", &st_serial_i->brk); } if ((p = strstr(line, "oe:")) != NULL) { sscanf(p + 3, "%u", &st_serial_i->overrun); } } } fclose(fp); return sl_read; } /* *************************************************************************** * Read kernel tables statistics from various system files. * * IN: * @st_ktables Structure where stats will be saved. * * OUT: * @st_ktables Structure with statistics. * * RETURNS: * 1 (always success). *************************************************************************** */ __nr_t read_kernel_tables(struct stats_ktables *st_ktables) { FILE *fp; 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 %llu", &st_ktables->dentry_stat); fclose(fp); if (rc == 0) { st_ktables->dentry_stat = 0; } } /* Open /proc/sys/fs/file-nr file */ if ((fp = fopen(FFILE_NR, "r")) != NULL) { rc = fscanf(fp, "%llu %llu", &st_ktables->file_used, &parm); fclose(fp); /* * The number of used handles is the number of allocated ones * minus the number of free ones. */ if (rc == 2) { st_ktables->file_used -= parm; } else { st_ktables->file_used = 0; } } /* Open /proc/sys/fs/inode-state file */ if ((fp = fopen(FINODE_STATE, "r")) != NULL) { rc = fscanf(fp, "%llu %llu", &st_ktables->inode_used, &parm); fclose(fp); /* * The number of inuse inodes is the number of allocated ones * minus the number of free ones. */ if (rc == 2) { st_ktables->inode_used -= parm; } else { st_ktables->inode_used = 0; } } /* Open /proc/sys/kernel/pty/nr file */ if ((fp = fopen(PTY_NR, "r")) != NULL) { rc = fscanf(fp, "%llu", &st_ktables->pty_nr); fclose(fp); if (rc == 0) { st_ktables->pty_nr = 0; } } return 1; } /* *************************************************************************** * Read network interfaces statistics from /proc/net/dev. * * IN: * @st_net_dev Structure where stats will be saved. * @nr_alloc Total number of structures allocated. Value is >= 1. * * OUT: * @st_net_dev Structure with statistics. * * RETURNS: * Number of interfaces read, or -1 if the buffer was too small and * needs to be reallocated. *************************************************************************** */ __nr_t read_net_dev(struct stats_net_dev *st_net_dev, __nr_t nr_alloc) { FILE *fp; struct stats_net_dev *st_net_dev_i; char line[256]; char iface[MAX_IFACE_LEN]; __nr_t dev_read = 0; int pos; if ((fp = fopen(NET_DEV, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { pos = strcspn(line, ":"); if (pos < strlen(line)) { if (dev_read + 1 > nr_alloc) { dev_read = -1; break; } st_net_dev_i = st_net_dev + dev_read++; strncpy(iface, line, MINIMUM(pos, sizeof(iface) - 1)); iface[MINIMUM(pos, sizeof(iface) - 1)] = '\0'; sscanf(iface, "%s", st_net_dev_i->interface); /* Skip heading spaces */ sscanf(line + pos + 1, "%llu %llu %*u %*u %*u %*u %llu %llu %llu %llu " "%*u %*u %*u %*u %*u %llu", &st_net_dev_i->rx_bytes, &st_net_dev_i->rx_packets, &st_net_dev_i->rx_compressed, &st_net_dev_i->multicast, &st_net_dev_i->tx_bytes, &st_net_dev_i->tx_packets, &st_net_dev_i->tx_compressed); } } fclose(fp); return dev_read; } /* *************************************************************************** * Read duplex and speed data for network interface cards. * * IN: * @st_net_dev Structure where stats will be saved. * @nbr Number of network interfaces to read. * * OUT: * @st_net_dev Structure with statistics. *************************************************************************** */ void read_if_info(struct stats_net_dev *st_net_dev, int nbr) { FILE *fp; struct stats_net_dev *st_net_dev_i; char filename[128], duplex[32]; int dev, n; for (dev = 0; dev < nbr; dev++) { st_net_dev_i = st_net_dev + dev; /* Read speed info */ sprintf(filename, IF_DUPLEX, st_net_dev_i->interface); if ((fp = fopen(filename, "r")) == NULL) /* Cannot read NIC duplex */ continue; n = fscanf(fp, "%31s", duplex); fclose(fp); if (n != 1) /* Cannot read NIC duplex */ continue; if (!strcmp(duplex, K_DUPLEX_FULL)) { st_net_dev_i->duplex = C_DUPLEX_FULL; } else if (!strcmp(duplex, K_DUPLEX_HALF)) { st_net_dev_i->duplex = C_DUPLEX_HALF; } else continue; /* Read speed info */ sprintf(filename, IF_SPEED, st_net_dev_i->interface); if ((fp = fopen(filename, "r")) == NULL) /* Cannot read NIC speed */ continue; n = fscanf(fp, "%u", &st_net_dev_i->speed); fclose(fp); if (n != 1) { st_net_dev_i->speed = 0; } } } /* *************************************************************************** * Read network interfaces errors statistics from /proc/net/dev. * * IN: * @st_net_edev Structure where stats will be saved. * @nr_alloc Total number of structures allocated. Value is >= 1. * * OUT: * @st_net_edev Structure with statistics. * * RETURNS: * Number of interfaces read, or -1 if the buffer was too small and * needs to be reallocated. *************************************************************************** */ __nr_t read_net_edev(struct stats_net_edev *st_net_edev, __nr_t nr_alloc) { FILE *fp; struct stats_net_edev *st_net_edev_i; static char line[256]; char iface[MAX_IFACE_LEN]; __nr_t dev_read = 0; int pos; if ((fp = fopen(NET_DEV, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { pos = strcspn(line, ":"); if (pos < strlen(line)) { if (dev_read + 1 > nr_alloc) { dev_read = -1; break; } st_net_edev_i = st_net_edev + dev_read++; strncpy(iface, line, MINIMUM(pos, sizeof(iface) - 1)); iface[MINIMUM(pos, sizeof(iface) - 1)] = '\0'; sscanf(iface, "%s", st_net_edev_i->interface); /* Skip heading spaces */ sscanf(line + pos + 1, "%*u %*u %llu %llu %llu %llu %*u %*u %*u %*u " "%llu %llu %llu %llu %llu", &st_net_edev_i->rx_errors, &st_net_edev_i->rx_dropped, &st_net_edev_i->rx_fifo_errors, &st_net_edev_i->rx_frame_errors, &st_net_edev_i->tx_errors, &st_net_edev_i->tx_dropped, &st_net_edev_i->tx_fifo_errors, &st_net_edev_i->collisions, &st_net_edev_i->tx_carrier_errors); } } fclose(fp); return dev_read; } /* *************************************************************************** * Read NFS client statistics from /proc/net/rpc/nfs. * * IN: * @st_net_nfs Structure where stats will be saved. * * OUT: * @st_net_nfs Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_nfs(struct stats_net_nfs *st_net_nfs) { FILE *fp; char line[256]; unsigned int getattcnt = 0, accesscnt = 0, readcnt = 0, writecnt = 0; if ((fp = fopen(NET_RPC_NFS, "r")) == NULL) return 0; memset(st_net_nfs, 0, STATS_NET_NFS_SIZE); while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "rpc ", 4)) { sscanf(line + 4, "%u %u", &st_net_nfs->nfs_rpccnt, &st_net_nfs->nfs_rpcretrans); } else if (!strncmp(line, "proc3 ", 6)) { sscanf(line + 6, "%*u %*u %u %*u %*u %u %*u %u %u", &getattcnt, &accesscnt, &readcnt, &writecnt); st_net_nfs->nfs_getattcnt += getattcnt; st_net_nfs->nfs_accesscnt += accesscnt; st_net_nfs->nfs_readcnt += readcnt; st_net_nfs->nfs_writecnt += writecnt; } else if (!strncmp(line, "proc4 ", 6)) { sscanf(line + 6, "%*u %*u %u %u " "%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %u %u", &readcnt, &writecnt, &accesscnt, &getattcnt); st_net_nfs->nfs_getattcnt += getattcnt; st_net_nfs->nfs_accesscnt += accesscnt; st_net_nfs->nfs_readcnt += readcnt; st_net_nfs->nfs_writecnt += writecnt; } } fclose(fp); return 1; } /* *************************************************************************** * Read NFS server statistics from /proc/net/rpc/nfsd. * * IN: * @st_net_nfsd Structure where stats will be saved. * * OUT: * @st_net_nfsd Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_nfsd(struct stats_net_nfsd *st_net_nfsd) { FILE *fp; char line[256]; unsigned int getattcnt = 0, accesscnt = 0, readcnt = 0, writecnt = 0; if ((fp = fopen(NET_RPC_NFSD, "r")) == NULL) return 0; memset(st_net_nfsd, 0, STATS_NET_NFSD_SIZE); while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "rc ", 3)) { sscanf(line + 3, "%u %u", &st_net_nfsd->nfsd_rchits, &st_net_nfsd->nfsd_rcmisses); } else if (!strncmp(line, "net ", 4)) { sscanf(line + 4, "%u %u %u", &st_net_nfsd->nfsd_netcnt, &st_net_nfsd->nfsd_netudpcnt, &st_net_nfsd->nfsd_nettcpcnt); } else if (!strncmp(line, "rpc ", 4)) { sscanf(line + 4, "%u %u", &st_net_nfsd->nfsd_rpccnt, &st_net_nfsd->nfsd_rpcbad); } else if (!strncmp(line, "proc3 ", 6)) { sscanf(line + 6, "%*u %*u %u %*u %*u %u %*u %u %u", &getattcnt, &accesscnt, &readcnt, &writecnt); st_net_nfsd->nfsd_getattcnt += getattcnt; st_net_nfsd->nfsd_accesscnt += accesscnt; st_net_nfsd->nfsd_readcnt += readcnt; st_net_nfsd->nfsd_writecnt += writecnt; } else if (!strncmp(line, "proc4ops ", 9)) { sscanf(line + 9, "%*u %*u %*u %*u %u " "%*u %*u %*u %*u %*u %u " "%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %u " "%*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %u", &accesscnt, &getattcnt, &readcnt, &writecnt); st_net_nfsd->nfsd_getattcnt += getattcnt; st_net_nfsd->nfsd_accesscnt += accesscnt; st_net_nfsd->nfsd_readcnt += readcnt; st_net_nfsd->nfsd_writecnt += writecnt; } } fclose(fp); return 1; } /* *************************************************************************** * Read network sockets statistics from /proc/net/sockstat. * * IN: * @st_net_sock Structure where stats will be saved. * * OUT: * @st_net_sock Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_sock(struct stats_net_sock *st_net_sock) { FILE *fp; char line[96]; char *p; if ((fp = fopen(NET_SOCKSTAT, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "sockets:", 8)) { /* Sockets */ sscanf(line + 14, "%u", &st_net_sock->sock_inuse); } else if (!strncmp(line, "TCP:", 4)) { /* TCP sockets */ sscanf(line + 11, "%u", &st_net_sock->tcp_inuse); if ((p = strstr(line, "tw")) != NULL) { sscanf(p + 2, "%u", &st_net_sock->tcp_tw); } } else if (!strncmp(line, "UDP:", 4)) { /* UDP sockets */ sscanf(line + 11, "%u", &st_net_sock->udp_inuse); } else if (!strncmp(line, "RAW:", 4)) { /* RAW sockets */ sscanf(line + 11, "%u", &st_net_sock->raw_inuse); } else if (!strncmp(line, "FRAG:", 5)) { /* FRAGments */ sscanf(line + 12, "%u", &st_net_sock->frag_inuse); } } fclose(fp); return 1; } /* *************************************************************************** * Read IP network traffic statistics from /proc/net/snmp. * * IN: * @st_net_ip Structure where stats will be saved. * * OUT: * @st_net_ip Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_ip(struct stats_net_ip *st_net_ip) { FILE *fp; char line[1024]; int sw = FALSE; if ((fp = fopen(NET_SNMP, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "Ip:", 3)) { if (sw) { sscanf(line + 3, "%*u %*u %llu %*u %*u %llu %*u %*u " "%llu %llu %*u %*u %*u %llu %llu %*u %llu %*u %llu", &st_net_ip->InReceives, &st_net_ip->ForwDatagrams, &st_net_ip->InDelivers, &st_net_ip->OutRequests, &st_net_ip->ReasmReqds, &st_net_ip->ReasmOKs, &st_net_ip->FragOKs, &st_net_ip->FragCreates); break; } else { sw = TRUE; } } } fclose(fp); return 1; } /* *************************************************************************** * Read IP network errors statistics from /proc/net/snmp. * * IN: * @st_net_eip Structure where stats will be saved. * * OUT: * @st_net_eip Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_eip(struct stats_net_eip *st_net_eip) { FILE *fp; char line[1024]; int sw = FALSE; if ((fp = fopen(NET_SNMP, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "Ip:", 3)) { if (sw) { sscanf(line + 3, "%*u %*u %*u %llu %llu %*u %llu %llu " "%*u %*u %llu %llu %*u %*u %*u %llu %*u %llu", &st_net_eip->InHdrErrors, &st_net_eip->InAddrErrors, &st_net_eip->InUnknownProtos, &st_net_eip->InDiscards, &st_net_eip->OutDiscards, &st_net_eip->OutNoRoutes, &st_net_eip->ReasmFails, &st_net_eip->FragFails); break; } else { sw = TRUE; } } } fclose(fp); return 1; } /* *************************************************************************** * Read ICMP network traffic statistics from /proc/net/snmp. * * IN: * @st_net_icmp Structure where stats will be saved. * * OUT: * @st_net_icmp Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_icmp(struct stats_net_icmp *st_net_icmp) { FILE *fp; char line[1024]; static char format[256] = ""; int sw = FALSE; if ((fp = fopen(NET_SNMP, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "Icmp:", 5)) { if (sw) { sscanf(line + 5, format, &st_net_icmp->InMsgs, &st_net_icmp->InEchos, &st_net_icmp->InEchoReps, &st_net_icmp->InTimestamps, &st_net_icmp->InTimestampReps, &st_net_icmp->InAddrMasks, &st_net_icmp->InAddrMaskReps, &st_net_icmp->OutMsgs, &st_net_icmp->OutEchos, &st_net_icmp->OutEchoReps, &st_net_icmp->OutTimestamps, &st_net_icmp->OutTimestampReps, &st_net_icmp->OutAddrMasks, &st_net_icmp->OutAddrMaskReps); break; } else { if (!strlen(format)) { if (strstr(line, "InCsumErrors")) { /* * New format: InCsumErrors field exists at position #3. * Capture: 1,9,10,11,12,13,14,15,22,23,24,25,26,27. */ strcpy(format, "%lu %*u %*u %*u %*u %*u %*u %*u " "%lu %lu %lu %lu %lu %lu %lu %*u %*u %*u %*u " "%*u %*u %lu %lu %lu %lu %lu %lu"); } else { /* * Old format: InCsumErrors field doesn't exist. * Capture: 1,8,9,10,11,12,13,14,21,22,23,24,25,26. */ strcpy(format, "%lu %*u %*u %*u %*u %*u %*u " "%lu %lu %lu %lu %lu %lu %lu %*u %*u %*u %*u " "%*u %*u %lu %lu %lu %lu %lu %lu"); } } sw = TRUE; } } } fclose(fp); return 1; } /* *************************************************************************** * Read ICMP network errors statistics from /proc/net/snmp. * * IN: * @st_net_eicmp Structure where stats will be saved. * * OUT: * @st_net_eicmp Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_eicmp(struct stats_net_eicmp *st_net_eicmp) { FILE *fp; char line[1024]; static char format[256] = ""; int sw = FALSE; if ((fp = fopen(NET_SNMP, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "Icmp:", 5)) { if (sw) { sscanf(line + 5, format, &st_net_eicmp->InErrors, &st_net_eicmp->InDestUnreachs, &st_net_eicmp->InTimeExcds, &st_net_eicmp->InParmProbs, &st_net_eicmp->InSrcQuenchs, &st_net_eicmp->InRedirects, &st_net_eicmp->OutErrors, &st_net_eicmp->OutDestUnreachs, &st_net_eicmp->OutTimeExcds, &st_net_eicmp->OutParmProbs, &st_net_eicmp->OutSrcQuenchs, &st_net_eicmp->OutRedirects); 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; } } } fclose(fp); return 1; } /* *************************************************************************** * Read TCP network traffic statistics from /proc/net/snmp. * * IN: * @st_net_tcp Structure where stats will be saved. * * OUT: * @st_net_tcp Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_tcp(struct stats_net_tcp *st_net_tcp) { FILE *fp; char line[1024]; int sw = FALSE; if ((fp = fopen(NET_SNMP, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "Tcp:", 4)) { if (sw) { sscanf(line + 4, "%*u %*u %*u %*d %lu %lu " "%*u %*u %*u %lu %lu", &st_net_tcp->ActiveOpens, &st_net_tcp->PassiveOpens, &st_net_tcp->InSegs, &st_net_tcp->OutSegs); break; } else { sw = TRUE; } } } fclose(fp); return 1; } /* *************************************************************************** * Read TCP network errors statistics from /proc/net/snmp. * * IN: * @st_net_etcp Structure where stats will be saved. * * OUT: * @st_net_etcp Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_etcp(struct stats_net_etcp *st_net_etcp) { FILE *fp; char line[1024]; int sw = FALSE; if ((fp = fopen(NET_SNMP, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "Tcp:", 4)) { if (sw) { sscanf(line + 4, "%*u %*u %*u %*d %*u %*u " "%lu %lu %*u %*u %*u %lu %lu %lu", &st_net_etcp->AttemptFails, &st_net_etcp->EstabResets, &st_net_etcp->RetransSegs, &st_net_etcp->InErrs, &st_net_etcp->OutRsts); break; } else { sw = TRUE; } } } fclose(fp); return 1; } /* *************************************************************************** * Read UDP network traffic statistics from /proc/net/snmp. * * IN: * @st_net_udp Structure where stats will be saved. * * OUT: * @st_net_udp Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_udp(struct stats_net_udp *st_net_udp) { FILE *fp; char line[1024]; int sw = FALSE; if ((fp = fopen(NET_SNMP, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "Udp:", 4)) { if (sw) { sscanf(line + 4, "%lu %lu %lu %lu", &st_net_udp->InDatagrams, &st_net_udp->NoPorts, &st_net_udp->InErrors, &st_net_udp->OutDatagrams); break; } else { sw = TRUE; } } } fclose(fp); return 1; } /* *************************************************************************** * Read IPv6 network sockets statistics from /proc/net/sockstat6. * * IN: * @st_net_sock6 Structure where stats will be saved. * * OUT: * @st_net_sock6 Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_sock6(struct stats_net_sock6 *st_net_sock6) { FILE *fp; char line[96]; if ((fp = fopen(NET_SOCKSTAT6, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "TCP6:", 5)) { /* TCPv6 sockets */ sscanf(line + 12, "%u", &st_net_sock6->tcp6_inuse); } else if (!strncmp(line, "UDP6:", 5)) { /* UDPv6 sockets */ sscanf(line + 12, "%u", &st_net_sock6->udp6_inuse); } else if (!strncmp(line, "RAW6:", 5)) { /* IPv6 RAW sockets */ sscanf(line + 12, "%u", &st_net_sock6->raw6_inuse); } else if (!strncmp(line, "FRAG6:", 6)) { /* IPv6 FRAGments */ sscanf(line + 13, "%u", &st_net_sock6->frag6_inuse); } } fclose(fp); return 1; } /* *************************************************************************** * Read IPv6 network traffic statistics from /proc/net/snmp6. * * IN: * @st_net_ip6 Structure where stats will be saved. * * OUT: * @st_net_ip6 Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_ip6(struct stats_net_ip6 *st_net_ip6) { FILE *fp; char line[128]; if ((fp = fopen(NET_SNMP6, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "Ip6InReceives ", 14)) { sscanf(line + 14, "%llu", &st_net_ip6->InReceives6); } else if (!strncmp(line, "Ip6OutForwDatagrams ", 20)) { sscanf(line + 20, "%llu", &st_net_ip6->OutForwDatagrams6); } else if (!strncmp(line, "Ip6InDelivers ", 14)) { sscanf(line + 14, "%llu", &st_net_ip6->InDelivers6); } else if (!strncmp(line, "Ip6OutRequests ", 15)) { sscanf(line + 15, "%llu", &st_net_ip6->OutRequests6); } else if (!strncmp(line, "Ip6ReasmReqds ", 14)) { sscanf(line + 14, "%llu", &st_net_ip6->ReasmReqds6); } else if (!strncmp(line, "Ip6ReasmOKs ", 12)) { sscanf(line + 12, "%llu", &st_net_ip6->ReasmOKs6); } else if (!strncmp(line, "Ip6InMcastPkts ", 15)) { sscanf(line + 15, "%llu", &st_net_ip6->InMcastPkts6); } else if (!strncmp(line, "Ip6OutMcastPkts ", 16)) { sscanf(line + 16, "%llu", &st_net_ip6->OutMcastPkts6); } else if (!strncmp(line, "Ip6FragOKs ", 11)) { sscanf(line + 11, "%llu", &st_net_ip6->FragOKs6); } else if (!strncmp(line, "Ip6FragCreates ", 15)) { sscanf(line + 15, "%llu", &st_net_ip6->FragCreates6); } } fclose(fp); return 1; } /* *************************************************************************** * Read IPv6 network errors statistics from /proc/net/snmp6. * * IN: * @st_net_eip6 Structure where stats will be saved. * * OUT: * @st_net_eip6 Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_eip6(struct stats_net_eip6 *st_net_eip6) { FILE *fp; char line[128]; if ((fp = fopen(NET_SNMP6, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "Ip6InHdrErrors ", 15)) { sscanf(line + 15, "%llu", &st_net_eip6->InHdrErrors6); } else if (!strncmp(line, "Ip6InAddrErrors ", 16)) { sscanf(line + 16, "%llu", &st_net_eip6->InAddrErrors6); } else if (!strncmp(line, "Ip6InUnknownProtos ", 19)) { sscanf(line + 19, "%llu", &st_net_eip6->InUnknownProtos6); } else if (!strncmp(line, "Ip6InTooBigErrors ", 18)) { sscanf(line + 18, "%llu", &st_net_eip6->InTooBigErrors6); } else if (!strncmp(line, "Ip6InDiscards ", 14)) { sscanf(line + 14, "%llu", &st_net_eip6->InDiscards6); } else if (!strncmp(line, "Ip6OutDiscards ", 15)) { sscanf(line + 15, "%llu", &st_net_eip6->OutDiscards6); } else if (!strncmp(line, "Ip6InNoRoutes ", 14)) { sscanf(line + 14, "%llu", &st_net_eip6->InNoRoutes6); } else if (!strncmp(line, "Ip6OutNoRoutes ", 15)) { sscanf(line + 15, "%llu", &st_net_eip6->OutNoRoutes6); } else if (!strncmp(line, "Ip6ReasmFails ", 14)) { sscanf(line + 14, "%llu", &st_net_eip6->ReasmFails6); } else if (!strncmp(line, "Ip6FragFails ", 13)) { sscanf(line + 13, "%llu", &st_net_eip6->FragFails6); } else if (!strncmp(line, "Ip6InTruncatedPkts ", 19)) { sscanf(line + 19, "%llu", &st_net_eip6->InTruncatedPkts6); } } fclose(fp); return 1; } /* *************************************************************************** * Read ICMPv6 network traffic statistics from /proc/net/snmp6. * * IN: * @st_net_icmp6 Structure where stats will be saved. * * OUT: * @st_net_icmp6 Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_icmp6(struct stats_net_icmp6 *st_net_icmp6) { FILE *fp; char line[128]; if ((fp = fopen(NET_SNMP6, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "Icmp6InMsgs ", 12)) { sscanf(line + 12, "%lu", &st_net_icmp6->InMsgs6); } else if (!strncmp(line, "Icmp6OutMsgs ", 13)) { sscanf(line + 13, "%lu", &st_net_icmp6->OutMsgs6); } else if (!strncmp(line, "Icmp6InEchos ", 13)) { sscanf(line + 13, "%lu", &st_net_icmp6->InEchos6); } else if (!strncmp(line, "Icmp6InEchoReplies ", 19)) { sscanf(line + 19, "%lu", &st_net_icmp6->InEchoReplies6); } else if (!strncmp(line, "Icmp6OutEchoReplies ", 20)) { sscanf(line + 20, "%lu", &st_net_icmp6->OutEchoReplies6); } else if (!strncmp(line, "Icmp6InGroupMembQueries ", 24)) { sscanf(line + 24, "%lu", &st_net_icmp6->InGroupMembQueries6); } else if (!strncmp(line, "Icmp6InGroupMembResponses ", 26)) { sscanf(line + 26, "%lu", &st_net_icmp6->InGroupMembResponses6); } else if (!strncmp(line, "Icmp6OutGroupMembResponses ", 27)) { sscanf(line + 27, "%lu", &st_net_icmp6->OutGroupMembResponses6); } else if (!strncmp(line, "Icmp6InGroupMembReductions ", 27)) { sscanf(line + 27, "%lu", &st_net_icmp6->InGroupMembReductions6); } else if (!strncmp(line, "Icmp6OutGroupMembReductions ", 28)) { sscanf(line + 28, "%lu", &st_net_icmp6->OutGroupMembReductions6); } else if (!strncmp(line, "Icmp6InRouterSolicits ", 22)) { sscanf(line + 22, "%lu", &st_net_icmp6->InRouterSolicits6); } else if (!strncmp(line, "Icmp6OutRouterSolicits ", 23)) { sscanf(line + 23, "%lu", &st_net_icmp6->OutRouterSolicits6); } else if (!strncmp(line, "Icmp6InRouterAdvertisements ", 28)) { sscanf(line + 28, "%lu", &st_net_icmp6->InRouterAdvertisements6); } else if (!strncmp(line, "Icmp6InNeighborSolicits ", 24)) { sscanf(line + 24, "%lu", &st_net_icmp6->InNeighborSolicits6); } else if (!strncmp(line, "Icmp6OutNeighborSolicits ", 25)) { sscanf(line + 25, "%lu", &st_net_icmp6->OutNeighborSolicits6); } else if (!strncmp(line, "Icmp6InNeighborAdvertisements ", 30)) { sscanf(line + 30, "%lu", &st_net_icmp6->InNeighborAdvertisements6); } else if (!strncmp(line, "Icmp6OutNeighborAdvertisements ", 31)) { sscanf(line + 31, "%lu", &st_net_icmp6->OutNeighborAdvertisements6); } } fclose(fp); return 1; } /* *************************************************************************** * Read ICMPv6 network errors statistics from /proc/net/snmp6. * * IN: * @st_net_eicmp6 Structure where stats will be saved. * * OUT: * @st_net_eicmp6 Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_eicmp6(struct stats_net_eicmp6 *st_net_eicmp6) { FILE *fp; char line[128]; if ((fp = fopen(NET_SNMP6, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "Icmp6InErrors ", 14)) { sscanf(line + 14, "%lu", &st_net_eicmp6->InErrors6); } else if (!strncmp(line, "Icmp6InDestUnreachs ", 20)) { sscanf(line + 20, "%lu", &st_net_eicmp6->InDestUnreachs6); } else if (!strncmp(line, "Icmp6OutDestUnreachs ", 21)) { sscanf(line + 21, "%lu", &st_net_eicmp6->OutDestUnreachs6); } else if (!strncmp(line, "Icmp6InTimeExcds ", 17)) { sscanf(line + 17, "%lu", &st_net_eicmp6->InTimeExcds6); } else if (!strncmp(line, "Icmp6OutTimeExcds ", 18)) { sscanf(line + 18, "%lu", &st_net_eicmp6->OutTimeExcds6); } else if (!strncmp(line, "Icmp6InParmProblems ", 20)) { sscanf(line + 20, "%lu", &st_net_eicmp6->InParmProblems6); } else if (!strncmp(line, "Icmp6OutParmProblems ", 21)) { sscanf(line + 21, "%lu", &st_net_eicmp6->OutParmProblems6); } else if (!strncmp(line, "Icmp6InRedirects ", 17)) { sscanf(line + 17, "%lu", &st_net_eicmp6->InRedirects6); } else if (!strncmp(line, "Icmp6OutRedirects ", 18)) { sscanf(line + 18, "%lu", &st_net_eicmp6->OutRedirects6); } else if (!strncmp(line, "Icmp6InPktTooBigs ", 18)) { sscanf(line + 18, "%lu", &st_net_eicmp6->InPktTooBigs6); } else if (!strncmp(line, "Icmp6OutPktTooBigs ", 19)) { sscanf(line + 19, "%lu", &st_net_eicmp6->OutPktTooBigs6); } } fclose(fp); return 1; } /* *************************************************************************** * Read UDPv6 network traffic statistics from /proc/net/snmp6. * * IN: * @st_net_udp6 Structure where stats will be saved. * * OUT: * @st_net_udp6 Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_net_udp6(struct stats_net_udp6 *st_net_udp6) { FILE *fp; char line[128]; if ((fp = fopen(NET_SNMP6, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "Udp6InDatagrams ", 16)) { sscanf(line + 16, "%lu", &st_net_udp6->InDatagrams6); } else if (!strncmp(line, "Udp6OutDatagrams ", 17)) { sscanf(line + 17, "%lu", &st_net_udp6->OutDatagrams6); } else if (!strncmp(line, "Udp6NoPorts ", 12)) { sscanf(line + 12, "%lu", &st_net_udp6->NoPorts6); } else if (!strncmp(line, "Udp6InErrors ", 13)) { sscanf(line + 13, "%lu", &st_net_udp6->InErrors6); } } fclose(fp); return 1; } /* *************************************************************************** * Read CPU frequency statistics. * * IN: * @st_pwr_cpufreq Structure where stats will be saved. * @nr_alloc Total number of structures allocated. Value is >= 1. * * OUT: * @st_pwr_cpufreq Structure with statistics. * * RETURNS: * Highest CPU number 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. *************************************************************************** */ __nr_t read_cpuinfo(struct stats_pwr_cpufreq *st_pwr_cpufreq, __nr_t nr_alloc) { FILE *fp; struct stats_pwr_cpufreq *st_pwr_cpufreq_i; char line[1024]; int nr = 0; __nr_t cpu_read = 1; /* For CPU "all" */ unsigned int proc_nr = 0, ifreq, dfreq; if ((fp = fopen(CPUINFO, "r")) == NULL) return 0; st_pwr_cpufreq->cpufreq = 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "processor\t", 10)) { sscanf(strchr(line, ':') + 1, "%u", &proc_nr); if (proc_nr + 2 > nr_alloc) { cpu_read = -1; break; } } /* Entry in /proc/cpuinfo is different between Intel and Power architectures */ else if (!strncmp(line, "cpu MHz\t", 8) || !strncmp(line, "clock\t", 6)) { sscanf(strchr(line, ':') + 1, "%u.%u", &ifreq, &dfreq); /* Save current CPU frequency */ st_pwr_cpufreq_i = st_pwr_cpufreq + proc_nr + 1; st_pwr_cpufreq_i->cpufreq = ifreq * 100 + dfreq / 10; /* Also save it to compute an average CPU frequency */ st_pwr_cpufreq->cpufreq += st_pwr_cpufreq_i->cpufreq; nr++; if (proc_nr + 2 > cpu_read) { cpu_read = proc_nr + 2; } } } fclose(fp); if (nr) { /* Compute average CPU frequency for this machine */ st_pwr_cpufreq->cpufreq /= nr; } return cpu_read; } /* *************************************************************************** * Read hugepages statistics from /proc/meminfo. * * IN: * @st_huge Structure where stats will be saved. * * OUT: * @st_huge Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_meminfo_huge(struct stats_huge *st_huge) { FILE *fp; char line[128]; unsigned long szhkb = 0; if ((fp = fopen(MEMINFO, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, "HugePages_Total:", 16)) { /* Read the total number of huge pages */ sscanf(line + 16, "%llu", &st_huge->tlhkb); } else if (!strncmp(line, "HugePages_Free:", 15)) { /* Read the number of free huge pages */ 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 */ sscanf(line + 13, "%lu", &szhkb); } } fclose(fp); /* 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; } /* *************************************************************************** * Read CPU average frequencies statistics. * * IN: * @st_pwr_wghfreq Structure where stats will be saved. * @cpu_nr CPU number for which time_in_state date will be read. * @nbr Total number of states (frequencies). * * OUT: * @st_pwr_wghfreq Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ int read_time_in_state(struct stats_pwr_wghfreq *st_pwr_wghfreq, int cpu_nr, int nbr) { FILE *fp; struct stats_pwr_wghfreq *st_pwr_wghfreq_j; char filename[MAX_PF_NAME]; char line[128]; int j = 0; unsigned long freq; unsigned long long time_in_state; snprintf(filename, MAX_PF_NAME, "%s/cpu%d/%s", SYSFS_DEVCPU, cpu_nr, SYSFS_TIME_IN_STATE); if ((fp = fopen(filename, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { sscanf(line, "%lu %llu", &freq, &time_in_state); if (j < nbr) { /* Save current frequency and time */ st_pwr_wghfreq_j = st_pwr_wghfreq + j; st_pwr_wghfreq_j->freq = freq; st_pwr_wghfreq_j->time_in_state = time_in_state; j++; } } fclose(fp); return 1; } /* *************************************************************************** * Read weighted CPU frequency statistics. * * IN: * @st_pwr_wghfreq Structure where stats will be saved. * @nr_alloc Total number of structures allocated. Value is >= 0. * @nr2 Number of sub-items allocated per structure. * * OUT: * @st_pwr_wghfreq Structure with statistics. * * RETURNS: * Number of CPU for which statistics have been read. * 1 means CPU "all", 2 means CPU "all" and 0, etc. * Or -1 if the buffer was to small and needs to be reallocated. *************************************************************************** */ __nr_t read_cpu_wghfreq(struct stats_pwr_wghfreq *st_pwr_wghfreq, __nr_t nr_alloc, __nr_t nr2) { __nr_t cpu_read = 0; int j; struct stats_pwr_wghfreq *st_pwr_wghfreq_i, *st_pwr_wghfreq_j, *st_pwr_wghfreq_all_j; do { if (cpu_read + 2 > nr_alloc) return -1; /* Read current CPU time-in-state data */ st_pwr_wghfreq_i = st_pwr_wghfreq + (cpu_read + 1) * nr2; if (!read_time_in_state(st_pwr_wghfreq_i, cpu_read, nr2)) break; /* Also save data for CPU 'all' */ for (j = 0; j < nr2; j++) { st_pwr_wghfreq_j = st_pwr_wghfreq_i + j; /* CPU #cpu, state #j */ st_pwr_wghfreq_all_j = st_pwr_wghfreq + j; /* CPU #all, state #j */ if (!cpu_read) { /* Assume that possible frequencies are the same for all CPUs */ st_pwr_wghfreq_all_j->freq = st_pwr_wghfreq_j->freq; } st_pwr_wghfreq_all_j->time_in_state += st_pwr_wghfreq_j->time_in_state; } cpu_read++; } while (1); if (cpu_read > 0) { for (j = 0; j < nr2; j++) { st_pwr_wghfreq_all_j = st_pwr_wghfreq + j; /* CPU #all, state #j */ st_pwr_wghfreq_all_j->time_in_state /= cpu_read; } return cpu_read + 1; /* For CPU "all" */ } return 0; } /* *************************************************************************** * Read current USB device data. * * IN: * @st_pwr_usb Structure where stats will be saved. * @usb_device File name for current USB device. * * OUT: * @st_pwr_usb Structure with statistics. *************************************************************************** */ void read_usb_stats(struct stats_pwr_usb *st_pwr_usb, char *usb_device) { int l, rc; FILE *fp; char * rs; char filename[MAX_PF_NAME]; /* Get USB device bus number */ sscanf(usb_device, "%u", &st_pwr_usb->bus_nr); /* Read USB device vendor ID */ snprintf(filename, MAX_PF_NAME, "%s/%s/%s", SYSFS_USBDEV, usb_device, SYSFS_IDVENDOR); if ((fp = fopen(filename, "r")) != NULL) { rc = fscanf(fp, "%x", &st_pwr_usb->vendor_id); fclose(fp); if (rc == 0) { st_pwr_usb->vendor_id = 0; } } /* Read USB device product ID */ snprintf(filename, MAX_PF_NAME, "%s/%s/%s", SYSFS_USBDEV, usb_device, SYSFS_IDPRODUCT); if ((fp = fopen(filename, "r")) != NULL) { rc = fscanf(fp, "%x", &st_pwr_usb->product_id); fclose(fp); if (rc == 0) { st_pwr_usb->product_id = 0; } } /* Read USB device max power consumption */ snprintf(filename, MAX_PF_NAME, "%s/%s/%s", SYSFS_USBDEV, usb_device, SYSFS_BMAXPOWER); if ((fp = fopen(filename, "r")) != NULL) { rc = fscanf(fp, "%u", &st_pwr_usb->bmaxpower); fclose(fp); if (rc == 0) { st_pwr_usb->bmaxpower = 0; } } /* Read USB device manufacturer */ snprintf(filename, MAX_PF_NAME, "%s/%s/%s", SYSFS_USBDEV, usb_device, SYSFS_MANUFACTURER); if ((fp = fopen(filename, "r")) != NULL) { rs = fgets(st_pwr_usb->manufacturer, MAX_MANUF_LEN - 1, fp); fclose(fp); if ((rs != NULL) && (l = strlen(st_pwr_usb->manufacturer)) > 0) { /* Remove trailing CR */ st_pwr_usb->manufacturer[l - 1] = '\0'; } } /* Read USB device product */ snprintf(filename, MAX_PF_NAME, "%s/%s/%s", SYSFS_USBDEV, usb_device, SYSFS_PRODUCT); if ((fp = fopen(filename, "r")) != NULL) { rs = fgets(st_pwr_usb->product, MAX_PROD_LEN - 1, fp); fclose(fp); if ((rs != NULL) && (l = strlen(st_pwr_usb->product)) > 0) { /* Remove trailing CR */ st_pwr_usb->product[l - 1] = '\0'; } } } /* *************************************************************************** * Read USB devices statistics. * * IN: * @st_pwr_usb Structure where stats will be saved. * @nr_alloc Total number of structures allocated. Value is >= 0. * * OUT: * @st_pwr_usb Structure with statistics. * * RETURNS: * Number of USB devices read, or -1 if the buffer was too small and * needs to be reallocated. *************************************************************************** */ __nr_t read_bus_usb_dev(struct stats_pwr_usb *st_pwr_usb, __nr_t nr_alloc) { DIR *dir; struct dirent *drd; struct stats_pwr_usb *st_pwr_usb_i; __nr_t usb_read = 0; /* Open relevant /sys directory */ if ((dir = __opendir(SYSFS_USBDEV)) == NULL) return 0; /* Get current file entry */ while ((drd = __readdir(dir)) != NULL) { if (isdigit(drd->d_name[0]) && !strchr(drd->d_name, ':')) { if (usb_read + 1 > nr_alloc) { usb_read = -1; break; } /* Read current USB device data */ st_pwr_usb_i = st_pwr_usb + usb_read++; read_usb_stats(st_pwr_usb_i, drd->d_name); } } /* Close directory */ __closedir(dir); return usb_read; } /* *************************************************************************** * Read filesystems statistics. * * IN: * @st_filesystem Structure where stats will be saved. * @nr_alloc Total number of structures allocated. Value is >= 0. * * OUT: * @st_filesystem Structure with statistics. * * RETURNS: * Number of filesystems read, or -1 if the buffer was too small and * needs to be reallocated. *************************************************************************** */ __nr_t read_filesystem(struct stats_filesystem *st_filesystem, __nr_t nr_alloc) { FILE *fp; char line[512], fs_name[MAX_FS_LEN], mountp[256], type[128]; int skip = 0, skip_next = 0, fs; char *pos = 0, *pos2 = 0; __nr_t fs_read = 0; struct stats_filesystem *st_filesystem_i; struct statvfs buf; if ((fp = fopen(MTAB, "r")) == NULL) return 0; 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 current filesystem name */ sscanf(line, "%127s", fs_name); /* * And now read the corresponding mount point. * Read fs name and mount point in two distinct operations, * using '@pos + 1' position value for the mount point. * Indeed, if fs name length is greater than 127 chars, * previous scanf() would read only the first 127 chars, and * mount point name would be read using the remaining chars * from the fs name. This would result in a bogus name * and following statvfs() function would always fail. */ sscanf(pos + 1, "%255s", mountp); /* Replace octal codes */ oct2chr(mountp); /* * 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)) continue; /* Check if it's a duplicate entry */ fs = fs_read - 1; while (fs >= 0) { st_filesystem_i = st_filesystem + fs; if (!strcmp(st_filesystem_i->fs_name, fs_name)) break; fs--; } if (fs >= 0) /* Duplicate entry found! Ignore current entry */ continue; if (fs_read + 1 > nr_alloc) { fs_read = -1; break; } st_filesystem_i = st_filesystem + fs_read++; st_filesystem_i->f_blocks = (unsigned long long) buf.f_blocks * (unsigned long long) buf.f_frsize; st_filesystem_i->f_bfree = (unsigned long long) buf.f_bfree * (unsigned long long) buf.f_frsize; st_filesystem_i->f_bavail = (unsigned long long) buf.f_bavail * (unsigned long long) buf.f_frsize; st_filesystem_i->f_files = (unsigned long long) buf.f_files; st_filesystem_i->f_ffree = (unsigned long long) buf.f_ffree; strncpy(st_filesystem_i->fs_name, fs_name, sizeof(st_filesystem_i->fs_name)); st_filesystem_i->fs_name[sizeof(st_filesystem_i->fs_name) - 1] = '\0'; strncpy(st_filesystem_i->mountp, mountp, sizeof(st_filesystem_i->mountp)); st_filesystem_i->mountp[sizeof(st_filesystem_i->mountp) - 1] = '\0'; } } fclose(fp); return fs_read; } /* *************************************************************************** * Read Fibre Channel HBA statistics. * * IN: * @st_fc Structure where stats will be saved. * @nr_alloc Total number of structures allocated. Value is >= 0. * * OUT: * @st_fc Structure with statistics. * * RETURNS: * Number of FC hosts read, or -1 if the buffer was too small and needs to * be reallocated. *************************************************************************** */ __nr_t read_fchost(struct stats_fchost *st_fc, __nr_t nr_alloc) { DIR *dir; FILE *fp; struct dirent *drd; struct stats_fchost *st_fc_i; __nr_t fch_read = 0; char fcstat_filename[MAX_PF_NAME]; char line[256]; 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) 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) { rx_frames = tx_frames = rx_words = tx_words = 0; if (!strncmp(drd->d_name, "host", 4)) { if (fch_read + 1 > nr_alloc) { fch_read = -1; break; } snprintf(fcstat_filename, MAX_PF_NAME, FC_RX_FRAMES, SYSFS_FCHOST, drd->d_name); if ((fp = fopen(fcstat_filename, "r"))) { if (fgets(line, sizeof(line), fp)) { sscanf(line, "%lx", &rx_frames); } fclose(fp); } snprintf(fcstat_filename, MAX_PF_NAME, FC_TX_FRAMES, SYSFS_FCHOST, drd->d_name); if ((fp = fopen(fcstat_filename, "r"))) { if (fgets(line, sizeof(line), fp)) { sscanf(line, "%lx", &tx_frames); } fclose(fp); } snprintf(fcstat_filename, MAX_PF_NAME, FC_RX_WORDS, SYSFS_FCHOST, drd->d_name); if ((fp = fopen(fcstat_filename, "r"))) { if (fgets(line, sizeof(line), fp)) { sscanf(line, "%lx", &rx_words); } fclose(fp); } snprintf(fcstat_filename, MAX_PF_NAME, FC_TX_WORDS, SYSFS_FCHOST, drd->d_name); if ((fp = fopen(fcstat_filename, "r"))) { if (fgets(line, sizeof(line), fp)) { sscanf(line, "%lx", &tx_words); } fclose(fp); } st_fc_i = st_fc + fch_read++; st_fc_i->f_rxframes = rx_frames; st_fc_i->f_txframes = tx_frames; st_fc_i->f_rxwords = rx_words; st_fc_i->f_txwords = tx_words; memcpy(st_fc_i->fchost_name, drd->d_name, sizeof(st_fc_i->fchost_name)); st_fc_i->fchost_name[sizeof(st_fc_i->fchost_name) - 1] = '\0'; } } __closedir(dir); return fch_read; } /* *************************************************************************** * Read softnet statistics. * * 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: * 1 if stats have been sucessfully read, or 0 otherwise. * Returns -1 if the buffer was too small and needs to be reallocated. *************************************************************************** */ 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, st_softnet_read; char line[1024]; int cpu = 1, rc = 1, i, cpu_id; /* Open /proc/net/softnet_stat file */ if ((fp = fopen(NET_SOFTNET, "r")) == NULL) return 0; while (fgets(line, sizeof(line), fp) != NULL) { /* Softnet backlog length may be not available */ st_softnet_read.backlog_len = 0; i = sscanf(line, "%x %x %x %*x %*x %*x %*x %*x %*x %x %x %x %x", &(st_softnet_read.processed), &(st_softnet_read.dropped), &(st_softnet_read.time_squeeze), &(st_softnet_read.received_rps), &(st_softnet_read.flow_limit), &(st_softnet_read.backlog_len), &cpu_id); if (i == 7) { /* Corresponding CPU read in file */ cpu = cpu_id + 1; } else { /* cpu_id not present in file */ while ((!(online_cpu_bitmap[(cpu - 1) >> 3] & (1 << ((cpu - 1) & 0x07)))) && (cpu < nr_alloc)) { cpu++; } } if (cpu >= nr_alloc) { rc = -1; break; } st_softnet_i = st_softnet + cpu++; *st_softnet_i = st_softnet_read; } fclose(fp); return rc; } /* *************************************************************************** * Read pressure-stall information from a file located in /proc/pressure * directory. * * IN: * @st_psi Structure where stats will be saved. * @filename File located in /proc/pressure directory to read. * @token "some" or "full". Indicate which line shall be read in file. * * OUT: * @st_psi Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ int read_psi_stub(struct stats_psi *st_psi, char *filename, char *token) { FILE *fp; char line[8192]; unsigned long psi_tmp[3]; int rc = 0, len; if ((fp = fopen(filename, "r")) == NULL) return 0; len = strlen(token); while (fgets(line, sizeof(line), fp) != NULL) { if (!strncmp(line, token, len)) { /* Read stats */ rc = sscanf(line + len + 1, "avg10=%lu.%lu avg60=%lu.%lu avg300=%lu.%lu total=%llu", &psi_tmp[0], &st_psi->avg10, &psi_tmp[1], &st_psi->avg60, &psi_tmp[2], &st_psi->avg300, &st_psi->total); } } fclose(fp); if (rc < 7) return 0; st_psi->avg10 += psi_tmp[0] * 100; st_psi->avg60 += psi_tmp[1] * 100; st_psi->avg300 += psi_tmp[2] * 100; return 1; } /* *************************************************************************** * Read pressure-stall CPU information. * * IN: * @st_psi_cpu Structure where stats will be saved. * * OUT: * @st_psi_cpu Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_psicpu(struct stats_psi_cpu *st_psi_cpu) { struct stats_psi st_psi; /* Read CPU stats */ if (!read_psi_stub(&st_psi, PSI_CPU, "some")) return 0; st_psi_cpu->some_acpu_10 = st_psi.avg10; st_psi_cpu->some_acpu_60 = st_psi.avg60; st_psi_cpu->some_acpu_300 = st_psi.avg300; st_psi_cpu->some_cpu_total = st_psi.total; return 1; } /* *************************************************************************** * Read pressure-stall I/O information. * * IN: * @st_psi_io Structure where stats will be saved. * * OUT: * @st_psi_io Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_psiio(struct stats_psi_io *st_psi_io) { struct stats_psi st_psi; /* Read I/O "some" stats */ if (!read_psi_stub(&st_psi, PSI_IO, "some")) return 0; st_psi_io->some_aio_10 = st_psi.avg10; st_psi_io->some_aio_60 = st_psi.avg60; st_psi_io->some_aio_300 = st_psi.avg300; st_psi_io->some_io_total = st_psi.total; /* Read I/O "full" stats */ if (!read_psi_stub(&st_psi, PSI_IO, "full")) return 0; st_psi_io->full_aio_10 = st_psi.avg10; st_psi_io->full_aio_60 = st_psi.avg60; st_psi_io->full_aio_300 = st_psi.avg300; st_psi_io->full_io_total = st_psi.total; return 1; } /* *************************************************************************** * Read pressure-stall memory information. * * IN: * @st_psi_mem Structure where stats will be saved. * * OUT: * @st_psi_mem Structure with statistics. * * RETURNS: * 1 on success, 0 otherwise. *************************************************************************** */ __nr_t read_psimem(struct stats_psi_mem *st_psi_mem) { struct stats_psi st_psi; /* Read memory "some" stats */ if (!read_psi_stub(&st_psi, PSI_MEM, "some")) return 0; st_psi_mem->some_amem_10 = st_psi.avg10; st_psi_mem->some_amem_60 = st_psi.avg60; st_psi_mem->some_amem_300 = st_psi.avg300; st_psi_mem->some_mem_total = st_psi.total; /* Read memory "full" stats */ if (!read_psi_stub(&st_psi, PSI_MEM, "full")) return 0; st_psi_mem->full_amem_10 = st_psi.avg10; st_psi_mem->full_amem_60 = st_psi.avg60; st_psi_mem->full_amem_300 = st_psi.avg300; st_psi_mem->full_mem_total = st_psi.total; return 1; } /*------------------ END: FUNCTIONS USED BY SADC ONLY ---------------------*/ #endif /* SOURCE_SADC */