/* *************************************************************************** * Allocate and init structures, according to system state. *************************************************************************** */ void io_sys_init(void) { /* Allocate and init stat common counters */ init_stats(); /* How many processors on this machine? */ cpu_nr = get_cpu_nr(~0, FALSE); /* Get number of block devices and partitions in /proc/diskstats */ if ((iodev_nr = get_diskstats_dev_nr(CNT_PART, CNT_ALL_DEV)) > 0) { flags |= I_F_HAS_DISKSTATS; iodev_nr += NR_DEV_PREALLOC; } if (!HAS_DISKSTATS(flags) || (DISPLAY_PARTITIONS(flags) && !DISPLAY_PART_ALL(flags))) { /* * If /proc/diskstats exists but we also want stats for the partitions * of a particular device, stats will have to be found in /sys. So we * need to know if /sys is mounted or not, and set flags accordingly. */ /* Get number of block devices (and partitions) in sysfs */ if ((iodev_nr = get_sysfs_dev_nr(DISPLAY_PARTITIONS(flags))) > 0) { flags |= I_F_HAS_SYSFS; iodev_nr += NR_DEV_PREALLOC; } else { fprintf(stderr, _("Cannot find disk data\n")); exit(2); } } /* Also allocate stat structures for "group" devices */ iodev_nr += group_nr; /* * Allocate structures for number of disks found, but also * for groups of devices if option -g has been entered on the command line. * iodev_nr must be <> 0. */ salloc_device(iodev_nr); }
/* *************************************************************************** * Read stats from /proc/diskstats. * * IN: * @curr Index in array for current sample statistics. *************************************************************************** */ void read_diskstats_stat(int curr) { FILE *fp; char line[256], dev_name[MAX_NAME_LEN]; char *dm_name; struct io_stats sdev; int i; unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks; unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_ios; unsigned long wr_merges, rd_sec_or_wr_ios, wr_sec; char *ioc_dname; unsigned int major, minor; /* Every I/O device entry is potentially unregistered */ set_entries_unregistered(iodev_nr, st_hdr_iodev); if ((fp = fopen(DISKSTATS, "r")) == NULL) return; while (fgets(line, sizeof(line), fp) != NULL) { /* major minor name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq */ i = sscanf(line, "%u %u %s %lu %lu %lu %lu %lu %lu %lu %u %u %u %u", &major, &minor, dev_name, &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec, &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks); if (i == 14) { /* Device or partition */ if (!dlist_idx && !DISPLAY_PARTITIONS(flags) && !is_device(dev_name, ACCEPT_VIRTUAL_DEVICES)) continue; sdev.rd_ios = rd_ios; sdev.rd_merges = rd_merges_or_rd_sec; sdev.rd_sectors = rd_sec_or_wr_ios; sdev.rd_ticks = (unsigned int) rd_ticks_or_wr_sec; sdev.wr_ios = wr_ios; sdev.wr_merges = wr_merges; sdev.wr_sectors = wr_sec; sdev.wr_ticks = wr_ticks; sdev.ios_pgr = ios_pgr; sdev.tot_ticks = tot_ticks; sdev.rq_ticks = rq_ticks; } else if (i == 7) { /* Partition without extended statistics */ if (DISPLAY_EXTENDED(flags) || (!dlist_idx && !DISPLAY_PARTITIONS(flags))) continue; sdev.rd_ios = rd_ios; sdev.rd_sectors = rd_merges_or_rd_sec; sdev.wr_ios = rd_sec_or_wr_ios; sdev.wr_sectors = rd_ticks_or_wr_sec; } else /* Unknown entry: Ignore it */ continue; if ((ioc_dname = ioc_name(major, minor)) != NULL) { if (strcmp(dev_name, ioc_dname) && strcmp(ioc_dname, K_NODEV)) { /* * No match: Use name generated from sysstat.ioconf data * (if different from "nodev") works around known issues * with EMC PowerPath. */ strncpy(dev_name, ioc_dname, MAX_NAME_LEN - 1); dev_name[MAX_NAME_LEN - 1] = '\0'; } } if ((DISPLAY_DEVMAP_NAME(flags)) && (major == dm_major)) { /* * If the device is a device mapper device, try to get its * assigned name of its logical device. */ dm_name = transform_devmapname(major, minor); if (dm_name) { strncpy(dev_name, dm_name, MAX_NAME_LEN - 1); dev_name[MAX_NAME_LEN - 1] = '\0'; } } save_stats(dev_name, curr, &sdev, iodev_nr, st_hdr_iodev); } fclose(fp); /* Free structures corresponding to unregistered devices */ free_unregistered_entries(iodev_nr, st_hdr_iodev); }
/* *************************************************************************** * Main entry to the iostat program. *************************************************************************** */ int main(int argc, char **argv) { int it = 0; int opt = 1; int i, report_set = FALSE; long count = 1; struct utsname header; struct io_dlist *st_dev_list_i; struct tm rectime; char *t, *persist_devname, *devname; #ifdef USE_NLS /* Init National Language Support */ init_nls(); #endif /* Get HZ */ get_HZ(); /* Allocate structures for device list */ if (argc > 1) { salloc_dev_list(argc - 1 + count_csvalues(argc, argv)); } /* Process args... */ while (opt < argc) { /* -p option used individually. See below for grouped use */ if (!strcmp(argv[opt], "-p")) { flags |= I_D_PARTITIONS; if (argv[++opt] && (strspn(argv[opt], DIGITS) != strlen(argv[opt])) && (strncmp(argv[opt], "-", 1))) { flags |= I_D_UNFILTERED; for (t = strtok(argv[opt], ","); t; t = strtok(NULL, ",")) { if (!strcmp(t, K_ALL)) { flags |= I_D_PART_ALL; } else { devname = device_name(t); if (DISPLAY_PERSIST_NAME_I(flags)) { /* Get device persistent name */ persist_devname = get_pretty_name_from_persistent(devname); if (persist_devname != NULL) { devname = persist_devname; } } /* Store device name */ i = update_dev_list(&dlist_idx, devname); st_dev_list_i = st_dev_list + i; st_dev_list_i->disp_part = TRUE; } } opt++; } else { flags |= I_D_PART_ALL; } } else if (!strcmp(argv[opt], "-g")) { /* * Option -g: Stats for a group of devices. * group_name contains the last group name entered on * the command line. If we define an additional one, save * the previous one in the list. We do that this way because we * want the group name to appear in the list _after_ all * the devices included in that group. The last group name * will be saved in the list later, in presave_device_list() function. */ if (group_nr > 0) { update_dev_list(&dlist_idx, group_name); } if (argv[++opt]) { /* * MAX_NAME_LEN - 2: one char for the heading space, * and one for the trailing '\0'. */ snprintf(group_name, MAX_NAME_LEN, " %-.*s", MAX_NAME_LEN - 2, argv[opt++]); } else { usage(argv[0]); } group_nr++; } else if (!strcmp(argv[opt], "-j")) { if (argv[++opt]) { if (strnlen(argv[opt], MAX_FILE_LEN) >= MAX_FILE_LEN - 1) { usage(argv[0]); } strncpy(persistent_name_type, argv[opt], MAX_FILE_LEN - 1); persistent_name_type[MAX_FILE_LEN - 1] = '\0'; strtolower(persistent_name_type); /* Check that this is a valid type of persistent device name */ if (!get_persistent_type_dir(persistent_name_type)) { fprintf(stderr, _("Invalid type of persistent device name\n")); exit(1); } /* * Persistent names are usually long: Display * them as human readable by default. */ flags |= I_D_PERSIST_NAME + I_D_HUMAN_READ; opt++; } else { usage(argv[0]); } } #ifdef DEBUG else if (!strcmp(argv[opt], "--debuginfo")) { flags |= I_D_DEBUG; opt++; } #endif else if (!strncmp(argv[opt], "-", 1)) { for (i = 1; *(argv[opt] + i); i++) { switch (*(argv[opt] + i)) { case 'c': /* Display cpu usage */ flags |= I_D_CPU; report_set = TRUE; break; case 'd': /* Display disk utilization */ flags |= I_D_DISK; report_set = TRUE; break; case 'h': /* * Display device utilization report * in a human readable format. */ 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 'N': /* Display device mapper logical name */ flags |= I_D_DEVMAP_NAME; break; case 'p': /* If option -p is grouped then it cannot take an arg */ flags |= I_D_PARTITIONS + I_D_PART_ALL; break; case 'T': /* Display stats only for the groups */ flags |= I_D_GROUP_TOTAL_ONLY; break; case 't': /* Display timestamp */ flags |= I_D_TIMESTAMP; break; case 'x': /* Display extended stats */ flags |= I_D_EXTENDED; break; case 'y': /* Don't display stats since system restart */ flags |= I_D_OMIT_SINCE_BOOT; break; case 'z': /* Omit output for devices with no activity */ flags |= I_D_ZERO_OMIT; break; case 'V': /* Print version number and exit */ print_version(); break; default: usage(argv[0]); } } opt++; } else if (!isdigit(argv[opt][0])) { /* * By default iostat doesn't display unused devices. * If some devices are explictly entered on the command line * then don't apply this rule any more. */ flags |= I_D_UNFILTERED; if (strcmp(argv[opt], K_ALL)) { /* Store device name entered on the command line */ devname = device_name(argv[opt++]); if (DISPLAY_PERSIST_NAME_I(flags)) { persist_devname = get_pretty_name_from_persistent(devname); if (persist_devname != NULL) { devname = persist_devname; } } update_dev_list(&dlist_idx, devname); } else { 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; } /* Default: Display CPU and DISK reports */ if (!report_set) { flags |= I_D_CPU + I_D_DISK; } /* * Also display DISK reports if options -p, -x or a device has been entered * on the command line. */ if (DISPLAY_PARTITIONS(flags) || DISPLAY_EXTENDED(flags) || DISPLAY_UNFILTERED(flags)) { flags |= I_D_DISK; } /* Option -T can only be used with option -g */ if (DISPLAY_GROUP_TOTAL_ONLY(flags) && !group_nr) { usage(argv[0]); } /* Select disk output unit (kB/s or blocks/s) */ set_disk_output_unit(); /* Ignore device list if '-p ALL' entered on the command line */ if (DISPLAY_PART_ALL(flags)) { dlist_idx = 0; } if (DISPLAY_DEVMAP_NAME(flags)) { dm_major = get_devmap_major(); } /* Init structures according to machine architecture */ io_sys_init(); if (group_nr > 0) { /* * If groups of devices have been defined * then save devices and groups in the list. */ presave_device_list(); } 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 = alarm_handler; sigaction(SIGALRM, &alrm_act, NULL); alarm(interval); /* Main loop */ rw_io_stat_loop(count, &rectime); /* Free structures */ io_sys_free(); sfree_dev_list(); return 0; }
/* *************************************************************************** * Main loop: Read I/O 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; 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 (cpu_nr > 1) { /* * Read system uptime (only for SMP machines). * Init uptime0. So if /proc/uptime cannot fill it, * this will be done by /proc/stat. */ uptime0[curr] = 0; read_uptime(&(uptime0[curr])); } /* * Read stats for CPU "all" and 0. * Note that stats for CPU 0 are not used per se. It only makes * read_stat_cpu() fill uptime0. */ read_stat_cpu(st_cpu[curr], 2, &(uptime[curr]), &(uptime0[curr])); if (dlist_idx) { /* * A device or partition name was explicitly entered * on the command line, with or without -p option * (but not -p ALL). */ if (HAS_DISKSTATS(flags) && !DISPLAY_PARTITIONS(flags)) { read_diskstats_stat(curr); } else if (HAS_SYSFS(flags)) { read_sysfs_dlist_stat(curr); } } else { /* * No devices nor partitions entered on the command line * (for example if -p ALL was used). */ if (HAS_DISKSTATS(flags)) { read_diskstats_stat(curr); } else if (HAS_SYSFS(flags)) { read_sysfs_stat(curr); } } /* Compute device groups stats */ if (group_nr > 0) { compute_device_groups_stats(curr); } /* Get time */ get_localtime(rectime, 0); /* Check whether we should skip first report */ if (!skip) { /* Print results */ write_stats(curr, rectime); if (count > 0) { count--; } } else { skip = 0; } if (count) { curr ^= 1; pause(); } } while (count); }
/* *************************************************************************** * Read stats from /proc/diskstats *************************************************************************** */ void read_diskstats_stat(int curr, int flags) { FILE *fp; char line[256], dev_name[MAX_NAME_LEN]; struct io_stats sdev; int i; unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_ios; unsigned long ios_pgr, tot_ticks, rq_ticks, wr_merges, wr_ticks; unsigned long long rd_sec_or_wr_ios, wr_sec; char *ioc_dname; unsigned int major, minor; /* Every I/O device entry is potentially unregistered */ set_entries_inactive(iodev_nr); if ((fp = fopen(DISKSTATS, "r")) == NULL) return; while (fgets(line, 256, fp) != NULL) { /* major minor name rio rmerge rsect ruse wio wmerge wsect wuse running use aveq */ i = sscanf(line, "%u %u %s %lu %lu %llu %lu %lu %lu %llu %lu %lu %lu %lu", &major, &minor, dev_name, &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios, &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec, &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks); if (i == 14) { /* Device */ sdev.rd_ios = rd_ios; sdev.rd_merges = rd_merges_or_rd_sec; sdev.rd_sectors = rd_sec_or_wr_ios; sdev.rd_ticks = rd_ticks_or_wr_sec; sdev.wr_ios = wr_ios; sdev.wr_merges = wr_merges; sdev.wr_sectors = wr_sec; sdev.wr_ticks = wr_ticks; sdev.ios_pgr = ios_pgr; sdev.tot_ticks = tot_ticks; sdev.rq_ticks = rq_ticks; } else if (i == 7) { /* Partition */ if (DISPLAY_EXTENDED(flags) || (!dlist_idx && !DISPLAY_PARTITIONS(flags))) continue; sdev.rd_ios = rd_ios; sdev.rd_sectors = rd_merges_or_rd_sec; sdev.wr_ios = rd_sec_or_wr_ios; sdev.wr_sectors = rd_ticks_or_wr_sec; } else /* Unknown entry: ignore it */ continue; if ((ioc_dname = ioc_name(major, minor)) != NULL) { if (strcmp(dev_name, ioc_dname) && strcmp(ioc_dname, K_NODEV)) /* * No match: Use name generated from sysstat.ioconf data (if different * from "nodev") works around known issues with EMC PowerPath. */ strcpy(dev_name, ioc_dname); } save_dev_stats(dev_name, curr, &sdev); } fclose(fp); /* Free structures corresponding to unregistered devices */ free_inactive_entries(iodev_nr); }
/* *************************************************************************** * Allocate and init structures, according to system state *************************************************************************** */ void io_sys_init(int *flags) { int i; /* Init stat common counters */ init_stats(); /* How many processors on this machine ? */ cpu_nr = get_cpu_nr(~0); /* Get number of block devices and partitions in /proc/diskstats */ if ((iodev_nr = get_diskstats_dev_nr(CNT_PART, CNT_ALL_DEV)) > 0) { *flags |= I_F_HAS_DISKSTATS; iodev_nr += NR_DEV_PREALLOC; } if (!HAS_DISKSTATS(*flags) || (DISPLAY_PARTITIONS(*flags) && !DISPLAY_PART_ALL(*flags))) { /* * If /proc/diskstats exists but we also want stats for the partitions * of a particular device, stats will have to be found in /sys. So we * need to know if /sys is mounted or not, and set *flags accordingly. */ /* Get number of block devices (and partitions) in sysfs */ if ((iodev_nr = get_sysfs_dev_nr(DISPLAY_PARTITIONS(*flags))) > 0) { *flags |= I_F_HAS_SYSFS; iodev_nr += NR_DEV_PREALLOC; } /* * Get number of block devices and partitions in /proc/partitions, * those with statistics... */ else if ((iodev_nr = get_ppartitions_dev_nr(CNT_PART)) > 0) { *flags |= I_F_HAS_PPARTITIONS; iodev_nr += NR_DEV_PREALLOC; } /* Get number of "disk_io:" entries in /proc/stat */ else if ((iodev_nr = get_disk_io_nr()) > 0) { *flags |= I_F_PLAIN_KERNEL24; iodev_nr += NR_DISK_PREALLOC; } else { /* Assume we have an old kernel: stats for 4 disks are in /proc/stat */ iodev_nr = 4; *flags |= I_F_OLD_KERNEL; } } /* * Allocate structures for number of disks found. * iodev_nr must be <> 0. */ salloc_device(iodev_nr); if (HAS_OLD_KERNEL(*flags)) { struct io_hdr_stats *shi = st_hdr_iodev; /* * If we have an old kernel with the stats for the first four disks * in /proc/stat, then set the devices names to hdisk[0..3]. */ for (i = 0; i < 4; i++, shi++) { shi->used = TRUE; sprintf(shi->name, "%s%d", K_HDISK, i); } } }