diff --git a/SOURCES/sysstat-10.1.5-int-handler.patch b/SOURCES/sysstat-10.1.5-int-handler.patch new file mode 100644 index 0000000..7a5f959 --- /dev/null +++ b/SOURCES/sysstat-10.1.5-int-handler.patch @@ -0,0 +1,27 @@ +diff -upr sysstat-10.1.5.orig/sadc.c sysstat-10.1.5/sadc.c +--- sysstat-10.1.5.orig/sadc.c 2013-03-23 17:31:46.000000000 +0100 ++++ sysstat-10.1.5/sadc.c 2016-04-29 17:52:11.438614928 +0200 +@@ -225,17 +225,19 @@ void alarm_handler(int sig) + */ + void int_handler(int sig) + { +- if (!optz) { +- /* sadc hasn't been called by sar */ ++ pid_t ppid = getppid(); ++ ++ if (!optz || (ppid == 1)) { ++ /* sadc hasn't been called by sar or sar process is already dead */ + exit(1); + } +- ++ + /* + * When starting sar then pressing ctrl/c, SIGINT is received + * by sadc, not sar. So send SIGINT to sar so that average stats + * can be displayed. + */ +- if (kill(getppid(), SIGINT) < 0) { ++ if (kill(ppid, SIGINT) < 0) { + exit(1); + } + } diff --git a/SOURCES/sysstat-10.1.5-max-cpus.patch b/SOURCES/sysstat-10.1.5-max-cpus.patch new file mode 100644 index 0000000..a593952 --- /dev/null +++ b/SOURCES/sysstat-10.1.5-max-cpus.patch @@ -0,0 +1,16 @@ +diff -upr sysstat-10.1.5.orig/common.h sysstat-10.1.5/common.h +--- sysstat-10.1.5.orig/common.h 2016-04-29 14:12:25.709765182 +0200 ++++ sysstat-10.1.5/common.h 2016-04-29 14:15:21.463578820 +0200 +@@ -28,10 +28,10 @@ + #define SEC_PER_DAY 3600 * 24 + + /* Maximum number of CPUs */ +-#if defined(__CPU_SETSIZE) && __CPU_SETSIZE > 2048 ++#if defined(__CPU_SETSIZE) && __CPU_SETSIZE > 8192 + #define NR_CPUS __CPU_SETSIZE + #else +-#define NR_CPUS 2048 ++#define NR_CPUS 8192 + #endif + + /* Maximum number of interrupts */ diff --git a/SOURCES/sysstat-10.1.5-max-name-len.patch b/SOURCES/sysstat-10.1.5-max-name-len.patch new file mode 100644 index 0000000..29fe860 --- /dev/null +++ b/SOURCES/sysstat-10.1.5-max-name-len.patch @@ -0,0 +1,35 @@ +diff -upr sysstat-10.1.5.orig/common.h sysstat-10.1.5/common.h +--- sysstat-10.1.5.orig/common.h 2016-04-29 14:29:24.298889634 +0200 ++++ sysstat-10.1.5/common.h 2016-04-29 14:30:10.589577176 +0200 +@@ -69,7 +69,7 @@ + #define MAX_FILE_LEN 256 + #define MAX_PF_NAME 1024 + #define DEFAULT_DEVMAP_MAJOR 253 +-#define MAX_NAME_LEN 72 ++#define MAX_NAME_LEN 128 + + #define IGNORE_VIRTUAL_DEVICES FALSE + #define ACCEPT_VIRTUAL_DEVICES TRUE +diff -upr sysstat-10.1.5.orig/iostat.c sysstat-10.1.5/iostat.c +--- sysstat-10.1.5.orig/iostat.c 2013-03-23 17:31:46.000000000 +0100 ++++ sysstat-10.1.5/iostat.c 2016-04-29 15:02:24.264492629 +0200 +@@ -748,7 +748,8 @@ void read_diskstats_stat(int curr) + * (if different from "nodev") works around known issues + * with EMC PowerPath. + */ +- strncpy(dev_name, ioc_dname, MAX_NAME_LEN); ++ strncpy(dev_name, ioc_dname, MAX_NAME_LEN - 1); ++ dev_name[MAX_NAME_LEN - 1] = '\0'; + } + } + +@@ -759,7 +760,8 @@ void read_diskstats_stat(int curr) + */ + dm_name = transform_devmapname(major, minor); + if (dm_name) { +- strncpy(dev_name, dm_name, MAX_NAME_LEN); ++ strncpy(dev_name, dm_name, MAX_NAME_LEN - 1); ++ dev_name[MAX_NAME_LEN - 1] = '\0'; + } + } + diff --git a/SOURCES/sysstat-10.1.5-nfsiostat.patch b/SOURCES/sysstat-10.1.5-nfsiostat.patch new file mode 100644 index 0000000..9e4a382 --- /dev/null +++ b/SOURCES/sysstat-10.1.5-nfsiostat.patch @@ -0,0 +1,2119 @@ +diff -uprN sysstat-10.1.5.orig/configure sysstat-10.1.5/configure +--- sysstat-10.1.5.orig/configure 2013-03-13 15:28:25.000000000 +0100 ++++ sysstat-10.1.5/configure 2016-05-24 18:25:16.209668309 +0200 +@@ -5388,7 +5388,7 @@ ac_config_files="$ac_config_files man/io + # File must be renamed + ac_config_files="$ac_config_files man/cifsiostat.1:man/cifsiostat.in" + # File must be renamed +-ac_config_files="$ac_config_files man/nfsiostat.1:man/nfsiostat.in" ++ac_config_files="$ac_config_files man/nfsiostat-sysstat.1:man/nfsiostat-sysstat.in" + # File must be renamed + ac_config_files="$ac_config_files contrib/isag/isag" + # Permissions must be changed +@@ -6120,7 +6120,7 @@ do + "man/sysstat.5") CONFIG_FILES="$CONFIG_FILES man/sysstat.5:man/sysstat.in" ;; + "man/iostat.1") CONFIG_FILES="$CONFIG_FILES man/iostat.1:man/iostat.in" ;; + "man/cifsiostat.1") CONFIG_FILES="$CONFIG_FILES man/cifsiostat.1:man/cifsiostat.in" ;; +- "man/nfsiostat.1") CONFIG_FILES="$CONFIG_FILES man/nfsiostat.1:man/nfsiostat.in" ;; ++ "man/nfsiostat-sysstat.1") CONFIG_FILES="$CONFIG_FILES man/nfsiostat-sysstat.1:man/nfsiostat-sysstat.in" ;; + "contrib/isag/isag") CONFIG_FILES="$CONFIG_FILES contrib/isag/isag" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + +diff -uprN sysstat-10.1.5.orig/configure.in sysstat-10.1.5/configure.in +--- sysstat-10.1.5.orig/configure.in 2013-03-13 15:28:17.000000000 +0100 ++++ sysstat-10.1.5/configure.in 2016-05-24 18:25:16.209668309 +0200 +@@ -580,7 +580,7 @@ AC_CONFIG_FILES([man/sar.1:man/sar.in]) + AC_CONFIG_FILES([man/sysstat.5:man/sysstat.in]) # File must be renamed + AC_CONFIG_FILES([man/iostat.1:man/iostat.in]) # File must be renamed + AC_CONFIG_FILES([man/cifsiostat.1:man/cifsiostat.in]) # File must be renamed +-AC_CONFIG_FILES([man/nfsiostat.1:man/nfsiostat.in]) # File must be renamed ++AC_CONFIG_FILES([man/nfsiostat-sysstat.1:man/nfsiostat-sysstat.in]) # File must be renamed + AC_CONFIG_FILES([contrib/isag/isag], [chmod +x contrib/isag/isag]) # Permissions must be changed + + AC_OUTPUT(Makefile) +diff -uprN sysstat-10.1.5.orig/Makefile.in sysstat-10.1.5/Makefile.in +--- sysstat-10.1.5.orig/Makefile.in 2016-05-24 18:33:48.438839524 +0200 ++++ sysstat-10.1.5/Makefile.in 2016-05-24 18:25:16.209668309 +0200 +@@ -160,7 +160,7 @@ NLSPOT= $(NLSPO:.po=.pot) + % : %.o + $(CC) -o $@ $(CFLAGS) $^ $(LFLAGS) + +-all: sadc sar sadf iostat mpstat pidstat nfsiostat cifsiostat locales ++all: sadc sar sadf iostat mpstat pidstat nfsiostat-sysstat cifsiostat locales + + common.o: common.c version.h common.h ioconf.h sysconfig.h + +@@ -229,9 +229,9 @@ mpstat.o: mpstat.c mpstat.h version.h co + + mpstat: mpstat.o librdstats.a libsyscom.a + +-nfsiostat.o: nfsiostat.c nfsiostat.h version.h common.h ++nfsiostat-sysstat.o: nfsiostat-sysstat.c nfsiostat-sysstat.h version.h common.h + +-nfsiostat: nfsiostat.o librdstats.a libsyscom.a ++nfsiostat-sysstat: nfsiostat-sysstat.o librdstats.a libsyscom.a + + cifsiostat.o: cifsiostat.c cifsiostat.h version.h common.h + +@@ -273,8 +273,8 @@ ifeq ($(INSTALL_DOC),y) + $(INSTALL_DATA) $(MANGRPARG) man/mpstat.1 $(DESTDIR)$(MAN1_DIR) + rm -f $(DESTDIR)$(MAN1_DIR)/pidstat.1* + $(INSTALL_DATA) $(MANGRPARG) man/pidstat.1 $(DESTDIR)$(MAN1_DIR) +- rm -f $(DESTDIR)$(MAN1_DIR)/nfsiostat.1* +- $(INSTALL_DATA) $(MANGRPARG) man/nfsiostat.1 $(DESTDIR)$(MAN1_DIR) ++ rm -f $(DESTDIR)$(MAN1_DIR)/nfsiostat-sysstat.1* ++ $(INSTALL_DATA) $(MANGRPARG) man/nfsiostat-sysstat.1 $(DESTDIR)$(MAN1_DIR) + rm -f $(DESTDIR)$(MAN1_DIR)/cifsiostat.1* + $(INSTALL_DATA) $(MANGRPARG) man/cifsiostat.1 $(DESTDIR)$(MAN1_DIR) + ifeq ($(INSTALL_ISAG),y) +@@ -290,7 +290,7 @@ ifeq ($(COMPRESS_MANPG),y) + $(ZIP) $(DESTDIR)$(MAN1_DIR)/iostat.1 + $(ZIP) $(DESTDIR)$(MAN1_DIR)/mpstat.1 + $(ZIP) $(DESTDIR)$(MAN1_DIR)/pidstat.1 +- $(ZIP) $(DESTDIR)$(MAN1_DIR)/nfsiostat.1 ++ $(ZIP) $(DESTDIR)$(MAN1_DIR)/nfsiostat-sysstat.1 + $(ZIP) $(DESTDIR)$(MAN1_DIR)/cifsiostat.1 + ifeq ($(INSTALL_ISAG),y) + $(ZIP) $(DESTDIR)$(MAN1_DIR)/isag.1 +@@ -330,7 +330,7 @@ endif + $(INSTALL_BIN) iostat $(DESTDIR)$(BIN_DIR) + $(INSTALL_BIN) mpstat $(DESTDIR)$(BIN_DIR) + $(INSTALL_BIN) pidstat $(DESTDIR)$(BIN_DIR) +- $(INSTALL_BIN) nfsiostat $(DESTDIR)$(BIN_DIR) ++ $(INSTALL_BIN) nfsiostat-sysstat $(DESTDIR)$(BIN_DIR) + $(INSTALL_BIN) cifsiostat $(DESTDIR)$(BIN_DIR) + ifeq ($(INSTALL_ISAG),y) + $(INSTALL_BIN) contrib/isag/isag $(DESTDIR)$(BIN_DIR) +@@ -397,7 +397,7 @@ ifeq ($(INSTALL_DOC),y) + rm -f $(DESTDIR)$(MAN1_DIR)/iostat.1* + rm -f $(DESTDIR)$(MAN1_DIR)/mpstat.1* + rm -f $(DESTDIR)$(MAN1_DIR)/pidstat.1* +- rm -f $(DESTDIR)$(MAN1_DIR)/nfsiostat.1* ++ rm -f $(DESTDIR)$(MAN1_DIR)/nfsiostat-sysstat.1* + rm -f $(DESTDIR)$(MAN1_DIR)/cifsiostat.1* + ifeq ($(INSTALL_ISAG),y) + rm -f $(DESTDIR)$(MAN1_DIR)/isag.1 +@@ -425,7 +425,7 @@ uninstall_base: uninstall_man uninstall_ + rm -f $(DESTDIR)$(BIN_DIR)/iostat + rm -f $(DESTDIR)$(BIN_DIR)/mpstat + rm -f $(DESTDIR)$(BIN_DIR)/pidstat +- rm -f $(DESTDIR)$(BIN_DIR)/nfsiostat ++ rm -f $(DESTDIR)$(BIN_DIR)/nfsiostat-sysstat + rm -f $(DESTDIR)$(BIN_DIR)/cifsiostat + ifeq ($(INSTALL_ISAG),y) + rm -f $(DESTDIR)$(BIN_DIR)/isag +@@ -487,7 +487,7 @@ po-files: + endif + + clean: +- rm -f sadc sar sadf iostat mpstat pidstat nfsiostat cifsiostat *.o *.a core TAGS ++ rm -f sadc sar sadf iostat mpstat pidstat nfsiostat-sysstat cifsiostat *.o *.a core TAGS + find nls -name "*.gmo" -exec rm -f {} \; + + almost-distclean: clean nls/sysstat.pot +@@ -496,7 +496,7 @@ almost-distclean: clean nls/sysstat.pot + rm -f cron/sysstat.cron.hourly cron/sysstat.crond.sample cron/sysstat.crond.sample.in + rm -f contrib/isag/isag + rm -f man/sa1.8 man/sa2.8 man/sadc.8 man/sadf.1 man/sar.1 man/iostat.1 man/sysstat.5 +- rm -f man/cifsiostat.1 man/nfsiostat.1 ++ rm -f man/cifsiostat.1 man/nfsiostat-sysstat.1 + rm -f *.log config.status + rm -rf autom4te.cache + rm -f *.save *.old .*.swp data +diff -uprN sysstat-10.1.5.orig/man/nfsiostat.in sysstat-10.1.5/man/nfsiostat.in +--- sysstat-10.1.5.orig/man/nfsiostat.in 2012-07-13 08:48:55.000000000 +0200 ++++ sysstat-10.1.5/man/nfsiostat.in 1970-01-01 01:00:00.000000000 +0100 +@@ -1,175 +0,0 @@ +-.TH NFSIOSTAT 1 "JULY 2012" Linux "Linux User's Manual" -*- nroff -*- +-.SH NAME +-nfsiostat \- Report input/output statistics for network filesystems (NFS). +-.SH SYNOPSIS +-.ie 'yes'@WITH_DEBUG@' \{ +-.B nfsiostat [ -h ] [ -k | -m ] [ -t ] [ -V ] [ --debuginfo ] [ +-.I interval +-.B [ +-.I count +-.B ] ] +-.\} +-.el \{ +-.B nfsiostat [ -h ] [ -k | -m ] [ -t ] [ -V ] [ +-.I interval +-.B [ +-.I count +-.B ] ] +-.\} +-.SH DESCRIPTION +-The +-.B nfsiostat +-command displays statistics about read and write operations +-on NFS filesystems. +- +-The +-.I interval +-parameter specifies the amount of time in seconds between +-each report. The first report contains statistics for the time since +-system startup (boot). Each subsequent report contains statistics +-collected during the interval since the previous report. +-A report consists of an NFS header row followed by +-a line of statistics for each network filesystem that is mounted. +-The +-.I count +-parameter can be specified in conjunction with the +-.I interval +-parameter. If the +-.I count +-parameter is specified, the value of +-.I count +-determines the number of reports generated at +-.I interval +-seconds apart. If the +-.I interval +-parameter is specified without the +-.I count +-parameter, the +-.B nfsiostat +-command generates reports continuously. +- +-.SH REPORT +-The Network Filesystem (NFS) report provides statistics for each mounted network filesystem. +-Transfer rates are shown in 1K blocks by default, unless the environment +-variable POSIXLY_CORRECT is set, in which case 512-byte blocks are used. +-The report shows the following fields: +- +-.B Filesystem: +-.RS +-This columns shows the hostname of the NFS server followed by a colon and +-by the directory name where the network filesystem is mounted. +- +-.RE +-.B rBlk_nor/s (rkB_nor/s, rMB_nor) +-.RS +-Indicate the number of blocks (kilobytes, megabytes) read by applications +-via the read(2) system +-call interface. A block has a size of 512 bytes. +- +-.RE +-.B wBlk_nor/s (wkB_nor/s, wMB_nor/s) +-.RS +-Indicate the number of blocks (kilobytes, megabytes) written by applications +-via the write(2) system +-call interface. +- +-.RE +-.B rBlk_dir/s (rkB_dir/s, rMB_dir/s) +-.RS +-Indicate the number of blocks (kilobytes, megabytes) read from files +-opened with the O_DIRECT flag. +- +-.RE +-.B wBlk_dir/s (wkB_dir/s, wMB_dir/s) +-.RS +-Indicate the number of blocks (kilobytes, megabytes) written to files +-opened with the O_DIRECT flag. +- +-.RE +-.B rBlk_svr/s (rkB_svr/s, rMB_svr/s) +-.RS +-Indicate the number of blocks (kilobytes, megabytes) read from the server +-by the NFS client via an NFS READ request. +- +-.RE +-.B wBlk_svr/s (wkB_svr/s, wMB_svr/s) +-.RS +-Indicate the number of blocks (kilobytes, megabytes) written to the server +-by the NFS client via an NFS WRITE request. +- +-.RE +-.B ops/s +-.RS +-Indicate the number of operations that were issued to the filesystem per second. +- +-.RE +-.B rops/s +-.RS +-Indicate the number of 'read' operations that were issued to the filesystem +-per second. +- +-.RE +-.B wops/s +-.RS +-Indicate the number of 'write' operations that were issued to the filesystem +-per second. +-.RE +-.RE +-.SH OPTIONS +-.if 'yes'@WITH_DEBUG@' \{ +-.IP --debuginfo +-Print debug output to stderr. +-.\} +-.IP -h +-Make the NFS report easier to read by a human. +-.IP -k +-Display statistics in kilobytes per second. +-.IP -m +-Display statistics in megabytes per second. +-.IP -t +-Print the time for each report displayed. The timestamp format may depend +-on the value of the S_TIME_FORMAT environment variable (see below). +-.IP -V +-Print version number then exit. +- +-.SH ENVIRONMENT +-The +-.B nfsiostat +-command takes into account the following environment variables: +- +-.IP S_TIME_FORMAT +-If this variable exists and its value is +-.BR ISO +-then the current locale will be ignored when printing the date in the report +-header. The +-.B nfsiostat +-command will use the ISO 8601 format (YYYY-MM-DD) instead. +-The timestamp displayed with option -t will also be compliant with ISO 8601 +-format. +- +-.IP POSIXLY_CORRECT +-When this variable is set, transfer rates are shown in 512-byte blocks instead +-of the default 1K blocks. +- +-.SH BUG +-.I /proc +-filesystem must be mounted for +-.B nfsiostat +-to work. +- +-.SH FILE +-.I /proc/self/mountstats +-contains statistics for network filesystems. +-.SH AUTHORS +-Written by Ivana Varekova (varekova redhat.com) +- +-Maintained by Sebastien Godard (sysstat orange.fr) +-.SH SEE ALSO +-.BR sar (1), +-.BR pidstat (1), +-.BR mpstat (1), +-.BR vmstat (8), +-.BR iostat (1), +-.BR cifsiostat (1) +- +-.I http://pagesperso-orange.fr/sebastien.godard/ +diff -uprN sysstat-10.1.5.orig/man/nfsiostat-sysstat.in sysstat-10.1.5/man/nfsiostat-sysstat.in +--- sysstat-10.1.5.orig/man/nfsiostat-sysstat.in 1970-01-01 01:00:00.000000000 +0100 ++++ sysstat-10.1.5/man/nfsiostat-sysstat.in 2016-05-24 18:25:16.209668309 +0200 +@@ -0,0 +1,187 @@ ++.TH NFSIOSTAT-SYSSTAT 1 "JANUARY 2014" Linux "Linux User's Manual" -*- nroff -*- ++.SH NAME ++nfsiostat-sysstat (the nfsiostat command from the sysstat package) \- Report input/output statistics for network filesystems (NFS). ++.SH SYNOPSIS ++.ie 'yes'@WITH_DEBUG@' \{ ++.B nfsiostat-sysstat [ -h ] [ -k | -m ] [ -t ] [ -V ] [ --debuginfo ] [ ++.I interval ++.B [ ++.I count ++.B ] ] ++.\} ++.el \{ ++.B nfsiostat-sysstat [ -h ] [ -k | -m ] [ -t ] [ -V ] [ ++.I interval ++.B [ ++.I count ++.B ] ] ++.\} ++.SH DESCRIPTION ++The ++.B nfsiostat-sysstat ++command displays statistics about read and write operations ++on NFS filesystems. ++ ++The ++.I interval ++parameter specifies the amount of time in seconds between ++each report. The first report contains statistics for the time since ++system startup (boot). Each subsequent report contains statistics ++collected during the interval since the previous report. ++A report consists of an NFS header row followed by ++a line of statistics for each network filesystem that is mounted. ++The ++.I count ++parameter can be specified in conjunction with the ++.I interval ++parameter. If the ++.I count ++parameter is specified, the value of ++.I count ++determines the number of reports generated at ++.I interval ++seconds apart. If the ++.I interval ++parameter is specified without the ++.I count ++parameter, the ++.B nfsiostat-sysstat ++command generates reports continuously. ++ ++.SH REPORT ++The Network Filesystem (NFS) report provides statistics for each mounted network filesystem. ++Transfer rates are shown in 1K blocks by default, unless the environment ++variable POSIXLY_CORRECT is set, in which case 512-byte blocks are used. ++The report shows the following fields: ++ ++.B Filesystem: ++.RS ++This columns shows the hostname of the NFS server followed by a colon and ++by the directory name where the network filesystem is mounted. ++ ++.RE ++.B rBlk_nor/s (rkB_nor/s, rMB_nor) ++.RS ++Indicate the number of blocks (kilobytes, megabytes) read by applications ++via the read(2) system ++call interface. A block has a size of 512 bytes. ++ ++.RE ++.B wBlk_nor/s (wkB_nor/s, wMB_nor/s) ++.RS ++Indicate the number of blocks (kilobytes, megabytes) written by applications ++via the write(2) system ++call interface. ++ ++.RE ++.B rBlk_dir/s (rkB_dir/s, rMB_dir/s) ++.RS ++Indicate the number of blocks (kilobytes, megabytes) read from files ++opened with the O_DIRECT flag. ++ ++.RE ++.B wBlk_dir/s (wkB_dir/s, wMB_dir/s) ++.RS ++Indicate the number of blocks (kilobytes, megabytes) written to files ++opened with the O_DIRECT flag. ++ ++.RE ++.B rBlk_svr/s (rkB_svr/s, rMB_svr/s) ++.RS ++Indicate the number of blocks (kilobytes, megabytes) read from the server ++by the NFS client via an NFS READ request. ++ ++.RE ++.B wBlk_svr/s (wkB_svr/s, wMB_svr/s) ++.RS ++Indicate the number of blocks (kilobytes, megabytes) written to the server ++by the NFS client via an NFS WRITE request. ++ ++.RE ++.B ops/s ++.RS ++Indicate the number of operations that were issued to the filesystem per second. ++ ++.RE ++.B rops/s ++.RS ++Indicate the number of 'read' operations that were issued to the filesystem ++per second. ++ ++.RE ++.B wops/s ++.RS ++Indicate the number of 'write' operations that were issued to the filesystem ++per second. ++.RE ++.RE ++.SH OPTIONS ++.if 'yes'@WITH_DEBUG@' \{ ++.IP --debuginfo ++Print debug output to stderr. ++.\} ++.IP -h ++Make the NFS report easier to read by a human. ++.IP -k ++Display statistics in kilobytes per second. ++.IP -m ++Display statistics in megabytes per second. ++.IP -t ++Print the time for each report displayed. The timestamp format may depend ++on the value of the S_TIME_FORMAT environment variable (see below). ++.IP -V ++Print version number then exit. ++ ++.SH ENVIRONMENT ++The ++.B nfsiostat-sysstat ++command takes into account the following environment variables: ++ ++.IP S_TIME_FORMAT ++If this variable exists and its value is ++.BR ISO ++then the current locale will be ignored when printing the date in the report ++header. The ++.B nfsiostat-sysstat ++command will use the ISO 8601 format (YYYY-MM-DD) instead. ++The timestamp displayed with option -t will also be compliant with ISO 8601 ++format. ++ ++.IP POSIXLY_CORRECT ++When this variable is set, transfer rates are shown in 512-byte blocks instead ++of the default 1K blocks. ++ ++.SH BUG ++.I /proc ++filesystem must be mounted for ++.B nfsiostat-sysstat ++to work. ++ ++.SH FILE ++.I /proc/self/mountstats ++contains statistics for network filesystems. ++ ++.SH WARNING ++The nfsiostat ++command from the sysstat package (nfsiostat-sysstat) is now obsolete and is no longer maintained. ++It will be removed in a future sysstat version. ++Please use now the ++.B nfsiostat ++command from the ++.I nfs-utils ++package. ++ ++.SH AUTHORS ++Written by Ivana Varekova (varekova redhat.com) ++ ++Maintained by Sebastien Godard (sysstat orange.fr) ++.SH SEE ALSO ++.BR nfsiostat (8), ++.BR sar (1), ++.BR pidstat (1), ++.BR mpstat (1), ++.BR vmstat (8), ++.BR iostat (1), ++.BR cifsiostat (1) ++ ++.I http://pagesperso-orange.fr/sebastien.godard/ +diff -uprN sysstat-10.1.5.orig/nfsiostat.c sysstat-10.1.5/nfsiostat.c +--- sysstat-10.1.5.orig/nfsiostat.c 2016-05-24 18:33:48.444839516 +0200 ++++ sysstat-10.1.5/nfsiostat.c 1970-01-01 01:00:00.000000000 +0100 +@@ -1,747 +0,0 @@ +-/* +- * nfsiostat: Report NFS I/O statistics +- * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved +- * Written by Ivana Varekova +- * +- *************************************************************************** +- * 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., * +- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * +- *************************************************************************** +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include "version.h" +-#include "nfsiostat.h" +-#include "common.h" +- +-#ifdef USE_NLS +-#include +-#include +-#define _(string) gettext(string) +-#else +-#define _(string) (string) +-#endif +- +-#define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__ +-char *sccsid(void) { return (SCCSID); } +- +-unsigned long long uptime0[2] = {0, 0}; +-struct io_nfs_stats *st_ionfs[2]; +-struct io_hdr_stats *st_hdr_ionfs; +- +-int ionfs_nr = 0; /* Nb of NFS mounted directories found */ +-int cpu_nr = 0; /* Nb of processors on the machine */ +-int flags = 0; /* Flag for common options and system state */ +- +-long interval = 0; +-char timestamp[64]; +- +-struct sigaction alrm_act; +- +-/* +- *************************************************************************** +- * Print usage and exit. +- * +- * IN: +- * @progname Name of sysstat command. +- *************************************************************************** +- */ +-void usage(char *progname) +-{ +- fprintf(stderr, _("Usage: %s [ options ] [ [ ] ]\n"), +- progname); +- +-#ifdef DEBUG +- fprintf(stderr, _("Options are:\n" +- "[ -h ] [ -k | -m ] [ -t ] [ -V ] [ --debuginfo ]\n")); +-#else +- fprintf(stderr, _("Options are:\n" +- "[ -h ] [ -k | -m ] [ -t ] [ -V ]\n")); +-#endif +- exit(1); +-} +- +-/* +- *************************************************************************** +- * Set output unit. Unit will be kB/s unless POSIXLY_CORRECT +- * environment variable has been set, in which case the output will be +- * expressed in blocks/s. +- *************************************************************************** +- */ +-void set_output_unit(void) +-{ +- char *e; +- +- if (DISPLAY_KILOBYTES(flags) || DISPLAY_MEGABYTES(flags)) +- return; +- +- /* Check POSIXLY_CORRECT environment variable */ +- if ((e = getenv(ENV_POSIXLY_CORRECT)) == NULL) { +- /* Variable not set: Unit is kB/s and not blocks/s */ +- flags |= I_D_KILOBYTES; +- } +-} +- +-/* +- *************************************************************************** +- * SIGALRM signal handler. +- * +- * IN: +- * @sig Signal number. +- *************************************************************************** +- */ +-void alarm_handler(int sig) +-{ +- alarm(interval); +-} +- +-/* +- *************************************************************************** +- * Find number of NFS-mounted points that are registered in +- * /proc/self/mountstats. +- * +- * RETURNS: +- * Number of NFS-mounted points. +- *************************************************************************** +- */ +-int get_nfs_mount_nr(void) +-{ +- FILE *fp; +- char line[8192]; +- char type_name[10]; +- unsigned int nfs = 0; +- +- if ((fp = fopen(NFSMOUNTSTATS, "r")) == NULL) +- /* File non-existent */ +- return 0; +- +- while (fgets(line, 8192, fp) != NULL) { +- +- if ((strstr(line, "mounted")) && (strstr(line, "on")) && +- (strstr(line, "with")) && (strstr(line, "fstype"))) { +- +- sscanf(strstr(line, "fstype") + 6, "%9s", type_name); +- if ((!strncmp(type_name, "nfs", 3)) && (strncmp(type_name, "nfsd", 4))) { +- nfs ++; +- } +- } +- } +- +- fclose(fp); +- +- return nfs; +-} +- +-/* +- *************************************************************************** +- * Set every nfs_io entry to inactive state (unregistered). +- *************************************************************************** +- */ +-void set_entries_inactive(void) +-{ +- int i; +- struct io_hdr_stats *shi = st_hdr_ionfs; +- +- for (i = 0; i < ionfs_nr; i++, shi++) { +- shi->active = FALSE; +- } +-} +- +-/* +- *************************************************************************** +- * Free inactive entries (mark them as unused). +- *************************************************************************** +- */ +-void free_inactive_entries(void) +-{ +- int i; +- struct io_hdr_stats *shi = st_hdr_ionfs; +- +- for (i = 0; i < ionfs_nr; i++, shi++) { +- if (!shi->active) { +- shi->used = FALSE; +- } +- } +-} +- +-/* +- *************************************************************************** +- * Allocate and init structures, according to system state. +- *************************************************************************** +- */ +-void io_sys_init(void) +-{ +- int i; +- +- /* How many processors on this machine? */ +- cpu_nr = get_cpu_nr(~0); +- +- /* Get number of NFS directories in /proc/self/mountstats */ +- if ((ionfs_nr = get_nfs_mount_nr()) > 0) { +- ionfs_nr += NR_NFS_PREALLOC; +- } +- if ((st_hdr_ionfs = (struct io_hdr_stats *) calloc(ionfs_nr, IO_HDR_STATS_SIZE)) == NULL) { +- perror("malloc"); +- exit(4); +- } +- +- /* Allocate structures for number of NFS directories found */ +- for (i = 0; i < 2; i++) { +- if ((st_ionfs[i] = +- (struct io_nfs_stats *) calloc(ionfs_nr, IO_NFS_STATS_SIZE)) == NULL) { +- perror("malloc"); +- exit(4); +- } +- } +-} +- +-/* +- *************************************************************************** +- * Free various structures. +- *************************************************************************** +-*/ +-void io_sys_free(void) +-{ +- int i; +- +- /* Free I/O NFS directories structures */ +- for (i = 0; i < 2; i++) { +- +- if (st_ionfs[i]) { +- free(st_ionfs[i]); +- } +- } +- +- if (st_hdr_ionfs) { +- free(st_hdr_ionfs); +- } +-} +- +-/* +- *************************************************************************** +- * Save stats for current NFS filesystem. +- * +- * IN: +- * @name Name of NFS filesystem. +- * @curr Index in array for current sample statistics. +- * @st_io Structure with NFS statistics to save. +- * @ionfs_nr Number of NFS filesystems. +- * @st_hdr_ionfs Pointer on structures describing an NFS filesystem. +- * +- * OUT: +- * @st_hdr_ionfs Pointer on structures describing an NFS filesystem. +- *************************************************************************** +- */ +-void save_stats(char *name, int curr, void *st_io) +-{ +- int i, j; +- struct io_hdr_stats *st_hdr_ionfs_i; +- struct io_nfs_stats *st_ionfs_i; +- +- /* Look for NFS directory in data table */ +- for (i = 0; i < ionfs_nr; i++) { +- st_hdr_ionfs_i = st_hdr_ionfs + i; +- if ((st_hdr_ionfs_i->used) && +- (!strcmp(st_hdr_ionfs_i->name, name))) { +- break; +- } +- } +- +- if (i == ionfs_nr) { +- /* +- * This is a new filesystem: Look for an unused entry to store it. +- */ +- for (i = 0; i < ionfs_nr; i++) { +- st_hdr_ionfs_i = st_hdr_ionfs + i; +- if (!st_hdr_ionfs_i->used) { +- /* Unused entry found... */ +- st_hdr_ionfs_i->used = TRUE; /* Indicate it is now used */ +- st_hdr_ionfs_i->active = TRUE; +- +- strcpy(st_hdr_ionfs_i->name, name); +- st_ionfs_i = st_ionfs[curr] + i; +- memset(st_ionfs_i, 0, IO_NFS_STATS_SIZE); +- *st_ionfs_i = *((struct io_nfs_stats *) st_io); +- break; +- } +- } +- if (i == ionfs_nr) { +- /* All entries are used: The number has to be increased */ +- ionfs_nr = ionfs_nr + 5; +- +- /* Increase the size of st_hdr_ionfs buffer */ +- if ((st_hdr_ionfs = (struct io_hdr_stats *) +- realloc(st_hdr_ionfs, ionfs_nr * IO_HDR_STATS_SIZE)) == NULL) { +- perror("malloc"); +- exit(4); +- } +- +- /* Set the new entries inactive */ +- for (j = 0; j < 5; j++) { +- st_hdr_ionfs_i = st_hdr_ionfs + i + j; +- st_hdr_ionfs_i->used = FALSE; +- st_hdr_ionfs_i->active = FALSE; +- } +- +- /* Increase the size of st_hdr_ionfs buffer */ +- for (j = 0; j < 2; j++) { +- if ((st_ionfs[j] = (struct io_nfs_stats *) +- realloc(st_ionfs[j], ionfs_nr * IO_NFS_STATS_SIZE)) == NULL) { +- perror("malloc"); +- exit(4); +- } +- memset(st_ionfs[j] + i, 0, 5 * IO_NFS_STATS_SIZE); +- } +- +- /* Now i shows the first unused entry of the new block */ +- st_hdr_ionfs_i = st_hdr_ionfs + i; +- st_hdr_ionfs_i->used = TRUE; /* Indicate it is now used */ +- strcpy(st_hdr_ionfs_i->name, name); +- st_ionfs_i = st_ionfs[curr] + i; +- memset(st_ionfs_i, 0, IO_NFS_STATS_SIZE); +- } +- } else { +- st_hdr_ionfs_i = st_hdr_ionfs + i; +- st_hdr_ionfs_i->used = TRUE; +- st_hdr_ionfs_i->active = TRUE; +- st_ionfs_i = st_ionfs[curr] + i; +- *st_ionfs_i = *((struct io_nfs_stats *) st_io); +- } +- /* +- * else it was a new NFS directory +- * but there was no free structure to store it. +- */ +-} +- +-/* +- *************************************************************************** +- * Read NFS-mount directories stats from /proc/self/mountstats. +- * +- * IN: +- * @curr Index in array for current sample statistics. +- *************************************************************************** +- */ +-void read_nfs_stat(int curr) +-{ +- FILE *fp; +- int sw = 0; +- char line[256]; +- char *xprt_line; +- char *mount_part; +- char nfs_name[MAX_NAME_LEN]; +- char mount[10], on[10], prefix[10], aux[32]; +- char operation[16]; +- struct io_nfs_stats snfs; +- long int v1; +- +- /* Every I/O NFS entry is potentially unregistered */ +- set_entries_inactive(); +- +- if ((fp = fopen(NFSMOUNTSTATS, "r")) == NULL) +- return; +- +- sprintf(aux, "%%%ds", +- MAX_NAME_LEN < 200 ? MAX_NAME_LEN-1 : 200); +- +- while (fgets(line, 256, fp) != NULL) { +- /* Read NFS directory name */ +- if (!strncmp(line, "device", 6)) { +- sw = 0; +- sscanf(line + 6, aux, nfs_name); +- mount_part = strchr(line + 7, ' '); +- if (mount_part != NULL) { +- sscanf(mount_part, "%9s %9s", mount, on); +- if ((!strncmp(mount, "mounted", 7)) && (!strncmp(on, "on", 2))) { +- sw = 1; +- } +- } +- } +- +- sscanf(line, "%9s", prefix); +- if (sw && (!strncmp(prefix, "bytes:", 6))) { +- /* Read the stats for the last NFS-mounted directory */ +- sscanf(strstr(line, "bytes:") + 6, "%llu %llu %llu %llu %llu %llu", +- &snfs.rd_normal_bytes, &snfs.wr_normal_bytes, +- &snfs.rd_direct_bytes, &snfs.wr_direct_bytes, +- &snfs.rd_server_bytes, &snfs.wr_server_bytes); +- sw = 2; +- } +- +- if ((sw == 2) && (!strncmp(prefix, "xprt:", 5))) { +- /* +- * Read extended statistic for the last NFS-mounted directory +- * - number of sent rpc requests. +- */ +- xprt_line = (strstr(line, "xprt:") + 6); +- /* udp, tcp or rdma data */ +- if (!strncmp(xprt_line, "udp", 3)) { +- /* port bind_count sends recvs (bad_xids req_u bklog_u) */ +- sscanf(strstr(xprt_line, "udp") + 4, "%*u %*u %lu", +- &snfs.rpc_sends); +- } +- if (!strncmp(xprt_line, "tcp", 3)) { +- /* +- * port bind_counter connect_count connect_time idle_time +- * sends recvs (bad_xids req_u bklog_u) +- */ +- sscanf(strstr(xprt_line, "tcp") + 4, +- "%*u %*u %*u %*u %*d %lu", +- &snfs.rpc_sends); +- } +- if (!strncmp(xprt_line,"rdma", 4)) { +- /* +- * 0(port) bind_count connect_count connect_time idle_time +- * sends recvs (bad_xids req_u bklog_u...) +- */ +- sscanf(strstr(xprt_line, "rdma") + 5, +- "%*u %*u %*u %*u %*d %lu", +- &snfs.rpc_sends); +- } +- sw = 3; +- } +- +- if ((sw == 3) && (!strncmp(prefix, "per-op", 6))) { +- sw = 4; +- while (sw == 4) { +- fgets(line, 256, fp); +- sscanf(line, "%15s %lu", operation, &v1); +- if (!strncmp(operation, "READ:", 5)) { +- snfs.nfs_rops = v1; +- } +- else if (!strncmp(operation, "WRITE:", 6)) { +- snfs.nfs_wops = v1; +- +- save_stats(nfs_name, curr, &snfs); +- sw = 0; +- } +- } +- } +- } +- +- fclose(fp); +- +- /* Free structures corresponding to unregistered filesystems */ +- free_inactive_entries(); +-} +- +-/* +- *************************************************************************** +- * Display NFS stats header. +- * +- * OUT: +- * @fctr Conversion factor. +- *************************************************************************** +- */ +-void write_nfs_stat_header(int *fctr) +-{ +- printf("Filesystem: "); +- if (DISPLAY_KILOBYTES(flags)) { +- printf(" rkB_nor/s wkB_nor/s rkB_dir/s wkB_dir/s" +- " rkB_svr/s wkB_svr/s"); +- *fctr = 1024; +- } +- else if (DISPLAY_MEGABYTES(flags)) { +- printf(" rMB_nor/s wMB_nor/s rMB_dir/s wMB_dir/s" +- " rMB_svr/s wMB_svr/s"); +- *fctr = 1024 * 1024; +- } +- else { +- printf(" rBlk_nor/s wBlk_nor/s rBlk_dir/s wBlk_dir/s" +- " rBlk_svr/s wBlk_svr/s"); +- *fctr = 512; +- } +- printf(" ops/s rops/s wops/s\n"); +-} +- +-/* +- *************************************************************************** +- * Write NFS stats read from /proc/self/mountstats. +- * +- * IN: +- * @curr Index in array for current sample statistics. +- * @itv Interval of time. +- * @fctr Conversion factor. +- * @shi Structures describing the NFS filesystems. +- * @ioi Current sample statistics. +- * @ioj Previous sample statistics. +- *************************************************************************** +- */ +-void write_nfs_stat(int curr, unsigned long long itv, int fctr, +- struct io_hdr_stats *shi, struct io_nfs_stats *ioni, +- struct io_nfs_stats *ionj) +-{ +- if (DISPLAY_HUMAN_READ(flags)) { +- printf("%-22s\n%23s", shi->name, ""); +- } +- else { +- printf("%-22s ", shi->name); +- } +- printf("%12.2f %12.2f %12.2f %12.2f %12.2f %12.2f %9.2f %9.2f %9.2f\n", +- S_VALUE(ionj->rd_normal_bytes, ioni->rd_normal_bytes, itv) / fctr, +- S_VALUE(ionj->wr_normal_bytes, ioni->wr_normal_bytes, itv) / fctr, +- S_VALUE(ionj->rd_direct_bytes, ioni->rd_direct_bytes, itv) / fctr, +- S_VALUE(ionj->wr_direct_bytes, ioni->wr_direct_bytes, itv) / fctr, +- S_VALUE(ionj->rd_server_bytes, ioni->rd_server_bytes, itv) / fctr, +- S_VALUE(ionj->wr_server_bytes, ioni->wr_server_bytes, itv) / fctr, +- S_VALUE(ionj->rpc_sends, ioni->rpc_sends, itv), +- S_VALUE(ionj->nfs_rops, ioni->nfs_rops, itv), +- S_VALUE(ionj->nfs_wops, ioni->nfs_wops, itv)); +-} +- +-/* +- *************************************************************************** +- * Print everything now (stats and uptime). +- * +- * IN: +- * @curr Index in array for current sample statistics. +- * @rectime Current date and time. +- *************************************************************************** +- */ +-void write_stats(int curr, struct tm *rectime) +-{ +- int i, fctr = 1; +- unsigned long long itv; +- struct io_hdr_stats *shi; +- struct io_nfs_stats *ioni, *ionj; +- +- /* Test stdout */ +- TEST_STDOUT(STDOUT_FILENO); +- +- /* Print time stamp */ +- if (DISPLAY_TIMESTAMP(flags)) { +- if (DISPLAY_ISO(flags)) { +- strftime(timestamp, sizeof(timestamp), "%FT%T%z", rectime); +- } +- else { +- strftime(timestamp, sizeof(timestamp), "%x %X", rectime); +- } +- printf("%s\n", timestamp); +-#ifdef DEBUG +- if (DISPLAY_DEBUG(flags)) { +- fprintf(stderr, "%s\n", timestamp); +- } +-#endif +- } +- +- /* Interval of time, reduced to one processor */ +- itv = get_interval(uptime0[!curr], uptime0[curr]); +- +- shi = st_hdr_ionfs; +- +- /* Display NFS stats header */ +- write_nfs_stat_header(&fctr); +- +- for (i = 0; i < ionfs_nr; i++, shi++) { +- if (shi->used) { +- ioni = st_ionfs[curr] + i; +- ionj = st_ionfs[!curr] + i; +-#ifdef DEBUG +- if (DISPLAY_DEBUG(flags)) { +- /* Debug output */ +- fprintf(stderr, "name=%s itv=%llu fctr=%d ioni{ rd_normal_bytes=%llu " +- "wr_normal_bytes=%llu rd_direct_bytes=%llu wr_direct_bytes=%llu rd_server_bytes=%llu " +- "wr_server_bytes=%llu rpc_sends=%lu nfs_rops=%lu nfs_wops=%lu }\n", +- shi->name, itv, fctr, +- ioni->rd_normal_bytes, ioni->wr_normal_bytes, +- ioni->rd_direct_bytes, ioni->wr_direct_bytes, +- ioni->rd_server_bytes, ioni->wr_server_bytes, +- ioni->rpc_sends, +- ioni->nfs_rops, ioni->nfs_wops); +- } +-#endif +- write_nfs_stat(curr, itv, fctr, shi, ioni, ionj); +- } +- } +- printf("\n"); +-} +- +-/* +- *************************************************************************** +- * Main loop: Read stats from the relevant sources and display them. +- * +- * IN: +- * @count Number of lines of stats to print. +- * @rectime Current date and time. +- *************************************************************************** +- */ +-void rw_io_stat_loop(long int count, struct tm *rectime) +-{ +- int curr = 1; +- +- /* Don't buffer data if redirected to a pipe */ +- setbuf(stdout, NULL); +- +- do { +- /* Read system uptime (reduced to one processor) */ +- uptime0[curr] = 0; +- read_uptime(&(uptime0[curr])); +- if (!uptime0[curr]) +- /* Cannot read system uptime (/proc/uptime doesn't exist) */ +- exit(2); +- +- /* Read NFS directories stats */ +- read_nfs_stat(curr); +- +- /* Get time */ +- get_localtime(rectime, 0); +- +- /* Print results */ +- write_stats(curr, rectime); +- +- if (count > 0) { +- count--; +- } +- if (count) { +- curr ^= 1; +- pause(); +- } +- } +- while (count); +-} +- +-/* +- *************************************************************************** +- * Main entry to the nfsiostat program. +- *************************************************************************** +- */ +-int main(int argc, char **argv) +-{ +- int it = 0; +- int opt = 1; +- int i; +- long count = 1; +- struct utsname header; +- struct tm rectime; +- +-#ifdef USE_NLS +- /* Init National Language Support */ +- init_nls(); +-#endif +- +- /* Get HZ */ +- get_HZ(); +- +- /* Process args... */ +- while (opt < argc) { +- +-#ifdef DEBUG +- if (!strcmp(argv[opt], "--debuginfo")) { +- flags |= I_D_DEBUG; +- opt++; +- } else +-#endif +- if (!strncmp(argv[opt], "-", 1)) { +- for (i = 1; *(argv[opt] + i); i++) { +- +- switch (*(argv[opt] + i)) { +- +- case 'h': +- /* Display an easy-to-read NFS report */ +- flags |= I_D_HUMAN_READ; +- break; +- +- case 'k': +- if (DISPLAY_MEGABYTES(flags)) { +- usage(argv[0]); +- } +- /* Display stats in kB/s */ +- flags |= I_D_KILOBYTES; +- break; +- +- case 'm': +- if (DISPLAY_KILOBYTES(flags)) { +- usage(argv[0]); +- } +- /* Display stats in MB/s */ +- flags |= I_D_MEGABYTES; +- break; +- +- case 't': +- /* Display timestamp */ +- flags |= I_D_TIMESTAMP; +- break; +- +- case 'V': +- /* Print version number and exit */ +- print_version(); +- break; +- +- default: +- usage(argv[0]); +- } +- } +- opt++; +- } +- +- else if (!it) { +- interval = atol(argv[opt++]); +- if (interval < 0) { +- usage(argv[0]); +- } +- count = -1; +- it = 1; +- } +- +- else if (it > 0) { +- count = atol(argv[opt++]); +- if ((count < 1) || !interval) { +- usage(argv[0]); +- } +- it = -1; +- } +- else { +- usage(argv[0]); +- } +- } +- +- if (!interval) { +- count = 1; +- } +- +- /* Select output unit (kB/s or blocks/s) */ +- set_output_unit(); +- +- /* Init structures according to machine architecture */ +- io_sys_init(); +- +- get_localtime(&rectime, 0); +- +- /* Get system name, release number and hostname */ +- uname(&header); +- if (print_gal_header(&rectime, header.sysname, header.release, +- header.nodename, header.machine, cpu_nr)) { +- flags |= I_D_ISO; +- } +- printf("\n"); +- +- /* Set a handler for SIGALRM */ +- memset(&alrm_act, 0, sizeof(alrm_act)); +- alrm_act.sa_handler = (void *) alarm_handler; +- sigaction(SIGALRM, &alrm_act, NULL); +- alarm(interval); +- +- /* Main loop */ +- rw_io_stat_loop(count, &rectime); +- +- /* Free structures */ +- io_sys_free(); +- +- return 0; +-} +diff -uprN sysstat-10.1.5.orig/nfsiostat.h sysstat-10.1.5/nfsiostat.h +--- sysstat-10.1.5.orig/nfsiostat.h 2013-03-23 17:31:46.000000000 +0100 ++++ sysstat-10.1.5/nfsiostat.h 1970-01-01 01:00:00.000000000 +0100 +@@ -1,57 +0,0 @@ +-/* +- * nfsiostat: Report NFS I/O statistics +- * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved +- * Written by Ivana Varekova +- */ +- +-#ifndef _NFSIOSTAT_H +-#define _NFSIOSTAT_H +- +-#include "common.h" +- +-#define NFSMOUNTSTATS "/proc/self/mountstats" +- +-/* I_: iostat - D_: Display - F_: Flag */ +-#define I_D_TIMESTAMP 0x001 +-#define I_D_KILOBYTES 0x002 +-#define I_D_MEGABYTES 0x004 +-#define I_D_ISO 0x008 +-#define I_D_HUMAN_READ 0x010 +-#define I_D_DEBUG 0x020 +- +-#define DISPLAY_TIMESTAMP(m) (((m) & I_D_TIMESTAMP) == I_D_TIMESTAMP) +-#define DISPLAY_KILOBYTES(m) (((m) & I_D_KILOBYTES) == I_D_KILOBYTES) +-#define DISPLAY_MEGABYTES(m) (((m) & I_D_MEGABYTES) == I_D_MEGABYTES) +-#define DISPLAY_ISO(m) (((m) & I_D_ISO) == I_D_ISO) +-#define DISPLAY_HUMAN_READ(m) (((m) & I_D_HUMAN_READ) == I_D_HUMAN_READ) +-#define DISPLAY_DEBUG(m) (((m) & I_D_DEBUG) == I_D_DEBUG) +- +-/* Environment variable */ +-#define ENV_POSIXLY_CORRECT "POSIXLY_CORRECT" +- +-/* Preallocation constats */ +-#define NR_NFS_PREALLOC 2 +- +-struct io_nfs_stats { +- unsigned long long rd_normal_bytes __attribute__ ((aligned (8))); +- unsigned long long wr_normal_bytes __attribute__ ((packed)); +- unsigned long long rd_direct_bytes __attribute__ ((packed)); +- unsigned long long wr_direct_bytes __attribute__ ((packed)); +- unsigned long long rd_server_bytes __attribute__ ((packed)); +- unsigned long long wr_server_bytes __attribute__ ((packed)); +- unsigned long rpc_sends __attribute__ ((packed)); +- unsigned long nfs_rops __attribute__ ((packed)); +- unsigned long nfs_wops __attribute__ ((packed)); +-}; +- +-#define IO_NFS_STATS_SIZE (sizeof(struct io_nfs_stats)) +- +-struct io_hdr_stats { +- unsigned int active __attribute__ ((aligned (4))); +- unsigned int used __attribute__ ((packed)); +- char name[MAX_NAME_LEN]; +-}; +- +-#define IO_HDR_STATS_SIZE (sizeof(struct io_hdr_stats)) +- +-#endif /* _NFSIOSTAT_H */ +diff -uprN sysstat-10.1.5.orig/nfsiostat-sysstat.c sysstat-10.1.5/nfsiostat-sysstat.c +--- sysstat-10.1.5.orig/nfsiostat-sysstat.c 1970-01-01 01:00:00.000000000 +0100 ++++ sysstat-10.1.5/nfsiostat-sysstat.c 2016-05-24 18:30:06.285162523 +0200 +@@ -0,0 +1,750 @@ ++/* ++ * nfsiostat-sysstat: Report NFS I/O statistics ++ * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved ++ * Written by Ivana Varekova ++ * ++ *************************************************************************** ++ * 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., * ++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ++ *************************************************************************** ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "version.h" ++#include "nfsiostat-sysstat.h" ++#include "common.h" ++ ++#ifdef USE_NLS ++#include ++#include ++#define _(string) gettext(string) ++#else ++#define _(string) (string) ++#endif ++ ++#define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__ ++char *sccsid(void) { return (SCCSID); } ++ ++unsigned long long uptime0[2] = {0, 0}; ++struct io_nfs_stats *st_ionfs[2]; ++struct io_hdr_stats *st_hdr_ionfs; ++ ++int ionfs_nr = 0; /* Nb of NFS mounted directories found */ ++int cpu_nr = 0; /* Nb of processors on the machine */ ++int flags = 0; /* Flag for common options and system state */ ++ ++long interval = 0; ++char timestamp[64]; ++ ++struct sigaction alrm_act; ++ ++/* ++ *************************************************************************** ++ * Print usage and exit. ++ * ++ * IN: ++ * @progname Name of sysstat command. ++ *************************************************************************** ++ */ ++void usage(char *progname) ++{ ++ fprintf(stderr, _("Usage: %s [ options ] [ [ ] ]\n"), ++ progname); ++ ++#ifdef DEBUG ++ fprintf(stderr, _("Options are:\n" ++ "[ -h ] [ -k | -m ] [ -t ] [ -V ] [ --debuginfo ]\n")); ++#else ++ fprintf(stderr, _("Options are:\n" ++ "[ -h ] [ -k | -m ] [ -t ] [ -V ]\n")); ++#endif ++ exit(1); ++} ++ ++/* ++ *************************************************************************** ++ * Set output unit. Unit will be kB/s unless POSIXLY_CORRECT ++ * environment variable has been set, in which case the output will be ++ * expressed in blocks/s. ++ *************************************************************************** ++ */ ++void set_output_unit(void) ++{ ++ char *e; ++ ++ if (DISPLAY_KILOBYTES(flags) || DISPLAY_MEGABYTES(flags)) ++ return; ++ ++ /* Check POSIXLY_CORRECT environment variable */ ++ if ((e = getenv(ENV_POSIXLY_CORRECT)) == NULL) { ++ /* Variable not set: Unit is kB/s and not blocks/s */ ++ flags |= I_D_KILOBYTES; ++ } ++} ++ ++/* ++ *************************************************************************** ++ * SIGALRM signal handler. ++ * ++ * IN: ++ * @sig Signal number. ++ *************************************************************************** ++ */ ++void alarm_handler(int sig) ++{ ++ alarm(interval); ++} ++ ++/* ++ *************************************************************************** ++ * Find number of NFS-mounted points that are registered in ++ * /proc/self/mountstats. ++ * ++ * RETURNS: ++ * Number of NFS-mounted points. ++ *************************************************************************** ++ */ ++int get_nfs_mount_nr(void) ++{ ++ FILE *fp; ++ char line[8192]; ++ char type_name[10]; ++ unsigned int nfs = 0; ++ ++ if ((fp = fopen(NFSMOUNTSTATS, "r")) == NULL) ++ /* File non-existent */ ++ return 0; ++ ++ while (fgets(line, 8192, fp) != NULL) { ++ ++ if ((strstr(line, "mounted")) && (strstr(line, "on")) && ++ (strstr(line, "with")) && (strstr(line, "fstype"))) { ++ ++ sscanf(strstr(line, "fstype") + 6, "%9s", type_name); ++ if ((!strncmp(type_name, "nfs", 3)) && (strncmp(type_name, "nfsd", 4))) { ++ nfs ++; ++ } ++ } ++ } ++ ++ fclose(fp); ++ ++ return nfs; ++} ++ ++/* ++ *************************************************************************** ++ * Set every nfs_io entry to inactive state (unregistered). ++ *************************************************************************** ++ */ ++void set_entries_inactive(void) ++{ ++ int i; ++ struct io_hdr_stats *shi = st_hdr_ionfs; ++ ++ for (i = 0; i < ionfs_nr; i++, shi++) { ++ shi->active = FALSE; ++ } ++} ++ ++/* ++ *************************************************************************** ++ * Free inactive entries (mark them as unused). ++ *************************************************************************** ++ */ ++void free_inactive_entries(void) ++{ ++ int i; ++ struct io_hdr_stats *shi = st_hdr_ionfs; ++ ++ for (i = 0; i < ionfs_nr; i++, shi++) { ++ if (!shi->active) { ++ shi->used = FALSE; ++ } ++ } ++} ++ ++/* ++ *************************************************************************** ++ * Allocate and init structures, according to system state. ++ *************************************************************************** ++ */ ++void io_sys_init(void) ++{ ++ int i; ++ ++ /* How many processors on this machine? */ ++ cpu_nr = get_cpu_nr(~0); ++ ++ /* Get number of NFS directories in /proc/self/mountstats */ ++ if ((ionfs_nr = get_nfs_mount_nr()) > 0) { ++ ionfs_nr += NR_NFS_PREALLOC; ++ } ++ if ((st_hdr_ionfs = (struct io_hdr_stats *) calloc(ionfs_nr, IO_HDR_STATS_SIZE)) == NULL) { ++ perror("malloc"); ++ exit(4); ++ } ++ ++ /* Allocate structures for number of NFS directories found */ ++ for (i = 0; i < 2; i++) { ++ if ((st_ionfs[i] = ++ (struct io_nfs_stats *) calloc(ionfs_nr, IO_NFS_STATS_SIZE)) == NULL) { ++ perror("malloc"); ++ exit(4); ++ } ++ } ++} ++ ++/* ++ *************************************************************************** ++ * Free various structures. ++ *************************************************************************** ++*/ ++void io_sys_free(void) ++{ ++ int i; ++ ++ /* Free I/O NFS directories structures */ ++ for (i = 0; i < 2; i++) { ++ ++ if (st_ionfs[i]) { ++ free(st_ionfs[i]); ++ } ++ } ++ ++ if (st_hdr_ionfs) { ++ free(st_hdr_ionfs); ++ } ++} ++ ++/* ++ *************************************************************************** ++ * Save stats for current NFS filesystem. ++ * ++ * IN: ++ * @name Name of NFS filesystem. ++ * @curr Index in array for current sample statistics. ++ * @st_io Structure with NFS statistics to save. ++ * @ionfs_nr Number of NFS filesystems. ++ * @st_hdr_ionfs Pointer on structures describing an NFS filesystem. ++ * ++ * OUT: ++ * @st_hdr_ionfs Pointer on structures describing an NFS filesystem. ++ *************************************************************************** ++ */ ++void save_stats(char *name, int curr, void *st_io) ++{ ++ int i, j; ++ struct io_hdr_stats *st_hdr_ionfs_i; ++ struct io_nfs_stats *st_ionfs_i; ++ ++ /* Look for NFS directory in data table */ ++ for (i = 0; i < ionfs_nr; i++) { ++ st_hdr_ionfs_i = st_hdr_ionfs + i; ++ if ((st_hdr_ionfs_i->used) && ++ (!strcmp(st_hdr_ionfs_i->name, name))) { ++ break; ++ } ++ } ++ ++ if (i == ionfs_nr) { ++ /* ++ * This is a new filesystem: Look for an unused entry to store it. ++ */ ++ for (i = 0; i < ionfs_nr; i++) { ++ st_hdr_ionfs_i = st_hdr_ionfs + i; ++ if (!st_hdr_ionfs_i->used) { ++ /* Unused entry found... */ ++ st_hdr_ionfs_i->used = TRUE; /* Indicate it is now used */ ++ st_hdr_ionfs_i->active = TRUE; ++ ++ strncpy(st_hdr_ionfs_i->name, name, MAX_NAME_LEN - 1); ++ st_hdr_ionfs_i->name[MAX_NAME_LEN - 1] = '\0'; ++ st_ionfs_i = st_ionfs[curr] + i; ++ memset(st_ionfs_i, 0, IO_NFS_STATS_SIZE); ++ *st_ionfs_i = *((struct io_nfs_stats *) st_io); ++ break; ++ } ++ } ++ if (i == ionfs_nr) { ++ /* All entries are used: The number has to be increased */ ++ ionfs_nr = ionfs_nr + 5; ++ ++ /* Increase the size of st_hdr_ionfs buffer */ ++ if ((st_hdr_ionfs = (struct io_hdr_stats *) ++ realloc(st_hdr_ionfs, ionfs_nr * IO_HDR_STATS_SIZE)) == NULL) { ++ perror("malloc"); ++ exit(4); ++ } ++ ++ /* Set the new entries inactive */ ++ for (j = 0; j < 5; j++) { ++ st_hdr_ionfs_i = st_hdr_ionfs + i + j; ++ st_hdr_ionfs_i->used = FALSE; ++ st_hdr_ionfs_i->active = FALSE; ++ } ++ ++ /* Increase the size of st_hdr_ionfs buffer */ ++ for (j = 0; j < 2; j++) { ++ if ((st_ionfs[j] = (struct io_nfs_stats *) ++ realloc(st_ionfs[j], ionfs_nr * IO_NFS_STATS_SIZE)) == NULL) { ++ perror("malloc"); ++ exit(4); ++ } ++ memset(st_ionfs[j] + i, 0, 5 * IO_NFS_STATS_SIZE); ++ } ++ ++ /* Now i shows the first unused entry of the new block */ ++ st_hdr_ionfs_i = st_hdr_ionfs + i; ++ st_hdr_ionfs_i->used = TRUE; /* Indicate it is now used */ ++ strncpy(st_hdr_ionfs_i->name, name, MAX_NAME_LEN - 1); ++ st_hdr_ionfs_i->name[MAX_NAME_LEN - 1] = '\0'; ++ st_ionfs_i = st_ionfs[curr] + i; ++ memset(st_ionfs_i, 0, IO_NFS_STATS_SIZE); ++ } ++ } else { ++ st_hdr_ionfs_i = st_hdr_ionfs + i; ++ st_hdr_ionfs_i->used = TRUE; ++ st_hdr_ionfs_i->active = TRUE; ++ st_ionfs_i = st_ionfs[curr] + i; ++ *st_ionfs_i = *((struct io_nfs_stats *) st_io); ++ } ++ /* ++ * else it was a new NFS directory ++ * but there was no free structure to store it. ++ */ ++} ++ ++/* ++ *************************************************************************** ++ * Read NFS-mount directories stats from /proc/self/mountstats. ++ * ++ * IN: ++ * @curr Index in array for current sample statistics. ++ *************************************************************************** ++ */ ++void read_nfs_stat(int curr) ++{ ++ FILE *fp; ++ int sw = 0; ++ char line[256]; ++ char *xprt_line; ++ char *mount_part; ++ char nfs_name[MAX_NAME_LEN]; ++ char mount[10], on[10], prefix[10], aux[32]; ++ char operation[16]; ++ struct io_nfs_stats snfs; ++ long int v1; ++ ++ /* Every I/O NFS entry is potentially unregistered */ ++ set_entries_inactive(); ++ ++ if ((fp = fopen(NFSMOUNTSTATS, "r")) == NULL) ++ return; ++ ++ sprintf(aux, "%%%ds", ++ MAX_NAME_LEN < 200 ? MAX_NAME_LEN-1 : 200); ++ ++ while (fgets(line, 256, fp) != NULL) { ++ /* Read NFS directory name */ ++ if (!strncmp(line, "device", 6)) { ++ sw = 0; ++ sscanf(line + 6, aux, nfs_name); ++ mount_part = strchr(line + 7, ' '); ++ if (mount_part != NULL) { ++ sscanf(mount_part, "%9s %9s", mount, on); ++ if ((!strncmp(mount, "mounted", 7)) && (!strncmp(on, "on", 2))) { ++ sw = 1; ++ } ++ } ++ } ++ ++ sscanf(line, "%9s", prefix); ++ if (sw && (!strncmp(prefix, "bytes:", 6))) { ++ /* Read the stats for the last NFS-mounted directory */ ++ sscanf(strstr(line, "bytes:") + 6, "%llu %llu %llu %llu %llu %llu", ++ &snfs.rd_normal_bytes, &snfs.wr_normal_bytes, ++ &snfs.rd_direct_bytes, &snfs.wr_direct_bytes, ++ &snfs.rd_server_bytes, &snfs.wr_server_bytes); ++ sw = 2; ++ } ++ ++ if ((sw == 2) && (!strncmp(prefix, "xprt:", 5))) { ++ /* ++ * Read extended statistic for the last NFS-mounted directory ++ * - number of sent rpc requests. ++ */ ++ xprt_line = (strstr(line, "xprt:") + 6); ++ /* udp, tcp or rdma data */ ++ if (!strncmp(xprt_line, "udp", 3)) { ++ /* port bind_count sends recvs (bad_xids req_u bklog_u) */ ++ sscanf(strstr(xprt_line, "udp") + 4, "%*u %*u %lu", ++ &snfs.rpc_sends); ++ } ++ if (!strncmp(xprt_line, "tcp", 3)) { ++ /* ++ * port bind_counter connect_count connect_time idle_time ++ * sends recvs (bad_xids req_u bklog_u) ++ */ ++ sscanf(strstr(xprt_line, "tcp") + 4, ++ "%*u %*u %*u %*u %*d %lu", ++ &snfs.rpc_sends); ++ } ++ if (!strncmp(xprt_line,"rdma", 4)) { ++ /* ++ * 0(port) bind_count connect_count connect_time idle_time ++ * sends recvs (bad_xids req_u bklog_u...) ++ */ ++ sscanf(strstr(xprt_line, "rdma") + 5, ++ "%*u %*u %*u %*u %*d %lu", ++ &snfs.rpc_sends); ++ } ++ sw = 3; ++ } ++ ++ if ((sw == 3) && (!strncmp(prefix, "per-op", 6))) { ++ sw = 4; ++ while (sw == 4) { ++ if (fgets(line, sizeof(line), fp) == NULL) ++ break; ++ sscanf(line, "%15s %lu", operation, &v1); ++ if (!strncmp(operation, "READ:", 5)) { ++ snfs.nfs_rops = v1; ++ } ++ else if (!strncmp(operation, "WRITE:", 6)) { ++ snfs.nfs_wops = v1; ++ ++ save_stats(nfs_name, curr, &snfs); ++ sw = 0; ++ } ++ } ++ } ++ } ++ ++ fclose(fp); ++ ++ /* Free structures corresponding to unregistered filesystems */ ++ free_inactive_entries(); ++} ++ ++/* ++ *************************************************************************** ++ * Display NFS stats header. ++ * ++ * OUT: ++ * @fctr Conversion factor. ++ *************************************************************************** ++ */ ++void write_nfs_stat_header(int *fctr) ++{ ++ printf("Filesystem: "); ++ if (DISPLAY_KILOBYTES(flags)) { ++ printf(" rkB_nor/s wkB_nor/s rkB_dir/s wkB_dir/s" ++ " rkB_svr/s wkB_svr/s"); ++ *fctr = 1024; ++ } ++ else if (DISPLAY_MEGABYTES(flags)) { ++ printf(" rMB_nor/s wMB_nor/s rMB_dir/s wMB_dir/s" ++ " rMB_svr/s wMB_svr/s"); ++ *fctr = 1024 * 1024; ++ } ++ else { ++ printf(" rBlk_nor/s wBlk_nor/s rBlk_dir/s wBlk_dir/s" ++ " rBlk_svr/s wBlk_svr/s"); ++ *fctr = 512; ++ } ++ printf(" ops/s rops/s wops/s\n"); ++} ++ ++/* ++ *************************************************************************** ++ * Write NFS stats read from /proc/self/mountstats. ++ * ++ * IN: ++ * @curr Index in array for current sample statistics. ++ * @itv Interval of time. ++ * @fctr Conversion factor. ++ * @shi Structures describing the NFS filesystems. ++ * @ioi Current sample statistics. ++ * @ioj Previous sample statistics. ++ *************************************************************************** ++ */ ++void write_nfs_stat(int curr, unsigned long long itv, int fctr, ++ struct io_hdr_stats *shi, struct io_nfs_stats *ioni, ++ struct io_nfs_stats *ionj) ++{ ++ if (DISPLAY_HUMAN_READ(flags)) { ++ printf("%-22s\n%23s", shi->name, ""); ++ } ++ else { ++ printf("%-22s ", shi->name); ++ } ++ printf("%12.2f %12.2f %12.2f %12.2f %12.2f %12.2f %9.2f %9.2f %9.2f\n", ++ S_VALUE(ionj->rd_normal_bytes, ioni->rd_normal_bytes, itv) / fctr, ++ S_VALUE(ionj->wr_normal_bytes, ioni->wr_normal_bytes, itv) / fctr, ++ S_VALUE(ionj->rd_direct_bytes, ioni->rd_direct_bytes, itv) / fctr, ++ S_VALUE(ionj->wr_direct_bytes, ioni->wr_direct_bytes, itv) / fctr, ++ S_VALUE(ionj->rd_server_bytes, ioni->rd_server_bytes, itv) / fctr, ++ S_VALUE(ionj->wr_server_bytes, ioni->wr_server_bytes, itv) / fctr, ++ S_VALUE(ionj->rpc_sends, ioni->rpc_sends, itv), ++ S_VALUE(ionj->nfs_rops, ioni->nfs_rops, itv), ++ S_VALUE(ionj->nfs_wops, ioni->nfs_wops, itv)); ++} ++ ++/* ++ *************************************************************************** ++ * Print everything now (stats and uptime). ++ * ++ * IN: ++ * @curr Index in array for current sample statistics. ++ * @rectime Current date and time. ++ *************************************************************************** ++ */ ++void write_stats(int curr, struct tm *rectime) ++{ ++ int i, fctr = 1; ++ unsigned long long itv; ++ struct io_hdr_stats *shi; ++ struct io_nfs_stats *ioni, *ionj; ++ ++ /* Test stdout */ ++ TEST_STDOUT(STDOUT_FILENO); ++ ++ /* Print time stamp */ ++ if (DISPLAY_TIMESTAMP(flags)) { ++ if (DISPLAY_ISO(flags)) { ++ strftime(timestamp, sizeof(timestamp), "%FT%T%z", rectime); ++ } ++ else { ++ strftime(timestamp, sizeof(timestamp), "%x %X", rectime); ++ } ++ printf("%s\n", timestamp); ++#ifdef DEBUG ++ if (DISPLAY_DEBUG(flags)) { ++ fprintf(stderr, "%s\n", timestamp); ++ } ++#endif ++ } ++ ++ /* Interval of time, reduced to one processor */ ++ itv = get_interval(uptime0[!curr], uptime0[curr]); ++ ++ shi = st_hdr_ionfs; ++ ++ /* Display NFS stats header */ ++ write_nfs_stat_header(&fctr); ++ ++ for (i = 0; i < ionfs_nr; i++, shi++) { ++ if (shi->used) { ++ ioni = st_ionfs[curr] + i; ++ ionj = st_ionfs[!curr] + i; ++#ifdef DEBUG ++ if (DISPLAY_DEBUG(flags)) { ++ /* Debug output */ ++ fprintf(stderr, "name=%s itv=%llu fctr=%d ioni{ rd_normal_bytes=%llu " ++ "wr_normal_bytes=%llu rd_direct_bytes=%llu wr_direct_bytes=%llu rd_server_bytes=%llu " ++ "wr_server_bytes=%llu rpc_sends=%lu nfs_rops=%lu nfs_wops=%lu }\n", ++ shi->name, itv, fctr, ++ ioni->rd_normal_bytes, ioni->wr_normal_bytes, ++ ioni->rd_direct_bytes, ioni->wr_direct_bytes, ++ ioni->rd_server_bytes, ioni->wr_server_bytes, ++ ioni->rpc_sends, ++ ioni->nfs_rops, ioni->nfs_wops); ++ } ++#endif ++ write_nfs_stat(curr, itv, fctr, shi, ioni, ionj); ++ } ++ } ++ printf("\n"); ++} ++ ++/* ++ *************************************************************************** ++ * Main loop: Read stats from the relevant sources and display them. ++ * ++ * IN: ++ * @count Number of lines of stats to print. ++ * @rectime Current date and time. ++ *************************************************************************** ++ */ ++void rw_io_stat_loop(long int count, struct tm *rectime) ++{ ++ int curr = 1; ++ ++ /* Don't buffer data if redirected to a pipe */ ++ setbuf(stdout, NULL); ++ ++ do { ++ /* Read system uptime (reduced to one processor) */ ++ uptime0[curr] = 0; ++ read_uptime(&(uptime0[curr])); ++ if (!uptime0[curr]) ++ /* Cannot read system uptime (/proc/uptime doesn't exist) */ ++ exit(2); ++ ++ /* Read NFS directories stats */ ++ read_nfs_stat(curr); ++ ++ /* Get time */ ++ get_localtime(rectime, 0); ++ ++ /* Print results */ ++ write_stats(curr, rectime); ++ ++ if (count > 0) { ++ count--; ++ } ++ if (count) { ++ curr ^= 1; ++ pause(); ++ } ++ } ++ while (count); ++} ++ ++/* ++ *************************************************************************** ++ * Main entry to the nfsiostat-sysstat program. ++ *************************************************************************** ++ */ ++int main(int argc, char **argv) ++{ ++ int it = 0; ++ int opt = 1; ++ int i; ++ long count = 1; ++ struct utsname header; ++ struct tm rectime; ++ ++#ifdef USE_NLS ++ /* Init National Language Support */ ++ init_nls(); ++#endif ++ ++ /* Get HZ */ ++ get_HZ(); ++ ++ /* Process args... */ ++ while (opt < argc) { ++ ++#ifdef DEBUG ++ if (!strcmp(argv[opt], "--debuginfo")) { ++ flags |= I_D_DEBUG; ++ opt++; ++ } else ++#endif ++ if (!strncmp(argv[opt], "-", 1)) { ++ for (i = 1; *(argv[opt] + i); i++) { ++ ++ switch (*(argv[opt] + i)) { ++ ++ case 'h': ++ /* Display an easy-to-read NFS report */ ++ flags |= I_D_HUMAN_READ; ++ break; ++ ++ case 'k': ++ if (DISPLAY_MEGABYTES(flags)) { ++ usage(argv[0]); ++ } ++ /* Display stats in kB/s */ ++ flags |= I_D_KILOBYTES; ++ break; ++ ++ case 'm': ++ if (DISPLAY_KILOBYTES(flags)) { ++ usage(argv[0]); ++ } ++ /* Display stats in MB/s */ ++ flags |= I_D_MEGABYTES; ++ break; ++ ++ case 't': ++ /* Display timestamp */ ++ flags |= I_D_TIMESTAMP; ++ break; ++ ++ case 'V': ++ /* Print version number and exit */ ++ print_version(); ++ break; ++ ++ default: ++ usage(argv[0]); ++ } ++ } ++ opt++; ++ } ++ ++ else if (!it) { ++ interval = atol(argv[opt++]); ++ if (interval < 0) { ++ usage(argv[0]); ++ } ++ count = -1; ++ it = 1; ++ } ++ ++ else if (it > 0) { ++ count = atol(argv[opt++]); ++ if ((count < 1) || !interval) { ++ usage(argv[0]); ++ } ++ it = -1; ++ } ++ else { ++ usage(argv[0]); ++ } ++ } ++ ++ if (!interval) { ++ count = 1; ++ } ++ ++ /* Select output unit (kB/s or blocks/s) */ ++ set_output_unit(); ++ ++ /* Init structures according to machine architecture */ ++ io_sys_init(); ++ ++ get_localtime(&rectime, 0); ++ ++ /* Get system name, release number and hostname */ ++ uname(&header); ++ if (print_gal_header(&rectime, header.sysname, header.release, ++ header.nodename, header.machine, cpu_nr)) { ++ flags |= I_D_ISO; ++ } ++ printf("\n"); ++ ++ /* Set a handler for SIGALRM */ ++ memset(&alrm_act, 0, sizeof(alrm_act)); ++ alrm_act.sa_handler = (void *) alarm_handler; ++ sigaction(SIGALRM, &alrm_act, NULL); ++ alarm(interval); ++ ++ /* Main loop */ ++ rw_io_stat_loop(count, &rectime); ++ ++ /* Free structures */ ++ io_sys_free(); ++ ++ return 0; ++} +diff -uprN sysstat-10.1.5.orig/nfsiostat-sysstat.h sysstat-10.1.5/nfsiostat-sysstat.h +--- sysstat-10.1.5.orig/nfsiostat-sysstat.h 1970-01-01 01:00:00.000000000 +0100 ++++ sysstat-10.1.5/nfsiostat-sysstat.h 2016-05-24 18:25:16.210668308 +0200 +@@ -0,0 +1,57 @@ ++/* ++ * nfsiostat-sysstat: Report NFS I/O statistics ++ * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved ++ * Written by Ivana Varekova ++ */ ++ ++#ifndef _NFSIOSTAT_SYSSTAT_H ++#define _NFSIOSTAT_SYSSTAT_H ++ ++#include "common.h" ++ ++#define NFSMOUNTSTATS "/proc/self/mountstats" ++ ++/* I_: iostat - D_: Display - F_: Flag */ ++#define I_D_TIMESTAMP 0x001 ++#define I_D_KILOBYTES 0x002 ++#define I_D_MEGABYTES 0x004 ++#define I_D_ISO 0x008 ++#define I_D_HUMAN_READ 0x010 ++#define I_D_DEBUG 0x020 ++ ++#define DISPLAY_TIMESTAMP(m) (((m) & I_D_TIMESTAMP) == I_D_TIMESTAMP) ++#define DISPLAY_KILOBYTES(m) (((m) & I_D_KILOBYTES) == I_D_KILOBYTES) ++#define DISPLAY_MEGABYTES(m) (((m) & I_D_MEGABYTES) == I_D_MEGABYTES) ++#define DISPLAY_ISO(m) (((m) & I_D_ISO) == I_D_ISO) ++#define DISPLAY_HUMAN_READ(m) (((m) & I_D_HUMAN_READ) == I_D_HUMAN_READ) ++#define DISPLAY_DEBUG(m) (((m) & I_D_DEBUG) == I_D_DEBUG) ++ ++/* Environment variable */ ++#define ENV_POSIXLY_CORRECT "POSIXLY_CORRECT" ++ ++/* Preallocation constats */ ++#define NR_NFS_PREALLOC 2 ++ ++struct io_nfs_stats { ++ unsigned long long rd_normal_bytes __attribute__ ((aligned (8))); ++ unsigned long long wr_normal_bytes __attribute__ ((packed)); ++ unsigned long long rd_direct_bytes __attribute__ ((packed)); ++ unsigned long long wr_direct_bytes __attribute__ ((packed)); ++ unsigned long long rd_server_bytes __attribute__ ((packed)); ++ unsigned long long wr_server_bytes __attribute__ ((packed)); ++ unsigned long rpc_sends __attribute__ ((packed)); ++ unsigned long nfs_rops __attribute__ ((packed)); ++ unsigned long nfs_wops __attribute__ ((packed)); ++}; ++ ++#define IO_NFS_STATS_SIZE (sizeof(struct io_nfs_stats)) ++ ++struct io_hdr_stats { ++ unsigned int active __attribute__ ((aligned (4))); ++ unsigned int used __attribute__ ((packed)); ++ char name[MAX_NAME_LEN]; ++}; ++ ++#define IO_HDR_STATS_SIZE (sizeof(struct io_hdr_stats)) ++ ++#endif /* _NFSIOSTAT_SYSSTAT_H */ diff --git a/SOURCES/sysstat-10.1.5-pids-prealloc.patch b/SOURCES/sysstat-10.1.5-pids-prealloc.patch new file mode 100644 index 0000000..23d9a59 --- /dev/null +++ b/SOURCES/sysstat-10.1.5-pids-prealloc.patch @@ -0,0 +1,209 @@ +diff -upr sysstat-10.1.5.orig/common.h sysstat-10.1.5/common.h +--- sysstat-10.1.5.orig/common.h 2013-03-23 17:31:46.000000000 +0100 ++++ sysstat-10.1.5/common.h 2016-04-29 12:47:09.285214132 +0200 +@@ -106,9 +106,12 @@ + * + * NB: Define SP_VALUE() to normalize to %; + * HZ is 1024 on IA64 and % should be normalized to 100. ++ * SP_VALUE_100() will not output value bigger than 100; this is needed for some ++ * corner cases, should be used with care. + */ + #define S_VALUE(m,n,p) (((double) ((n) - (m))) / (p) * HZ) + #define SP_VALUE(m,n,p) (((double) ((n) - (m))) / (p) * 100) ++#define SP_VALUE_100(m,n,p) MINIMUM((((double) ((n) - (m))) / (p) * 100),100) + + /* + * Under very special circumstances, STDOUT may become unavailable. +diff -upr sysstat-10.1.5.orig/pidstat.c sysstat-10.1.5/pidstat.c +--- sysstat-10.1.5.orig/pidstat.c 2013-03-23 17:31:46.000000000 +0100 ++++ sysstat-10.1.5/pidstat.c 2016-04-29 13:04:02.672402150 +0200 +@@ -152,15 +152,35 @@ void salloc_pid_array(unsigned int len) + */ + void salloc_pid(unsigned int len) + { +- int i; ++ short i; ++ ++ for (i = 0; i < 3; i++) { ++ if ((st_pid_list[i] = (struct pid_stats *) calloc(len, PID_STATS_SIZE)) == NULL) { ++ perror("calloc"); ++ exit(4); ++ } ++ } ++} ++ ++/* ++ *************************************************************************** ++ * Reallocate structures for PIDs to read. ++ *************************************************************************** ++ */ ++void realloc_pid(void) ++{ ++ short i; ++ unsigned int new_size = 2 * pid_nr; + + for (i = 0; i < 3; i++) { +- if ((st_pid_list[i] = (struct pid_stats *) malloc(PID_STATS_SIZE * len)) == NULL) { +- perror("malloc"); ++ if ((st_pid_list[i] = (struct pid_stats *) realloc(st_pid_list[i], PID_STATS_SIZE * new_size)) == NULL) { ++ perror("realloc"); + exit(4); + } +- memset(st_pid_list[i], 0, PID_STATS_SIZE * len); ++ memset(st_pid_list[i] + pid_nr, 0, PID_STATS_SIZE * (new_size - pid_nr)); + } ++ ++ pid_nr = new_size; + } + + /* +@@ -774,23 +794,22 @@ void read_task_stats(int curr, unsigned + if ((dir = opendir(filename)) == NULL) + return; + +- while (*index < pid_nr) { ++ while ((drp = readdir(dir)) != NULL) { ++ if (!isdigit(drp->d_name[0])) { ++ continue; ++ } + +- while ((drp = readdir(dir)) != NULL) { +- if (isdigit(drp->d_name[0])) +- break; ++ pst = st_pid_list[curr] + (*index)++; ++ if (read_pid_stats(atoi(drp->d_name), pst, &thr_nr, pid)) { ++ /* Thread no longer exists */ ++ pst->pid = 0; + } + +- if (drp) { +- pst = st_pid_list[curr] + (*index)++; +- if (read_pid_stats(atoi(drp->d_name), pst, &thr_nr, pid)) { +- /* Thread no longer exists */ +- pst->pid = 0; +- } ++ if (*index >= pid_nr) { ++ realloc_pid(); + } +- else +- break; + } ++ + closedir(dir); + } + +@@ -830,36 +849,34 @@ void read_stats(int curr) + exit(4); + } + +- while (p < pid_nr) { ++ /* Get directory entries */ ++ while ((drp = readdir(dir)) != NULL) { ++ if (!isdigit(drp->d_name[0])) { ++ continue; ++ } ++ ++ pst = st_pid_list[curr] + p++; ++ pid = atoi(drp->d_name); ++ ++ if (read_pid_stats(pid, pst, &thr_nr, 0)) { ++ /* Process has terminated */ ++ pst->pid = 0; ++ } else if (DISPLAY_TID(pidflag)) { ++ /* Read stats for threads in task subdirectory */ ++ read_task_stats(curr, pid, &p); + +- /* Get directory entries */ +- while ((drp = readdir(dir)) != NULL) { +- if (isdigit(drp->d_name[0])) +- break; +- } +- if (drp) { +- pst = st_pid_list[curr] + p++; +- pid = atoi(drp->d_name); +- +- if (read_pid_stats(pid, pst, &thr_nr, 0)) { +- /* Process has terminated */ +- pst->pid = 0; +- } +- +- else if (DISPLAY_TID(pidflag)) { +- /* Read stats for threads in task subdirectory */ +- read_task_stats(curr, pid, &p); +- } +- } +- else { +- for (q = p; q < pid_nr; q++) { +- pst = st_pid_list[curr] + q; +- pst->pid = 0; +- } +- break; + } ++ ++ if (p >= pid_nr) { ++ realloc_pid(); ++ } + } + ++ for (q = p; q < pid_nr; q++) { ++ pst = st_pid_list[curr] + q; ++ pst->pid = 0; ++ } ++ + /* Close /proc directory */ + closedir(dir); + } +@@ -1174,15 +1191,15 @@ int write_pid_task_all_stats(int prev, i + printf(" %7.2f %7.2f %7.2f %7.2f", + (pstc->utime - pstc->gtime) < (pstp->utime - pstp->gtime) ? + 0.0 : +- SP_VALUE(pstp->utime - pstp->gtime, ++ SP_VALUE_100(pstp->utime - pstp->gtime, + pstc->utime - pstc->gtime, itv), +- SP_VALUE(pstp->stime, pstc->stime, itv), +- SP_VALUE(pstp->gtime, pstc->gtime, itv), ++ SP_VALUE_100(pstp->stime, pstc->stime, itv), ++ SP_VALUE_100(pstp->gtime, pstc->gtime, itv), + /* User time already includes guest time */ + IRIX_MODE_OFF(pidflag) ? +- SP_VALUE(pstp->utime + pstp->stime, ++ SP_VALUE_100(pstp->utime + pstp->stime, + pstc->utime + pstc->stime, g_itv) : +- SP_VALUE(pstp->utime + pstp->stime, ++ SP_VALUE_100(pstp->utime + pstp->stime, + pstc->utime + pstc->stime, itv)); + + printf(" %3d", pstc->processor); +@@ -1351,15 +1368,15 @@ int write_pid_task_cpu_stats(int prev, i + printf(" %7.2f %7.2f %7.2f %7.2f", + (pstc->utime - pstc->gtime) < (pstp->utime - pstp->gtime) ? + 0.0 : +- SP_VALUE(pstp->utime - pstp->gtime, ++ SP_VALUE_100(pstp->utime - pstp->gtime, + pstc->utime - pstc->gtime, itv), +- SP_VALUE(pstp->stime, pstc->stime, itv), +- SP_VALUE(pstp->gtime, pstc->gtime, itv), ++ SP_VALUE_100(pstp->stime, pstc->stime, itv), ++ SP_VALUE_100(pstp->gtime, pstc->gtime, itv), + /* User time already includes guest time */ + IRIX_MODE_OFF(pidflag) ? +- SP_VALUE(pstp->utime + pstp->stime, ++ SP_VALUE_100(pstp->utime + pstp->stime, + pstc->utime + pstc->stime, g_itv) : +- SP_VALUE(pstp->utime + pstp->stime, ++ SP_VALUE_100(pstp->utime + pstp->stime, + pstc->utime + pstc->stime, itv)); + + if (!disp_avg) { +diff -upr sysstat-10.1.5.orig/pidstat.h sysstat-10.1.5/pidstat.h +--- sysstat-10.1.5.orig/pidstat.h 2013-03-23 17:31:46.000000000 +0100 ++++ sysstat-10.1.5/pidstat.h 2016-04-28 18:24:37.700124018 +0200 +@@ -13,7 +13,7 @@ + #define K_P_CHILD "CHILD" + #define K_P_ALL "ALL" + +-#define NR_PID_PREALLOC 10 ++#define NR_PID_PREALLOC 100 + + #define MAX_COMM_LEN 128 + #define MAX_CMDLINE_LEN 128 diff --git a/SOURCES/sysstat-10.1.5-tapestat.patch b/SOURCES/sysstat-10.1.5-tapestat.patch new file mode 100644 index 0000000..50ab274 --- /dev/null +++ b/SOURCES/sysstat-10.1.5-tapestat.patch @@ -0,0 +1,1124 @@ +diff -uprN sysstat-10.1.5.orig/Makefile.in sysstat-10.1.5/Makefile.in +--- sysstat-10.1.5.orig/Makefile.in 2016-06-01 12:46:28.193462305 +0200 ++++ sysstat-10.1.5/Makefile.in 2016-06-01 12:46:52.027338556 +0200 +@@ -160,7 +160,7 @@ NLSPOT= $(NLSPO:.po=.pot) + % : %.o + $(CC) -o $@ $(CFLAGS) $^ $(LFLAGS) + +-all: sadc sar sadf iostat mpstat pidstat nfsiostat-sysstat cifsiostat locales ++all: sadc sar sadf iostat tapestat mpstat pidstat nfsiostat-sysstat cifsiostat locales + + common.o: common.c version.h common.h ioconf.h sysconfig.h + +@@ -221,6 +221,10 @@ iostat.o: iostat.c iostat.h version.h co + + iostat: iostat.o librdstats.a libsyscom.a + ++tapestat.o: tapestat.c tapestat.h version.h common.h ++ ++tapestat: tapestat.o librdstats.a libsyscom.a ++ + pidstat.o: pidstat.c pidstat.h version.h common.h rd_stats.h + + pidstat: pidstat.o librdstats.a libsyscom.a +@@ -269,6 +273,8 @@ ifeq ($(INSTALL_DOC),y) + $(INSTALL_DATA) $(MANGRPARG) man/sysstat.5 $(DESTDIR)$(MAN5_DIR) + rm -f $(DESTDIR)$(MAN1_DIR)/iostat.1* + $(INSTALL_DATA) $(MANGRPARG) man/iostat.1 $(DESTDIR)$(MAN1_DIR) ++ rm -f $(DESTDIR)$(MAN1_DIR)/tapestat.1* ++ $(INSTALL_DATA) $(MANGRPARG) man/tapestat.1 $(DESTDIR)$(MAN1_DIR) + rm -f $(DESTDIR)$(MAN1_DIR)/mpstat.1* + $(INSTALL_DATA) $(MANGRPARG) man/mpstat.1 $(DESTDIR)$(MAN1_DIR) + rm -f $(DESTDIR)$(MAN1_DIR)/pidstat.1* +@@ -288,6 +294,7 @@ ifeq ($(COMPRESS_MANPG),y) + $(ZIP) $(DESTDIR)$(MAN1_DIR)/sadf.1 + $(ZIP) $(DESTDIR)$(MAN5_DIR)/sysstat.5 + $(ZIP) $(DESTDIR)$(MAN1_DIR)/iostat.1 ++ $(ZIP) $(DESTDIR)$(MAN1_DIR)/tapestat.1 + $(ZIP) $(DESTDIR)$(MAN1_DIR)/mpstat.1 + $(ZIP) $(DESTDIR)$(MAN1_DIR)/pidstat.1 + $(ZIP) $(DESTDIR)$(MAN1_DIR)/nfsiostat-sysstat.1 +@@ -328,6 +335,7 @@ endif + $(INSTALL_BIN) sar $(DESTDIR)$(BIN_DIR) + $(INSTALL_BIN) sadf $(DESTDIR)$(BIN_DIR) + $(INSTALL_BIN) iostat $(DESTDIR)$(BIN_DIR) ++ $(INSTALL_BIN) tapestat $(DESTDIR)$(BIN_DIR) + $(INSTALL_BIN) mpstat $(DESTDIR)$(BIN_DIR) + $(INSTALL_BIN) pidstat $(DESTDIR)$(BIN_DIR) + $(INSTALL_BIN) nfsiostat-sysstat $(DESTDIR)$(BIN_DIR) +@@ -395,6 +403,7 @@ ifeq ($(INSTALL_DOC),y) + rm -f $(DESTDIR)$(MAN1_DIR)/sadf.1* + rm -f $(DESTDIR)$(MAN5_DIR)/sysstat.5* + rm -f $(DESTDIR)$(MAN1_DIR)/iostat.1* ++ rm -f $(DESTDIR)$(MAN1_DIR)/tapestat.1* + rm -f $(DESTDIR)$(MAN1_DIR)/mpstat.1* + rm -f $(DESTDIR)$(MAN1_DIR)/pidstat.1* + rm -f $(DESTDIR)$(MAN1_DIR)/nfsiostat-sysstat.1* +@@ -423,6 +432,7 @@ uninstall_base: uninstall_man uninstall_ + rm -f $(DESTDIR)$(BIN_DIR)/sar + rm -f $(DESTDIR)$(BIN_DIR)/sadf + rm -f $(DESTDIR)$(BIN_DIR)/iostat ++ rm -f $(DESTDIR)$(BIN_DIR)/tapestat + rm -f $(DESTDIR)$(BIN_DIR)/mpstat + rm -f $(DESTDIR)$(BIN_DIR)/pidstat + rm -f $(DESTDIR)$(BIN_DIR)/nfsiostat-sysstat +@@ -487,7 +497,7 @@ po-files: + endif + + clean: +- rm -f sadc sar sadf iostat mpstat pidstat nfsiostat-sysstat cifsiostat *.o *.a core TAGS ++ rm -f sadc sar sadf iostat tapestat mpstat pidstat nfsiostat-sysstat cifsiostat *.o *.a core TAGS + find nls -name "*.gmo" -exec rm -f {} \; + + almost-distclean: clean nls/sysstat.pot +diff -uprN sysstat-10.1.5.orig/man/tapestat.1 sysstat-10.1.5/man/tapestat.1 +--- sysstat-10.1.5.orig/man/tapestat.1 1970-01-01 01:00:00.000000000 +0100 ++++ sysstat-10.1.5/man/tapestat.1 2016-06-01 12:46:52.028338551 +0200 +@@ -0,0 +1,216 @@ ++.TH TAPESTAT 1 "MARCH 2016" Linux "Linux User's Manual" -*- nroff -*- ++.SH NAME ++tapestat \- Report tape statistics. ++.SH SYNOPSIS ++.B tapestat [ -k | -m ] [ -t ] [ -V ] [ -y ] [ -z ] [ ++.I interval ++.B [ ++.I count ++.B ] ] ++.SH DESCRIPTION ++The ++.B tapestat ++command is used for monitoring the activity of tape drives connected to a system. ++ ++The first report generated by the ++.B tapestat ++command provides statistics ++concerning the time since the system was booted, unless the ++.B -y ++option is used, when this first report is omitted. ++Each subsequent report ++covers the time since the previous report. ++ ++The ++.I interval ++parameter specifies the amount of time in seconds between ++each report. ++The ++.I count ++parameter can be specified in conjunction with the ++.I interval ++parameter. If the ++.I count ++parameter is specified, the value of ++.I count ++determines the number of reports generated at ++.I interval ++seconds apart. If the ++.I interval ++parameter is specified without the ++.I count ++parameter, the ++.B tapestat ++command generates reports continuously. ++ ++.SH REPORT ++The ++.B tapestat ++report provides statistics for each tape drive connected to the system. ++The following data are displayed: ++ ++.B r/s ++.RS ++The number of reads issued expressed as the number per second averaged over the interval. ++ ++.RE ++.B w/s ++.RS ++The number of writes issued expressed as the number per second averaged over the interval. ++ ++.RE ++.B kB_read/s | MB_read/s ++.RS ++The amount of data read expressed in kilobytes (by default or if option -k used) or ++megabytes (if option -m used) per second averaged over the interval. ++ ++.RE ++.B kB_wrtn/s | MB_wrtn/s ++.RS ++The amount of data written expressed in kilobytes (by default or if option -k used) or ++megabytes (if option -m used) per second averaged over the interval. ++ ++.RE ++.B %Rd ++.RS ++Read percentage wait - The percentage of time over the interval spent waiting for read requests ++to complete. ++The time is measured from when the request is dispatched to the SCSI mid-layer until it signals ++that it completed. ++ ++.RE ++.B %Wr ++.RS ++Write percentage wait - The percentage of time over the interval spent waiting for write requests ++to complete. The time is measured from when the request is dispatched to the SCSI mid-layer until ++it signals that it completed. ++ ++.RE ++.B %Oa ++.RS ++Overall percentage wait - The percentage of time over the interval spent waiting for any ++I/O request to complete (read, write, and other). ++ ++.RE ++.B Rs/s ++.RS ++The number of I/Os, expressed as the number per second averaged over the interval, where ++a non-zero residual value was encountered. ++ ++.RE ++.B Ot/s ++.RS ++The number of I/Os, expressed as the number per second averaged over the interval, that ++were included as "other". Other I/O includes ioctl calls made to the tape driver and ++implicit operations performed by the tape driver such as rewind on close ++(for tape devices that implement rewind on close). It does not include any I/O performed ++using methods outside of the tape driver (e.g. via sg ioctls). ++.RE ++.RE ++.SH OPTIONS ++.IP -k ++Show the amount of data written or read in kilobytes per second instead of megabytes. ++This option is mutually exclusive with -m. ++.IP -m ++Show the amount of data written or read in megabytes per second instead of kilobytes. ++This option is mutually exclusive with -k. ++.IP -t ++Display time stamps. The time stamp format may depend ++on the value of the S_TIME_FORMAT environment variable (see below). ++.IP -V ++Print version and exit. ++.IP -y ++Omit the initial statistic showing values since boot. ++.IP -z ++Tell ++.B tapestat ++to omit output for any tapes for which there was no activity ++during the sample period. ++ ++.SH CONSIDERATIONS ++It is possible for a percentage value (read, write, or other) to be greater than 100 percent ++(the ++.B tapestat ++command will never show a percentage value more than 999). ++If rewinding a tape takes 40 seconds where the interval time is 5 seconds the %Oa value ++would show as 0 in the intervals before the rewind completed and then show as approximately ++800 percent when the rewind completes. ++ ++Similar values will be observed for %Rd and %Wr if a tape drive stops reading or writing ++and then restarts (that is it stopped streaming). In such a case you may see the r/s or w/s drop to zero and the %Rd/%Wr value could be higher than 100 when reading or writing continues ++(depending on how long it takes to restart writing or reading). ++This is only an issue if it happens a lot as it may cause tape wear and will impact ++on the backup times. ++ ++For fast tape drives you may see low percentage wait times. ++This does not indicate an issue with the tape drive. For a slower tape drive (e.g. an older ++generation DDS drive) the speed of the tape (and tape drive) is much slower than filesystem I/O, ++percent wait times are likely to be higher. For faster tape drives (e.g. LTO) the percentage ++wait times are likely to be lower as program writing to or reading from tape is going ++to be doing a lot more filesystem I/O because of the higher throughput. ++ ++Although tape statistics are implemented in the kernel using atomic variables they cannot be ++read atomically as a group. All of the statistics values are read from different files under ++/sys, because of this there may be I/O completions while reading the different files for the ++one tape drive. This may result in a set of statistics for a device that contain some values ++before an I/O completed and some after. ++ ++This command uses rounding down as the rounding method when calculating per second statistics. ++If, for example, you are using dd to copy one tape to another and running ++.B tapestat ++with an interval of 5 seconds and over the interval there were 3210 writes and 3209 reads ++then w/s would show 642 and r/s 641 (641.8 rounded down to 641). In such a case if it was ++a tar archive being copied (with a 10k block size) you would also see a difference between ++the kB_read/s and kB_wrtn/s of 2 (one I/O 10k in size divided by the interval period of 5 ++seconds). If instead there were 3210 writes and 3211 reads both w/s and r/s would both show ++642 but you would still see a difference between the kB_read/s and kB_wrtn/s values of 2 kB/s. ++ ++This command is provided with an interval in seconds. However internally the interval is ++tracked per device and can potentially have an effect on the per second statistics reported. ++The time each set of statistics is captured is kept with those statistics. The difference ++between the current and previous time is converted to milliseconds for use in calculations. ++We can look at how this can impact the statistics reported if we use an example of a tar ++archive being copied between two tape drives using dd. If both devices reported 28900 kilobytes ++transferred and the reading tape drive had an interval of 5001 milliseconds and the writing ++tape drive 5000 milliseconds that would calculate out as 5778 kB_read/s and 5780 kB_wrtn/s. ++ ++The impact of some retrieving statistics during an I/O completion, rounding down, and small differences in the interval period on the statistics calculated should be minimal but may be non-zero. ++.SH ENVIRONMENT ++The ++.B tapestat ++command takes into account the following environment variable: ++ ++.IP S_TIME_FORMAT ++If this variable exists and its value is ++.BR ISO ++then the current locale will be ignored when printing the date in the report ++header. The ++.B tapestat ++command will use the ISO 8601 format (YYYY-MM-DD) instead. ++The timestamp displayed with option -t will also be compliant with ISO 8601 ++format. ++ ++.SH BUGS ++.I /sys ++filesystem must be mounted for ++.B tapestat ++to work. It will not work on kernels that do not have sysfs support ++ ++This command requires kernel version 4.2 or later ++(or tape statistics support backported for an earlier kernel version). ++ ++.SH FILES ++.I /sys/class/scsi_tape/st/stats/* ++Statistics files for tape devices. ++ ++.I /proc/uptime ++contains system uptime. ++.SH AUTHOR ++Initial revision by Shane M. SEYMOUR (shane.seymour hpe.com) ++.br ++Modified for sysstat by Sebastien Godard (sysstat orange.fr) ++.SH SEE ALSO ++.BR iostat (1), ++.BR mpstat (1) ++ ++.I http://pagesperso-orange.fr/sebastien.godard/ +diff -uprN sysstat-10.1.5.orig/tapestat.c sysstat-10.1.5/tapestat.c +--- sysstat-10.1.5.orig/tapestat.c 1970-01-01 01:00:00.000000000 +0100 ++++ sysstat-10.1.5/tapestat.c 2016-06-01 12:54:20.384015957 +0200 +@@ -0,0 +1,697 @@ ++/* ++ * tapestat: report tape statistics ++ * (C) 2015 Hewlett-Packard Development Company, L.P. ++ * ++ * Initial revision by Shane M. SEYMOUR (shane.seymour hpe.com) ++ * Modified for sysstat 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 ++#define __DO_NOT_DEFINE_COMPILE ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#undef HZ /* sys/param.h defines HZ but needed for MAXPATHLEN */ ++ ++#include "version.h" ++#include "tapestat.h" ++#include "common.h" ++ ++#ifdef USE_NLS ++#include ++#include ++#define _(string) gettext(string) ++#else ++#define _(string) (string) ++#endif ++ ++#define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__ ++char *sccsid(void) { return (SCCSID); } ++ ++int cpu_nr = 0; /* Nb of processors on the machine */ ++int flags = 0; /* Flag for common options and system state */ ++ ++long interval = 0; ++char timestamp[64]; ++ ++struct sigaction alrm_act; ++ ++/* ++ * For tape stats - it would be extremely rare for there to be a very large ++ * number of tape drives attached to a system. I wouldn't expect to see more ++ * than 20-30 in a very large configuration and discontinguous ones should ++ * be even more rare. Because of this we keep the old and new data in a ++ * simple data structure with the tape index being the number after the tape ++ * drive, st0 at index 0, etc. ++ */ ++int max_tape_drives = 0; ++struct tape_stats *tape_new_stats = { NULL }; ++struct tape_stats *tape_old_stats = { NULL }; ++regex_t tape_reg; ++ ++/* ++ *************************************************************************** ++ * Print usage and exit. ++ * ++ * IN: ++ * @progname Name of sysstat command. ++ *************************************************************************** ++ */ ++void usage(char *progname) ++{ ++ fprintf(stderr, _("Usage: %s [ options ] [ [ ] ]\n"), ++ progname); ++ fprintf(stderr, _("Options are:\n" ++ "[ -k | -m ] [ -t ] [ -V ] [ -y ] [ -z ]\n")); ++ exit(1); ++} ++ ++/* ++ *************************************************************************** ++ * SIGALRM signal handler. No need to reset the handler here. ++ * ++ * IN: ++ * @sig Signal number. ++ *************************************************************************** ++ */ ++void alarm_handler(int sig) ++{ ++ alarm(interval); ++} ++ ++/* ++ *************************************************************************** ++ * Initialization. ++ *************************************************************************** ++ */ ++void tape_initialise(void) ++{ ++ /* How many processors on this machine? */ ++ cpu_nr = get_cpu_nr(~0); ++ ++ /* Compile regular expression for tape names */ ++ if (regcomp(&tape_reg, "^st[0-9]+$", REG_EXTENDED) != 0) { ++ exit(1); ++ } ++} ++ ++/* ++ *************************************************************************** ++ * Free structures. ++ *************************************************************************** ++ */ ++void tape_uninitialise(void) ++{ ++ regfree(&tape_reg); ++ if (tape_old_stats != NULL) { ++ free(tape_old_stats); ++ } ++ if (tape_new_stats != NULL) { ++ free(tape_new_stats); ++ } ++} ++ ++/* ++ *************************************************************************** ++ * Get maximum number of tapes in the system. ++ * ++ * RETURNS: ++ * Number of tapes found. ++ *************************************************************************** ++ */ ++int get_max_tape_drives(void) ++{ ++ DIR *dir; ++ struct dirent *entry; ++ int new_max_tape_drives, tmp, num_stats_dir = 0; ++ regmatch_t match; ++ char stats_dir[MAXPATHLEN + 1]; ++ struct stat stat_buf; ++ ++ new_max_tape_drives = max_tape_drives; ++ ++ /* Open sysfs tree */ ++ dir = opendir(SYSFS_CLASS_TAPE_DIR); ++ if (dir == NULL) ++ return 0; ++ ++ while ((entry = readdir(dir)) != NULL) { ++ if (regexec(&tape_reg, &entry->d_name[0], 1, &match, 0) == 0) { ++ /* d_name[2] to skip the st at the front */ ++ tmp = atoi(&entry->d_name[2]) + 1; ++ if (tmp > new_max_tape_drives) { ++ new_max_tape_drives = tmp; ++ } ++ } ++ snprintf(stats_dir, MAXPATHLEN, "%s/%s/%s", ++ SYSFS_CLASS_TAPE_DIR, &entry->d_name[0], "stats"); ++ if (stat(stats_dir, &stat_buf) == 0) { ++ if (S_ISDIR(stat_buf.st_mode)) { ++ num_stats_dir++; ++ } ++ } ++ } ++ closedir(dir); ++ ++ /* If there are no stats directories make the new number of tape drives 0 */ ++ if (num_stats_dir == 0) { ++ new_max_tape_drives = 0; ++ } ++ ++ return new_max_tape_drives; ++} ++ ++/* ++ *************************************************************************** ++ * Check if new tapes have been added and reallocate structures accordingly. ++ *************************************************************************** ++ */ ++void tape_check_tapes_and_realloc(void) ++{ ++ int new_max_tape_drives, i; ++ ++ /* Count again number of tapes */ ++ new_max_tape_drives = get_max_tape_drives(); ++ ++ if (new_max_tape_drives > max_tape_drives && new_max_tape_drives > 0) { ++ /* New tapes found: Realloc structures */ ++ struct tape_stats *tape_old_stats_t = (struct tape_stats *) ++ realloc(tape_old_stats, sizeof(struct tape_stats) * new_max_tape_drives); ++ struct tape_stats *tape_new_stats_t = (struct tape_stats *) ++ realloc(tape_new_stats, sizeof(struct tape_stats) * new_max_tape_drives); ++ if ((tape_old_stats_t == NULL) || (tape_new_stats_t == NULL)) { ++ if (tape_old_stats_t != NULL) { ++ free(tape_old_stats_t); ++ tape_old_stats_t = NULL; ++ } else { ++ free(tape_old_stats); ++ tape_old_stats = NULL; ++ } ++ if (tape_new_stats_t != NULL) { ++ free(tape_new_stats_t); ++ tape_new_stats_t = NULL; ++ } else { ++ free(tape_new_stats); ++ tape_new_stats = NULL; ++ } ++ ++ perror("realloc"); ++ exit(4); ++ } ++ ++ tape_old_stats = tape_old_stats_t; ++ tape_new_stats = tape_new_stats_t; ++ ++ for (i = max_tape_drives; i < new_max_tape_drives; i++) { ++ tape_old_stats[i].valid = TAPE_STATS_INVALID; ++ tape_new_stats[i].valid = TAPE_STATS_INVALID; ++ } ++ max_tape_drives = new_max_tape_drives; ++ } ++} ++ ++/* ++ *************************************************************************** ++ * Collect initial statistics for all existing tapes in the system. ++ * This function should be called only once. ++ *************************************************************************** ++ */ ++void tape_gather_initial_stats(void) ++{ ++ int new_max_tape_drives, i; ++ FILE *fp; ++ char filename[MAXPATHLEN + 1]; ++ ++ /* Get number of tapes in the system */ ++ new_max_tape_drives = get_max_tape_drives(); ++ ++ if (new_max_tape_drives == 0) { ++ /* No tapes found */ ++ fprintf(stderr, _("No tape drives with statistics found\n")); ++ exit(1); ++ } ++ else { ++ /* Allocate structures */ ++ if (tape_old_stats == NULL) { ++ tape_old_stats = (struct tape_stats *) ++ malloc(sizeof(struct tape_stats) * new_max_tape_drives); ++ tape_new_stats = (struct tape_stats *) ++ malloc(sizeof(struct tape_stats) * new_max_tape_drives); ++ for (i = 0; i < new_max_tape_drives; i++) { ++ tape_old_stats[i].valid = TAPE_STATS_INVALID; ++ tape_new_stats[i].valid = TAPE_STATS_INVALID; ++ } ++ max_tape_drives = new_max_tape_drives; ++ } else ++ /* This should only be called once */ ++ return; ++ } ++ ++ /* Read stats for each tape */ ++ for (i = 0; i < max_tape_drives; i++) { ++ /* ++ * Everything starts out valid but failing to open ++ * a file gets the tape drive marked invalid. ++ */ ++ tape_new_stats[i].valid = TAPE_STATS_VALID; ++ tape_old_stats[i].valid = TAPE_STATS_VALID; ++ ++ gettimeofday(&tape_old_stats[i].tv, NULL); ++ ++ tape_new_stats[i].tv.tv_sec = tape_old_stats[i].tv.tv_sec; ++ tape_new_stats[i].tv.tv_usec = tape_old_stats[i].tv.tv_usec; ++ ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_ns", read_time) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_ns", write_time) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "io_ns", other_time) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_byte_cnt", read_bytes) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_byte_cnt", write_bytes) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_cnt", read_count) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_cnt", write_count) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "other_cnt", other_count) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "resid_cnt", resid_count) ++ ++ tape_old_stats[i].read_time = 0; ++ tape_old_stats[i].write_time = 0; ++ tape_old_stats[i].other_time = 0; ++ tape_old_stats[i].read_bytes = 0; ++ tape_old_stats[i].write_bytes = 0; ++ tape_old_stats[i].read_count = 0; ++ tape_old_stats[i].write_count = 0; ++ tape_old_stats[i].other_count = 0; ++ tape_old_stats[i].resid_count = 0; ++ } ++} ++ ++/* ++ *************************************************************************** ++ * Collect a new sample of statistics for all existing tapes in the system. ++ *************************************************************************** ++ */ ++void tape_get_updated_stats(void) ++{ ++ int i; ++ FILE *fp; ++ char filename[MAXPATHLEN + 1] = { 0 }; ++ ++ /* Check tapes and realloc structures if needed */ ++ tape_check_tapes_and_realloc(); ++ ++ for (i = 0; i < max_tape_drives; i++) { ++ /* ++ * Everything starts out valid but failing ++ * to open a file gets the tape drive marked invalid. ++ */ ++ tape_new_stats[i].valid = TAPE_STATS_VALID; ++ gettimeofday(&tape_new_stats[i].tv, NULL); ++ ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_ns", read_time) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_ns", write_time) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "io_ns", other_time) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_byte_cnt", read_bytes) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_byte_cnt", write_bytes) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "read_cnt", read_count) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "write_cnt", write_count) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "other_cnt", other_count) ++ TAPE_STAT_FILE_VAL(TAPE_STAT_PATH "resid_cnt", resid_count) ++ ++ if ((tape_new_stats[i].read_time < tape_old_stats[i].read_time) || ++ (tape_new_stats[i].write_time < tape_old_stats[i].write_time) || ++ (tape_new_stats[i].other_time < tape_old_stats[i].other_time)) { ++ tape_new_stats[i].valid = TAPE_STATS_INVALID; ++ } ++ } ++} ++ ++/* ++ *************************************************************************** ++ * Display tapes statistics headings. ++ *************************************************************************** ++ */ ++void tape_write_headings(void) ++{ ++ printf("Tape: r/s w/s "); ++ if (DISPLAY_MEGABYTES(flags)) { ++ printf("MB_read/s MB_wrtn/s"); ++ } else { ++ printf("kB_read/s kB_wrtn/s"); ++ } ++ printf(" %%Rd %%Wr %%Oa Rs/s Ot/s\n"); ++} ++ ++/* ++ *************************************************************************** ++ * Calculate statistics for current tape. ++ * ++ * IN: ++ * @i Index in array for current tape. ++ * ++ * OUT: ++ * @stats Statistics for current tape. ++ *************************************************************************** ++ */ ++void tape_calc_one_stats(struct calc_stats *stats, int i) ++{ ++ uint64_t duration; ++ double temp; ++ FILE *fp; ++ ++ /* Duration in ms done in ms to prevent rounding issues with using seconds */ ++ duration = (tape_new_stats[i].tv.tv_sec - ++ tape_old_stats[i].tv.tv_sec) * 1000; ++ duration -= tape_old_stats[i].tv.tv_usec / 1000; ++ duration += tape_new_stats[i].tv.tv_usec / 1000; ++ ++ /* If duration is zero we need to calculate the ms since boot time */ ++ if (duration == 0) { ++ fp = fopen("/proc/uptime", "r"); ++ ++ /* ++ * Get uptime from /proc/uptime and if we can't then just set duration to ++ * be 0 - it will mean that we don't calculate stats. ++ */ ++ if (fp == NULL) { ++ duration = 0; ++ } else { ++ if (fscanf(fp, "%lf", &temp) != 1) { ++ temp = 0; ++ } ++ duration = (uint64_t) (temp * 1000); ++ fclose(fp); ++ } ++ } ++ ++ /* The second value passed into the macro is the thing being calculated */ ++ CALC_STAT_CNT(read_count, reads_per_second) ++ CALC_STAT_CNT(write_count, writes_per_second) ++ CALC_STAT_CNT(other_count, other_per_second) ++ CALC_STAT_KB(read_bytes, kbytes_read_per_second) ++ CALC_STAT_KB(write_bytes, kbytes_written_per_second) ++ CALC_STAT_PCT(read_time, read_pct_wait) ++ CALC_STAT_PCT(write_time, write_pct_wait) ++ CALC_STAT_PCT(other_time, all_pct_wait) ++ CALC_STAT_CNT(resid_count, resids_per_second) ++} ++ ++/* ++ *************************************************************************** ++ * Display statistics for current tape. ++ * ++ * IN: ++ * @tape Statistics for current tape. ++ * @i Index in array for current tape. ++ *************************************************************************** ++ */ ++void tape_write_stats(struct calc_stats *tape, int i) ++{ ++ char buffer[32]; ++ uint64_t divisor = 1; ++ ++ if (DISPLAY_MEGABYTES(flags)) ++ divisor = 1024; ++ ++ sprintf(buffer, "st%i ", i); ++ buffer[5] = 0; ++ printf("%s%7"PRId64" %7"PRId64" %11"PRId64 ++ " %11"PRId64" %3"PRId64" %3"PRId64" %3"PRId64 ++ " %7"PRId64" %7"PRId64"\n", buffer, ++ tape->reads_per_second, tape->writes_per_second, ++ tape->kbytes_read_per_second / divisor, ++ tape->kbytes_written_per_second / divisor, ++ tape->read_pct_wait, tape->write_pct_wait, ++ tape->all_pct_wait, tape->resids_per_second, ++ tape->other_per_second); ++} ++ ++/* ++ *************************************************************************** ++ * Print everything now (stats and uptime). ++ * ++ * IN: ++ * @rectime Current date and time. ++ *************************************************************************** ++ */ ++void write_stats(struct tm *rectime) ++{ ++ int i; ++ struct calc_stats tape; ++ struct tape_stats *tmp; ++ ++ /* Test stdout */ ++ TEST_STDOUT(STDOUT_FILENO); ++ ++ /* Print time stamp */ ++ if (DISPLAY_TIMESTAMP(flags)) { ++ if (DISPLAY_ISO(flags)) { ++ strftime(timestamp, sizeof(timestamp), "%FT%T%z", rectime); ++ } ++ else { ++ strftime(timestamp, sizeof(timestamp), "%x %X", rectime); ++ } ++ printf("%s\n", timestamp); ++ } ++ ++ /* Print the headings */ ++ tape_write_headings(); ++ ++ /* ++ * If either new or old is invalid or the I/Os per second is 0 and ++ * zero omit is true then we print nothing. ++ */ ++ if (max_tape_drives > 0) { ++ ++ for (i = 0; i < max_tape_drives; i++) { ++ if ((tape_new_stats[i].valid == TAPE_STATS_VALID) && ++ (tape_old_stats[i].valid == TAPE_STATS_VALID)) { ++ tape_calc_one_stats(&tape, i); ++ if (!(DISPLAY_ZERO_OMIT(flags) ++ && (tape.other_per_second == 0) ++ && (tape.reads_per_second == 0) ++ && (tape.writes_per_second == 0) ++ && (tape.kbytes_read_per_second == 0) ++ && (tape.kbytes_written_per_second == 0) ++ && (tape.read_pct_wait == 0) ++ && (tape.write_pct_wait == 0) ++ && (tape.all_pct_wait == 0) ++ && (tape.resids_per_second == 0))) { ++ tape_write_stats(&tape, i); ++ } ++ } ++ } ++ /* ++ * Swap new and old so next time we compare against the new old stats. ++ * If a new tape drive appears it won't appear in the output until after ++ * the second time we gather information about it. ++ */ ++ tmp = tape_old_stats; ++ tape_old_stats = tape_new_stats; ++ tape_new_stats = tmp; ++ } ++ printf("\n"); ++} ++ ++/* ++ *************************************************************************** ++ * Main loop: Read tape stats from the relevant sources and display them. ++ * ++ * IN: ++ * @count Number of lines of stats to print. ++ * @rectime Current date and time. ++ *************************************************************************** ++ */ ++void rw_tape_stat_loop(long int count, struct tm *rectime) ++{ ++ struct tape_stats *tmp; ++ int skip = 0; ++ ++ /* Should we skip first report? */ ++ if (DISPLAY_OMIT_SINCE_BOOT(flags) && interval > 0) { ++ skip = 1; ++ } ++ ++ /* Don't buffer data if redirected to a pipe */ ++ setbuf(stdout, NULL); ++ ++ do { ++ ++ if (tape_new_stats == NULL) { ++ tape_gather_initial_stats(); ++ } else { ++ tape_get_updated_stats(); ++ } ++ ++ /* Get time */ ++ get_localtime(rectime, 0); ++ ++ /* Check whether we should skip first report */ ++ if (!skip) { ++ /* Print results */ ++ write_stats(rectime); ++ ++ if (count > 0) { ++ count--; ++ } ++ } ++ else { ++ skip = 0; ++ tmp = tape_old_stats; ++ tape_old_stats = tape_new_stats; ++ tape_new_stats = tmp; ++ } ++ ++ if (count) { ++ pause(); ++ } ++ } ++ while (count); ++} ++ ++/* ++ *************************************************************************** ++ * Main entry to the tapestat program. ++ *************************************************************************** ++ */ ++int main(int argc, char **argv) ++{ ++ int it = 0; ++ int opt = 1; ++ int i; ++ long count = 1; ++ struct utsname header; ++ struct tm rectime; ++ ++#ifdef USE_NLS ++ /* Init National Language Support */ ++ init_nls(); ++#endif ++ ++ /* Get HZ */ ++ get_HZ(); ++ ++ /* Process args... */ ++ while (opt < argc) { ++ if (!strncmp(argv[opt], "-", 1)) { ++ for (i = 1; *(argv[opt] + i); i++) { ++ ++ switch (*(argv[opt] + i)) { ++ ++ case 'k': ++ if (DISPLAY_MEGABYTES(flags)) { ++ usage(argv[0]); ++ } ++ /* Display stats in kB/s */ ++ flags |= T_D_KILOBYTES; ++ break; ++ ++ case 'm': ++ if (DISPLAY_KILOBYTES(flags)) { ++ usage(argv[0]); ++ } ++ /* Display stats in MB/s */ ++ flags |= T_D_MEGABYTES; ++ break; ++ ++ case 't': ++ /* Display timestamp */ ++ flags |= T_D_TIMESTAMP; ++ break; ++ ++ case 'y': ++ /* Don't display stats since system restart */ ++ flags |= T_D_OMIT_SINCE_BOOT; ++ break; ++ ++ case 'z': ++ /* Omit output for devices with no activity */ ++ flags |= T_D_ZERO_OMIT; ++ break; ++ ++ case 'V': ++ /* Print version number and exit */ ++ print_version(); ++ break; ++ ++ default: ++ usage(argv[0]); ++ } ++ } ++ opt++; ++ } ++ ++ else if (!it) { ++ interval = atol(argv[opt++]); ++ if (interval < 0) { ++ usage(argv[0]); ++ } ++ count = -1; ++ it = 1; ++ } ++ ++ else if (it > 0) { ++ count = atol(argv[opt++]); ++ if ((count < 1) || !interval) { ++ usage(argv[0]); ++ } ++ it = -1; ++ } ++ else { ++ usage(argv[0]); ++ } ++ } ++ ++ if (!interval) { ++ count = 1; ++ } ++ ++ tape_initialise(); ++ ++ get_localtime(&rectime, 0); ++ ++ /* Get system name, release number and hostname */ ++ uname(&header); ++ if (print_gal_header(&rectime, header.sysname, header.release, ++ header.nodename, header.machine, cpu_nr)) { ++ flags |= T_D_ISO; ++ } ++ printf("\n"); ++ ++ /* Set a handler for SIGALRM */ ++ memset(&alrm_act, 0, sizeof(alrm_act)); ++ alrm_act.sa_handler = alarm_handler; ++ sigaction(SIGALRM, &alrm_act, NULL); ++ alarm(interval); ++ ++ /* Main loop */ ++ rw_tape_stat_loop(count, &rectime); ++ ++ /* Free structures */ ++ tape_uninitialise(); ++ ++ return 0; ++} +diff -uprN sysstat-10.1.5.orig/tapestat.h sysstat-10.1.5/tapestat.h +--- sysstat-10.1.5.orig/tapestat.h 1970-01-01 01:00:00.000000000 +0100 ++++ sysstat-10.1.5/tapestat.h 2016-06-01 12:46:52.028338551 +0200 +@@ -0,0 +1,126 @@ ++/* ++ * tapestat: report tape statistics ++ * (C) 2015 Hewlett-Packard Development Company, L.P. ++ * ++ * Initial revision by Shane M. SEYMOUR (shane.seymour hpe.com) ++ * Modified for sysstat by Sebastien GODARD (sysstat orange.fr) ++ */ ++ ++#ifndef _TAPESTAT_H ++#define _TAPESTAT_H ++ ++/* T_: tapestat - D_: Display - F_: Flag */ ++#define T_D_TIMESTAMP 0x00001 ++#define T_D_KILOBYTES 0x00002 ++#define T_D_MEGABYTES 0x00004 ++#define T_D_OMIT_SINCE_BOOT 0x00008 ++#define T_D_ISO 0x00010 ++#define T_D_ZERO_OMIT 0x00020 ++ ++#define DISPLAY_TIMESTAMP(m) (((m) & T_D_TIMESTAMP) == T_D_TIMESTAMP) ++#define DISPLAY_KILOBYTES(m) (((m) & T_D_KILOBYTES) == T_D_KILOBYTES) ++#define DISPLAY_MEGABYTES(m) (((m) & T_D_MEGABYTES) == T_D_MEGABYTES) ++#define DISPLAY_OMIT_SINCE_BOOT(m) (((m) & T_D_OMIT_SINCE_BOOT) == T_D_OMIT_SINCE_BOOT) ++#define DISPLAY_ISO(m) (((m) & T_D_ISO) == T_D_ISO) ++#define DISPLAY_ZERO_OMIT(m) (((m) & T_D_ZERO_OMIT) == T_D_ZERO_OMIT) ++ ++ ++#define TAPE_STATS_VALID 1 ++#define TAPE_STATS_INVALID 0 ++ ++#define SYSFS_CLASS_TAPE_DIR "/sys/class/scsi_tape" ++#define TAPE_STAT_PATH "/sys/class/scsi_tape/st%i/stats/" ++ ++#define TAPE_STAT_FILE_VAL(A, B) \ ++ snprintf(filename, MAXPATHLEN, A, i); \ ++ if ((fp = fopen(filename, "r")) != NULL) { \ ++ if (fscanf(fp, "%"PRId64, &tape_new_stats[i].B) != 1) { \ ++ tape_new_stats[i].valid = TAPE_STATS_INVALID; \ ++ } \ ++ fclose(fp); \ ++ } else { \ ++ tape_new_stats[i].valid = TAPE_STATS_INVALID; \ ++ continue; \ ++ } ++ ++ ++/* ++ * A - tape_stats structure member name, e.g. read_count ++ * B - calc_stats structure member name, e.g. reads_per_second ++ * ++ * These macros are not selfcontained they depend on some other ++ * variables defined either as global or local to the function. ++ */ ++ ++#define CALC_STAT_CNT(A, B) \ ++ if ((tape_new_stats[i].A == tape_old_stats[i].A) || \ ++ (duration <= 0)) { \ ++ stats->B = 0; \ ++ } else { \ ++ temp = (double) (tape_new_stats[i].A - \ ++ tape_old_stats[i].A) \ ++ / (((double) duration) / 1000); \ ++ stats->B = (uint64_t) temp; \ ++ } ++#define CALC_STAT_KB(A, B) \ ++ if ((tape_new_stats[i].A == tape_old_stats[i].A) || \ ++ (duration <= 0)) { \ ++ stats->B = 0; \ ++ } else { \ ++ temp = (double) (tape_new_stats[i].A - \ ++ tape_old_stats[i].A) \ ++ / (((double) duration) / 1000.0); \ ++ stats->B = (uint64_t) (temp / 1024.0); \ ++ } ++ ++#define TAPE_MAX_PCT 999 ++ ++#define CALC_STAT_PCT(A, B) \ ++ if ((tape_new_stats[i].A == tape_old_stats[i].A) || \ ++ (duration <= 0)) { \ ++ stats->B = 0; \ ++ } else { \ ++ temp = (double) (tape_new_stats[i].A - \ ++ tape_old_stats[i].A) \ ++ / (((double) duration)); \ ++ stats->B = (uint64_t) (100.0 * temp / 1000000.0); \ ++ if (stats->B > TAPE_MAX_PCT) \ ++ stats->B = TAPE_MAX_PCT; \ ++ } ++ ++struct tape_stats { ++ uint64_t read_time; ++ uint64_t write_time; ++ uint64_t other_time; ++ uint64_t read_bytes; ++ uint64_t write_bytes; ++ uint64_t read_count; ++ uint64_t write_count; ++ uint64_t other_count; ++ uint64_t resid_count; ++ char valid; ++ struct timeval tv; ++}; ++struct calc_stats { ++ uint64_t reads_per_second; ++ uint64_t writes_per_second; ++ uint64_t other_per_second; ++ uint64_t kbytes_read_per_second; ++ uint64_t kbytes_written_per_second; ++ uint64_t read_pct_wait; ++ uint64_t write_pct_wait; ++ uint64_t all_pct_wait; ++ uint64_t resids_per_second; ++}; ++ ++void tape_get_updated_stats(void); ++void tape_gather_initial_stats(void); ++void tape_check_tapes_and_realloc(void); ++int get_max_tape_drives(void); ++void tape_uninitialise(void); ++void tape_initialise(void); ++void tape_calc_one_stats(struct calc_stats *, int); ++void tape_write_headings(void); ++void tape_write_stats(struct calc_stats *, int); ++ ++#endif /* _TAPESTAT_H */ diff --git a/SPECS/sysstat.spec b/SPECS/sysstat.spec index e2d90e2..ec0b449 100644 --- a/SPECS/sysstat.spec +++ b/SPECS/sysstat.spec @@ -1,7 +1,7 @@ Summary: Collection of performance monitoring tools for Linux Name: sysstat Version: 10.1.5 -Release: 7%{?dist} +Release: 11%{?dist} License: GPLv2+ Group: Applications/System URL: http://sebastien.godard.pagesperso-orange.fr/ @@ -22,6 +22,18 @@ Patch5: sysstat-10.1.5-zip-conf.patch Patch6: sysstat-10.1.5-single-cpu-cifs.patch # fixes 1162773 Patch7: sysstat-10.1.5-elapsed-time.patch +# fixes 1224882 +Patch8: sysstat-10.1.5-pids-prealloc.patch +# fixes 1258990 +Patch9: sysstat-10.1.5-max-cpus.patch +# fixes 1267972 +Patch10: sysstat-10.1.5-max-name-len.patch +# fixes 1328490 +Patch11: sysstat-10.1.5-int-handler.patch +# fixes 846699 +Patch12: sysstat-10.1.5-nfsiostat.patch +# fixes 1332662 +Patch13: sysstat-10.1.5-tapestat.patch Requires: /etc/cron.d, fileutils, grep, sh-utils, textutils Requires(post): systemd, systemd-sysv @@ -32,8 +44,8 @@ BuildRequires: %{_includedir}/linux/if.h, gettext, lm_sensors-devel, perl BuildRequires: systemd %description -The sysstat package contains sar, sadf, mpstat, iostat, pidstat, nfsiostat, -cifsiostat and sa tools for Linux. +The sysstat package contains sar, sadf, mpstat, iostat, pidstat, nfsiostat-sysstat, +tapestat, cifsiostat and sa tools for Linux. The sar command collects and reports system activity information. This information can be saved in a file in a binary format for future inspection. The statistics reported by sar concern I/O transfer rates, paging activity, @@ -43,9 +55,10 @@ others. Both UP and SMP machines are fully supported. The sadf command may be used to display data collected by sar in various formats (CSV, XML, etc.). The iostat command reports CPU utilization and I/O statistics for disks. +The tapestat command reports statistics for tapes connected to the system. The mpstat command reports global and per-processor statistics. The pidstat command reports statistics for Linux tasks (processes). -The nfsiostat command reports I/O statistics for network file systems. +The nfsiostat-sysstat command reports I/O statistics for network file systems. The cifsiostat command reports I/O statistics for CIFS file systems. %prep @@ -58,6 +71,12 @@ The cifsiostat command reports I/O statistics for CIFS file systems. %patch5 -p1 -b .zip-conf %patch6 -p1 -b .single-cpu-cifs %patch7 -p1 -b .elapsed-time +%patch8 -p1 -b .pids-prealloc +%patch9 -p1 -b .max-cpus +%patch10 -p1 -b .max-name-len +%patch11 -p1 -b .int-handler +%patch12 -p1 -b .nfsiostat +%patch13 -p1 -b .tapestat iconv -f windows-1252 -t utf8 CREDITS > CREDITS.aux mv CREDITS.aux CREDITS @@ -118,6 +137,33 @@ fi %{_localstatedir}/log/sa %changelog +* Wed Jun 01 2016 Peter Schiffer - 10.1.5-11 +- related: #1332662 + fixed more coverity issues + +* Tue May 31 2016 Peter Schiffer - 10.1.5-10 +- related: #846699, #1332662 + fixed coverity issues + +* Thu May 05 2016 Peter Schiffer - 10.1.5-9 +- related: #846699 + do the renaming of nfsiostat properly, with patch +- resolves: #1332662 + added tapestat utility for monitoring tape drives + +* Fri Apr 29 2016 Peter Schiffer - 10.1.5-8 +- resolves: #846699 + renamed nfsiostat to nfsiostat-sysstat, as nfsiostat is also provided + by the nfs-utils package +- resolves: #1224882 + fixed bug when pidstat could run out of pre-allocated space for PIDs +- resolves: #1258990 + added support for more CPUs +- resolves: #1267972 + fixed bug when iostat didn't display the full device name if it's too long +- resolves: #1328490 + fixed bug, when sadc could forward SIGINIT to init process and couse reboot + * Wed Nov 12 2014 Peter Schiffer - 10.1.5-7 - resolves: #1162773 fixed incorrect description of util field on iostat(1) and sar(1) man pages