From: Sebastien GODARD Date: Mon, 27 Apr 2020 13:17:28 +0000 (+0200) Subject: Add new flag to iostat that can be used to specify an alternate X-Git-Tag: v12.3.3~7 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e1b85a94a922710521ba1d83a8a682fac6171424;p=sysstat Add new flag to iostat that can be used to specify an alternate directory for devices statistics. Example: iostat -f /altdir [...] -> Use directory to read devices statistics. iostat +f /altdir [...] -> Use standard kernel files *and* to read devices statistics. is a directory containing files with statistics for devices managed in userspace. may contain: - a diskstats file whose format is compliant with that located in /proc. - statistics for individual devices contained in files whose format is compliant with that of files located in /sys. In particular, the following files located in may be used by iostat: /block//stat /block///stat files must have an entry in /dev/block/ directory, e.g.: /dev/block/[major]:[minor] --> ../../block// Notes: 1) iostat uses the /proc/diskstats file to read statistics only when "-p all" has been entered on the command line (read statistics for all the devices and their partitions). 2) iostat uses the /sys/block//stat files to read the statistics for a device (e.g. "iostat sda") and possibly all its partitions (e.g. "iostat -p sda"). 3) iostat uses the link in /sys/dev/block/[major]:[minor] to know where the stat file is located for a partition that has been entered on the command line (e.g. "iostat sda3"). The partition must exist in /dev to get its major and minor numbers. Signed-off-by: Sebastien GODARD --- diff --git a/common.c b/common.c index e8aef69..ce4f58a 100644 --- a/common.c +++ b/common.c @@ -189,6 +189,7 @@ void init_nls(void) * ioconf.c which should be used only with kernels that don't have sysfs. * * IN: + * @sysdev sysfs location. * @name Device or partition name. * @allow_virtual TRUE if virtual devices are also accepted. * The device is assumed to be virtual if no @@ -198,7 +199,7 @@ void init_nls(void) * TRUE if @name is not a partition. *************************************************************************** */ -int is_device(char *name, int allow_virtual) +int is_device(char *sysdev, char *name, int allow_virtual) { char syspath[PATH_MAX]; char *slash; @@ -207,7 +208,7 @@ int is_device(char *name, int allow_virtual) while ((slash = strchr(name, '/'))) { *slash = '!'; } - snprintf(syspath, sizeof(syspath), "%s/%s%s", SYSFS_BLOCK, name, + snprintf(syspath, sizeof(syspath), "%s/%s/%s%s", sysdev, __BLOCK, name, allow_virtual ? "" : "/device"); return !(access(syspath, F_OK)); diff --git a/common.h b/common.h index adbb774..f8a8dd4 100644 --- a/common.h +++ b/common.h @@ -68,13 +68,18 @@ #define K_JSON "JSON" /* Files */ +#define __DISKSTATS "diskstats" +#define __BLOCK "block" +#define __DEV_BLOCK "dev/block" +#define SLASH_SYS PRE "/sys" +#define SLASH_DEV PRE "/dev/" #define STAT PRE "/proc/stat" #define UPTIME PRE "/proc/uptime" -#define DISKSTATS PRE "/proc/diskstats" +#define DISKSTATS PRE "/proc/" __DISKSTATS #define INTERRUPTS PRE "/proc/interrupts" #define MEMINFO PRE "/proc/meminfo" -#define SYSFS_BLOCK PRE "/sys/block" -#define SYSFS_DEV_BLOCK PRE "/sys/dev/block" +#define SYSFS_BLOCK SLASH_SYS "/" __BLOCK +#define SYSFS_DEV_BLOCK SLASH_SYS "/" __DEV_BLOCK #define SYSFS_DEVCPU PRE "/sys/devices/system/cpu" #define SYSFS_TIME_IN_STATE "cpufreq/stats/time_in_state" #define S_STAT "stat" @@ -89,7 +94,6 @@ #define SYSFS_MANUFACTURER "manufacturer" #define SYSFS_PRODUCT "product" #define SYSFS_FCHOST PRE "/sys/class/fc_host" -#define SLASH_DEV PRE "/dev/" #define MAX_FILE_LEN 512 #define MAX_PF_NAME 1024 @@ -240,7 +244,7 @@ time_t get_time void init_nls (void); int is_device - (char *, int); + (char *, char *, int); void sysstat_panic (const char *, int); int extract_wwnid diff --git a/count.c b/count.c index d84ae7a..58e68ed 100644 --- a/count.c +++ b/count.c @@ -250,7 +250,7 @@ __nr_t get_diskstats_dev_nr(int count_part, int only_used_dev) if (!count_part) { i = sscanf(line, "%*d %*d %s %lu %*u %*u %*u %lu", dev_name, &rd_ios, &wr_ios); - if ((i == 2) || !is_device(dev_name, ACCEPT_VIRTUAL_DEVICES)) + if ((i == 2) || !is_device(SLASH_SYS, dev_name, ACCEPT_VIRTUAL_DEVICES)) /* It was a partition and not a device */ continue; if (only_used_dev && !rd_ios && !wr_ios) diff --git a/iostat.c b/iostat.c index 86eb8d7..417197d 100644 --- a/iostat.c +++ b/iostat.c @@ -71,6 +71,7 @@ unsigned int dm_major; /* Device-mapper major number */ long interval = 0; char timestamp[TIMESTAMP_LEN]; +char alt_dir[MAX_FILE_LEN]; struct sigaction alrm_act, int_act; int sigint_caught = 0; @@ -90,14 +91,14 @@ void usage(char *progname) #ifdef DEBUG fprintf(stderr, _("Options are:\n" "[ -c ] [ -d ] [ -h ] [ -k | -m ] [ -N ] [ -s ] [ -t ] [ -V ] [ -x ] [ -y ] [ -z ]\n" - "[ -j { ID | LABEL | PATH | UUID | ... } ]\n" + "[ { -f | +f } ] [ -j { ID | LABEL | PATH | UUID | ... } ]\n" "[ --dec={ 0 | 1 | 2 } ] [ --human ] [ -o JSON ]\n" "[ [ -H ] -g ] [ -p [ [,...] | ALL ] ]\n" "[ [...] | ALL ] [ --debuginfo ]\n")); #else fprintf(stderr, _("Options are:\n" "[ -c ] [ -d ] [ -h ] [ -k | -m ] [ -N ] [ -s ] [ -t ] [ -V ] [ -x ] [ -y ] [ -z ]\n" - "[ -j { ID | LABEL | PATH | UUID | ... } ]\n" + "[ { -f | +f } ] [ -j { ID | LABEL | PATH | UUID | ... } ]\n" "[ --dec={ 0 | 1 | 2 } ] [ --human ] [ -o JSON ]\n" "[ [ -H ] -g ] [ -p [ [,...] | ALL ] ]\n" "[ [...] | ALL ]\n")); @@ -207,7 +208,7 @@ void set_devices_nonexistent(struct io_device *dlist) struct io_device *add_list_device(struct io_device **dlist, char *name, int dtype) { struct io_device *d, *ds; - int i; + int i, rc = 0; if (strnlen(name, MAX_NAME_LEN) == MAX_NAME_LEN) /* Device name is too long */ @@ -261,12 +262,22 @@ struct io_device *add_list_device(struct io_device **dlist, char *name, int dtyp if (dtype == T_GROUP) { d->dev_tp = dtype; } - else if (is_device(name, ACCEPT_VIRTUAL_DEVICES)) { - d->dev_tp = (dtype == T_PART_DEV ? T_PART_DEV : T_DEV); - } - else { - /* This is a partition (T_PART) */ - d->dev_tp = T_PART; + else { + if (!alt_dir[0] || USE_ALL_DIR(flags)) { + rc = is_device(SLASH_SYS, name, ACCEPT_VIRTUAL_DEVICES); + } + + if (alt_dir[0] && (!USE_ALL_DIR(flags) || (USE_ALL_DIR(flags) && !rc))) { + rc = is_device(alt_dir, name, ACCEPT_VIRTUAL_DEVICES); + } + + if (rc) { + d->dev_tp = (dtype == T_PART_DEV ? T_PART_DEV : T_DEV); + } + else { + /* This is a partition (T_PART) */ + d->dev_tp = T_PART; + } } return d; @@ -328,7 +339,7 @@ int get_major_minor_nr(char filename[], int *major, int *minor) * 0 on success, -1 otherwise. *************************************************************************** */ -int read_sysfs_file_stat(char *filename, struct io_stats *ios) +int read_sysfs_file_stat_work(char *filename, struct io_stats *ios) { FILE *fp; struct io_stats sdev; @@ -393,6 +404,48 @@ int read_sysfs_file_stat(char *filename, struct io_stats *ios) return 0; } +/* + *************************************************************************** + * Read sysfs stat for current whole device using /sys or an alternate + * location. + * + * IN: + * @devname Device name for which stats have to be read. + * @ios Structure where stats will be saved. + * + * OUT: + * @ios Structure where stats have been saved. + * + * RETURNS: + * 0 on success, -1 otherwise. + *************************************************************************** + */ +int read_sysfs_file_stat(char *devname, struct io_stats *ios) +{ + int rc = 0; + char dfile[MAX_PF_NAME]; + + if (!alt_dir[0] || USE_ALL_DIR(flags)) { + /* Read stats for current whole device using /sys/block/ directory */ + snprintf(dfile, sizeof(dfile), "%s/%s/%s/%s", + SLASH_SYS, __BLOCK, devname, S_STAT); + dfile[sizeof(dfile) - 1] = '\0'; + + rc = read_sysfs_file_stat_work(dfile, ios); + } + + if (alt_dir[0] && (!USE_ALL_DIR(flags) || (USE_ALL_DIR(flags) && (rc < 0)))) { + /* Read stats for current whole device using an alternate /sys directory */ + snprintf(dfile, sizeof(dfile), "%s/%s/%s/%s", + alt_dir, __BLOCK, devname, S_STAT); + dfile[sizeof(dfile) - 1] = '\0'; + + rc = read_sysfs_file_stat_work(dfile, ios); + } + + return rc; +} + /* *************************************************************************** * Read sysfs stats for all the partitions of a whole device. Devices are @@ -401,12 +454,13 @@ int read_sysfs_file_stat(char *filename, struct io_stats *ios) * IN: * @curr Index in array for current sample statistics. * @dname Whole device name. + * @sysdev sysfs location. * * RETURNS: * 0 on success, -1 otherwise. *************************************************************************** */ -int read_sysfs_device_part_stat(int curr, char *dname) +int read_sysfs_device_part_stat_work(int curr, char *dname, char *sysdev) { DIR *dir; struct dirent *drd; @@ -415,7 +469,7 @@ int read_sysfs_device_part_stat(int curr, char *dname) char dfile[MAX_PF_NAME], filename[MAX_PF_NAME + 512]; int major, minor; - snprintf(dfile, sizeof(dfile), "%s/%s", SYSFS_BLOCK, dname); + snprintf(dfile, sizeof(dfile), "%s/%s/%s", sysdev, __BLOCK, dname); dfile[sizeof(dfile) - 1] = '\0'; /* Open current device directory in /sys/block */ @@ -431,7 +485,7 @@ int read_sysfs_device_part_stat(int curr, char *dname) filename[sizeof(filename) - 1] = '\0'; /* Read current partition stats */ - if (read_sysfs_file_stat(filename, &sdev) < 0) + if (read_sysfs_file_stat_work(filename, &sdev) < 0) continue; d = add_list_device(&dev_list, drd->d_name, 0); @@ -454,6 +508,36 @@ int read_sysfs_device_part_stat(int curr, char *dname) return 0; } +/* + *************************************************************************** + * Read sysfs stats for all the partitions of a whole device. + * Stats are from /sys or an alternate directory. + * + * IN: + * @curr Index in array for current sample statistics. + * @dname Whole device name. + * + * RETURNS: + * 0 on success, -1 otherwise. + *************************************************************************** + */ +int read_sysfs_device_part_stat(int curr, char *dname) +{ + int rc = 0; + + if (!alt_dir[0] || USE_ALL_DIR(flags)) { + /* Read partition stats from /sys */ + rc = read_sysfs_device_part_stat_work(curr, dname, SLASH_SYS); + } + + if (alt_dir[0] && (!USE_ALL_DIR(flags) || (USE_ALL_DIR(flags) && (rc < 0)))) { + /* Read partition stats from an alternate /sys directory */ + rc = read_sysfs_device_part_stat_work(curr, dname, alt_dir); + } + + return rc; +} + /* *************************************************************************** * Read sysfs stats for every whole device. Devices are saved in the linked @@ -461,12 +545,13 @@ int read_sysfs_device_part_stat(int curr, char *dname) * * IN: * @curr Index in array for current sample statistics. + * @sysblock __sys/block directory location. * * RETURNS: * 0 on success, -1 otherwise. *************************************************************************** */ -int read_sysfs_all_devices_stat(int curr) +int read_sysfs_all_devices_stat_work(int curr, char *sysblock) { DIR *dir; struct dirent *drd; @@ -475,8 +560,8 @@ int read_sysfs_all_devices_stat(int curr) char dfile[MAX_PF_NAME]; int major, minor; - /* Open /sys/block directory */ - if ((dir = __opendir(SYSFS_BLOCK)) == NULL) + /* Open __sys/block directory */ + if ((dir = __opendir(sysblock)) == NULL) return -1; /* Get current entry */ @@ -484,11 +569,11 @@ int read_sysfs_all_devices_stat(int curr) if (!strcmp(drd->d_name, ".") || !strcmp(drd->d_name, "..")) continue; - snprintf(dfile, sizeof(dfile), "%s/%s/%s", SYSFS_BLOCK, drd->d_name, S_STAT); + snprintf(dfile, sizeof(dfile), "%s/%s/%s", sysblock, drd->d_name, S_STAT); dfile[sizeof(dfile) - 1] = '\0'; /* Read current whole device stats */ - if (read_sysfs_file_stat(dfile, &sdev) < 0) + if (read_sysfs_file_stat_work(dfile, &sdev) < 0) continue; d = add_list_device(&dev_list, drd->d_name, 0); @@ -513,17 +598,50 @@ int read_sysfs_all_devices_stat(int curr) /* *************************************************************************** - * Read sysfs stats for a partition using /sys/dev/block/M:m/ directory. + * Read sysfs stats for every whole device from /sys or an alternate + * location. + * + * IN: + * @curr Index in array for current sample statistics. + * + * RETURNS: + * 0 on success, -1 otherwise. + *************************************************************************** + */ +int read_sysfs_all_devices_stat(int curr) +{ + int rc = 0; + char sysblock[MAX_PF_NAME]; + + if (!alt_dir[0] || USE_ALL_DIR(flags)) { + /* Read all whole devices from /sys */ + rc = read_sysfs_all_devices_stat_work(curr, SYSFS_BLOCK); + } + + if (alt_dir[0]) { + snprintf(sysblock, sizeof(sysblock), "%s/%s", alt_dir, __BLOCK); + sysblock[sizeof(sysblock) - 1] = '\0'; + /* Read stats from an alternate sys location */ + rc = read_sysfs_all_devices_stat_work(curr, sysblock); + } + + return rc; +} + +/* + *************************************************************************** + * Read sysfs stats for a partition using __sys/dev/block/M:m/ directory. * * IN: * @curr Index in array for current sample statistics. * @d Device structure. + * @sysdev sysfs directory. * * RETURNS: * 0 on success, and -1 otherwise. *************************************************************************** */ -int read_sysfs_part_stat(int curr, struct io_device *d) +int read_sysfs_part_stat_work(int curr, struct io_device *d, char *sysdev) { char dfile[MAX_PF_NAME]; int major, minor; @@ -537,11 +655,41 @@ int read_sysfs_part_stat(int curr, struct io_device *d) } /* Read stats for device */ - snprintf(dfile, sizeof(dfile), "%s/%d:%d/%s", - SYSFS_DEV_BLOCK, d->major, d->minor, S_STAT); + snprintf(dfile, sizeof(dfile), "%s/%s/%d:%d/%s", + sysdev, __DEV_BLOCK, d->major, d->minor, S_STAT); dfile[sizeof(dfile) - 1] = '\0'; - return read_sysfs_file_stat(dfile, d->dev_stats[curr]); + return read_sysfs_file_stat_work(dfile, d->dev_stats[curr]); +} + +/* + *************************************************************************** + * Read sysfs stats for a partition using /sys/dev/block/M:m/ directory or + * an alternate directory. + * + * IN: + * @curr Index in array for current sample statistics. + * @d Device structure. + * + * RETURNS: + * 0 on success, and -1 otherwise. + *************************************************************************** + */ +int read_sysfs_part_stat(int curr, struct io_device *d) +{ + int rc = 0; + + if (!alt_dir[0] || USE_ALL_DIR(flags)) { + /* Read partition stats from /sys */ + rc = read_sysfs_part_stat_work(curr, d, SLASH_SYS); + } + + if (alt_dir[0] && (!USE_ALL_DIR(flags) || (USE_ALL_DIR(flags) && (rc < 0)))) { + /* Read partition stats from an alternate /sys directory */ + rc = read_sysfs_part_stat_work(curr, d, alt_dir); + } + + return rc; } /* @@ -556,7 +704,6 @@ int read_sysfs_part_stat(int curr, struct io_device *d) void read_sysfs_dlist_stat(int curr) { struct io_device *dlist; - char dfile[MAX_PF_NAME]; for (dlist = dev_list; dlist != NULL; dlist = dlist->next) { if (dlist->exist) @@ -575,10 +722,7 @@ void read_sysfs_dlist_stat(int curr) else if ((dlist->dev_tp == T_PART_DEV) || (dlist->dev_tp == T_DEV)) { /* Read stats for current whole device using /sys/block/ directory */ - snprintf(dfile, sizeof(dfile), "%s/%s/%s", - SYSFS_BLOCK, dlist->name, S_STAT); - dfile[sizeof(dfile) - 1] = '\0'; - if (read_sysfs_file_stat(dfile, dlist->dev_stats[curr]) == 0) { + if (read_sysfs_file_stat(dlist->name, dlist->dev_stats[curr]) == 0) { dlist->exist = TRUE; } @@ -597,14 +741,15 @@ void read_sysfs_dlist_stat(int curr) /* *************************************************************************** - * Read stats from /proc/diskstats. Only used when "-p ALL" has been entered - * on the command line. + * Read stats from the diskstats file. Only used when "-p ALL" has been + * entered on the command line. * * IN: * @curr Index in array for current sample statistics. + * @diskstats Path to diskstats file (e.g. "/proc/diskstats"). *************************************************************************** */ -void read_diskstats_stat(int curr) +void read_diskstats_stat_work(int curr, char *diskstats) { FILE *fp; char line[256], dev_name[MAX_NAME_LEN]; @@ -617,7 +762,7 @@ void read_diskstats_stat(int curr) unsigned long dc_ios, dc_merges, dc_sec, fl_ios; unsigned int major, minor; - if ((fp = fopen(DISKSTATS, "r")) == NULL) + if ((fp = fopen(diskstats, "r")) == NULL) return; while (fgets(line, sizeof(line), fp) != NULL) { @@ -683,6 +828,32 @@ void read_diskstats_stat(int curr) fclose(fp); } +/* + *************************************************************************** + * Read stats from /proc/diskstats or an alternate diskstats file. + * Only used when "-p ALL" has been entered on the command line. + * + * IN: + * @curr Index in array for current sample statistics. + *************************************************************************** + */ +void read_diskstats_stat(int curr) +{ + char diskstats[MAX_PF_NAME]; + + if (!alt_dir[0] || USE_ALL_DIR(flags)) { + /* Read stats from /proc/diskstats */ + read_diskstats_stat_work(curr, DISKSTATS); + } + + if (alt_dir[0]) { + snprintf(diskstats, sizeof(diskstats), "%s/%s", alt_dir, __DISKSTATS); + diskstats[sizeof(diskstats) - 1] = '\0'; + /* Read stats from an alternate diskstats file */ + read_diskstats_stat_work(curr, diskstats); + } +} + /* *************************************************************************** * Add current device statistics to corresponding group. @@ -1894,6 +2065,8 @@ int main(int argc, char **argv) init_nls(); #endif + alt_dir[0] = '\0'; + /* Process args... */ while (opt < argc) { @@ -1963,6 +2136,20 @@ int main(int argc, char **argv) opt++; } + else if (!strcmp(argv[opt], "-f") || !strcmp(argv[opt], "+f")) { + if (alt_dir[0] || !argv[opt + 1]) { + usage(argv[0]); + } + if (argv[opt++][0] == '+') { + flags |= I_D_ALL_DIR; + } + strncpy(alt_dir, argv[opt++], MAX_FILE_LEN); + alt_dir[MAX_FILE_LEN - 1] = '\0'; + if (!check_dir(alt_dir)) { + usage(argv[0]); + } + } + else if (!strcmp(argv[opt], "-j")) { if (!argv[++opt]) { usage(argv[0]); diff --git a/iostat.h b/iostat.h index 2c45c04..ceabb73 100644 --- a/iostat.h +++ b/iostat.h @@ -15,7 +15,7 @@ #define I_D_EXTENDED 0x000008 #define I_D_EVERYTHING 0x000010 #define I_D_KILOBYTES 0x000020 -/* Unused 0x000040 */ +#define I_D_ALL_DIR 0x000040 #define I_D_DEBUG 0x000080 #define I_D_UNFILTERED 0x000100 #define I_D_MEGABYTES 0x000200 @@ -53,6 +53,7 @@ #define DISPLAY_JSON_OUTPUT(m) (((m) & I_D_JSON_OUTPUT) == I_D_JSON_OUTPUT) #define DISPLAY_UNIT(m) (((m) & I_D_UNIT) == I_D_UNIT) #define DISPLAY_SHORT_OUTPUT(m) (((m) & I_D_SHORT_OUTPUT) == I_D_SHORT_OUTPUT) +#define USE_ALL_DIR(m) (((m) & I_D_ALL_DIR) == I_D_ALL_DIR) #define T_PART 0 #define T_DEV 1 diff --git a/rd_stats.c b/rd_stats.c index 45b1be7..cec0262 100644 --- a/rd_stats.c +++ b/rd_stats.c @@ -753,7 +753,7 @@ __nr_t read_diskstats_io(struct stats_io *st_io) &wr_ios, &wr_sec, &dc_ios, &dc_sec) >= 7) { - if (is_device(dev_name, IGNORE_VIRTUAL_DEVICES)) { + if (is_device(SLASH_SYS, dev_name, IGNORE_VIRTUAL_DEVICES)) { /* * OK: It's a (real) device and not a partition. * Note: Structure should have been initialized first! @@ -828,7 +828,7 @@ __nr_t read_diskstats_disk(struct stats_disk *st_disk, __nr_t nr_alloc, if (!rd_ios && !wr_ios && !dc_ios) /* Unused device: Ignore it */ continue; - if (read_part || is_device(dev_name, ACCEPT_VIRTUAL_DEVICES)) { + if (read_part || is_device(SLASH_SYS, dev_name, ACCEPT_VIRTUAL_DEVICES)) { if (dsk_read + 1 > nr_alloc) { dsk_read = -1;