/* *************************************************************************** * 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 dev, i, fctr = 1; unsigned long long itv; struct io_hdr_stats *shi; struct io_dlist *st_dev_list_i; /* 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 is multiplied by the number of processors */ itv = get_interval(uptime[!curr], uptime[curr]); if (DISPLAY_CPU(flags)) { #ifdef DEBUG if (DISPLAY_DEBUG(flags)) { /* Debug output */ fprintf(stderr, "itv=%llu st_cpu[curr]{ cpu_user=%llu cpu_nice=%llu " "cpu_sys=%llu cpu_idle=%llu cpu_iowait=%llu cpu_steal=%llu " "cpu_hardirq=%llu cpu_softirq=%llu cpu_guest=%llu " "cpu_guest_nice=%llu }\n", itv, st_cpu[curr]->cpu_user, st_cpu[curr]->cpu_nice, st_cpu[curr]->cpu_sys, st_cpu[curr]->cpu_idle, st_cpu[curr]->cpu_iowait, st_cpu[curr]->cpu_steal, st_cpu[curr]->cpu_hardirq, st_cpu[curr]->cpu_softirq, st_cpu[curr]->cpu_guest, st_cpu[curr]->cpu_guest_nice); } #endif /* Display CPU utilization */ write_cpu_stat(curr, itv); } if (cpu_nr > 1) { /* On SMP machines, reduce itv to one processor (see note above) */ itv = get_interval(uptime0[!curr], uptime0[curr]); } if (DISPLAY_DISK(flags)) { struct io_stats *ioi, *ioj; shi = st_hdr_iodev; /* Display disk stats header */ write_disk_stat_header(&fctr); for (i = 0; i < iodev_nr; i++, shi++) { if (shi->used) { if (dlist_idx && !HAS_SYSFS(flags)) { /* * With /proc/diskstats, stats for every device * are read even if we have entered a list on devices * on the command line. Thus we need to check * if stats for current device are to be displayed. */ for (dev = 0; dev < dlist_idx; dev++) { st_dev_list_i = st_dev_list + dev; if (!strcmp(shi->name, st_dev_list_i->dev_name)) break; } if (dev == dlist_idx) /* Device not found in list: Don't display it */ continue; } ioi = st_iodev[curr] + i; ioj = st_iodev[!curr] + i; if (!DISPLAY_UNFILTERED(flags)) { if (!ioi->rd_ios && !ioi->wr_ios) continue; } if (DISPLAY_ZERO_OMIT(flags)) { if ((ioi->rd_ios == ioj->rd_ios) && (ioi->wr_ios == ioj->wr_ios)) /* No activity: Ignore it */ continue; } if (DISPLAY_GROUP_TOTAL_ONLY(flags)) { if (shi->status != DISK_GROUP) continue; } #ifdef DEBUG if (DISPLAY_DEBUG(flags)) { /* Debug output */ fprintf(stderr, "name=%s itv=%llu fctr=%d ioi{ rd_sectors=%lu " "wr_sectors=%lu rd_ios=%lu rd_merges=%lu rd_ticks=%u " "wr_ios=%lu wr_merges=%lu wr_ticks=%u ios_pgr=%u tot_ticks=%u " "rq_ticks=%u }\n", shi->name, itv, fctr, ioi->rd_sectors, ioi->wr_sectors, ioi->rd_ios, ioi->rd_merges, ioi->rd_ticks, ioi->wr_ios, ioi->wr_merges, ioi->wr_ticks, ioi->ios_pgr, ioi->tot_ticks, ioi->rq_ticks ); } #endif if (DISPLAY_EXTENDED(flags)) { write_ext_stat(curr, itv, fctr, shi, ioi, ioj); } else { write_basic_stat(curr, itv, fctr, shi, ioi, ioj); } } } printf("\n"); } }
/* *************************************************************************** * 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/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); }