/* *************************************************************************** * 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/stat file... * Useful at least for CPU utilization. * May be useful to get disk stats if /sys not available. *************************************************************************** */ void read_proc_stat(int curr, int flags) { FILE *fp; char line[8192]; int pos, i; unsigned long v_tmp[4]; unsigned int v_major, v_index; struct io_stats *st_iodev_tmp[4]; unsigned long long cc_idle, cc_iowait, cc_steal; unsigned long long cc_user, cc_nice, cc_system, cc_hardirq, cc_softirq; /* * Prepare pointers on the 4 disk structures in case we have a * /proc/stat file with "disk_rblk", etc. entries. */ for (i = 0; i < 4; i++) st_iodev_tmp[i] = st_iodev[curr] + i; if ((fp = fopen(STAT, "r")) == NULL) { perror("fopen"); exit(2); } while (fgets(line, 8192, fp) != NULL) { if (!strncmp(line, "cpu ", 4)) { /* * Read the number of jiffies spent in the different modes, * and compute system uptime in jiffies (1/100ths of a second * if HZ=100). * Some fields are only present in 2.6 kernels. */ comm_stats[curr].cpu_iowait = 0; /* For pre 2.6 kernels */ comm_stats[curr].cpu_steal = 0; cc_hardirq = cc_softirq = 0; /* CPU counters became unsigned long long with kernel 2.6.5 */ sscanf(line + 5, "%llu %llu %llu %llu %llu %llu %llu %llu", &(comm_stats[curr].cpu_user), &(comm_stats[curr].cpu_nice), &(comm_stats[curr].cpu_system), &(comm_stats[curr].cpu_idle), &(comm_stats[curr].cpu_iowait), &cc_hardirq, &cc_softirq, &(comm_stats[curr].cpu_steal)); /* * Time spent in system mode also includes time spent servicing * hard interrupts and softirqs. */ comm_stats[curr].cpu_system += cc_hardirq + cc_softirq; /* * Compute system uptime in jiffies. * Uptime is multiplied by the number of processors. */ comm_stats[curr].uptime = comm_stats[curr].cpu_user + comm_stats[curr].cpu_nice + comm_stats[curr].cpu_system + comm_stats[curr].cpu_idle + comm_stats[curr].cpu_iowait + comm_stats[curr].cpu_steal; } else if ((!strncmp(line, "cpu0", 4)) && (cpu_nr > 1)) { /* * Read CPU line for proc#0 (if available). * This is necessary to compute time interval since * processors may be disabled (offline) sometimes. * (Assume that proc#0 can never be offline). */ cc_iowait = cc_hardirq = cc_softirq = cc_steal = 0; sscanf(line + 5, "%llu %llu %llu %llu %llu %llu %llu %llu", &cc_user, &cc_nice, &cc_system, &cc_idle, &cc_iowait, &cc_hardirq, &cc_softirq, &cc_steal); comm_stats[curr].uptime0 = cc_user + cc_nice + cc_system + cc_idle + cc_iowait + cc_hardirq + cc_softirq + cc_steal; } else if (DISPLAY_EXTENDED(flags) || HAS_DISKSTATS(flags) || HAS_PPARTITIONS(flags) || HAS_SYSFS(flags)) /* * When displaying extended statistics, or if /proc/diskstats or * /proc/partitions exists, or /sys is mounted, * we just need to get CPU info from /proc/stat. */ continue; else if (!strncmp(line, "disk_rblk ", 10)) { /* * Read the number of blocks read from disk. * A block is of indeterminate size. * The size may vary depending on the device type. */ sscanf(line + 10, "%lu %lu %lu %lu", &v_tmp[0], &v_tmp[1], &v_tmp[2], &v_tmp[3]); st_iodev_tmp[0]->dk_drive_rblk = v_tmp[0]; st_iodev_tmp[1]->dk_drive_rblk = v_tmp[1]; st_iodev_tmp[2]->dk_drive_rblk = v_tmp[2]; st_iodev_tmp[3]->dk_drive_rblk = v_tmp[3]; } else if (!strncmp(line, "disk_wblk ", 10)) { /* Read the number of blocks written to disk */ sscanf(line + 10, "%lu %lu %lu %lu", &v_tmp[0], &v_tmp[1], &v_tmp[2], &v_tmp[3]); st_iodev_tmp[0]->dk_drive_wblk = v_tmp[0]; st_iodev_tmp[1]->dk_drive_wblk = v_tmp[1]; st_iodev_tmp[2]->dk_drive_wblk = v_tmp[2]; st_iodev_tmp[3]->dk_drive_wblk = v_tmp[3]; } else if (!strncmp(line, "disk ", 5)) { /* Read the number of I/O done since the last reboot */ sscanf(line + 5, "%lu %lu %lu %lu", &v_tmp[0], &v_tmp[1], &v_tmp[2], &v_tmp[3]); st_iodev_tmp[0]->dk_drive = v_tmp[0]; st_iodev_tmp[1]->dk_drive = v_tmp[1]; st_iodev_tmp[2]->dk_drive = v_tmp[2]; st_iodev_tmp[3]->dk_drive = v_tmp[3]; } else if (!strncmp(line, "disk_io: ", 9)) { struct io_stats sdev; char dev_name[MAX_NAME_LEN]; pos = 9; /* Every disk_io entry is potentially unregistered */ set_entries_inactive(iodev_nr); /* Read disks I/O statistics (for 2.4 kernels) */ while (pos < strlen(line) - 1) { /* Beware: a CR is already included in the line */ sscanf(line + pos, "(%u,%u):(%lu,%*u,%lu,%*u,%lu) ", &v_major, &v_index, &v_tmp[0], &v_tmp[1], &v_tmp[2]); sprintf(dev_name, "dev%d-%d", v_major, v_index); sdev.dk_drive = v_tmp[0]; sdev.dk_drive_rblk = v_tmp[1]; sdev.dk_drive_wblk = v_tmp[2]; save_dev_stats(dev_name, curr, &sdev); pos += strcspn(line + pos, " ") + 1; } /* Free structures corresponding to unregistered disks */ free_inactive_entries(iodev_nr); } } fclose(fp); }
/* *************************************************************************** * 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); }
/* *************************************************************************** * 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); } } }