ash: add INT_OFF/ON around allocations
[oweals/busybox.git] / procps / iostat.c
index 573419e1c1c3ccd0fc9202bb568b8c6ff47e70cf..c290c594b8ada48c8a1a5e7189da390fcb6c8001 100644 (file)
@@ -4,28 +4,27 @@
  *
  * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com>
  *
- * Licensed under GPLv2, see file License in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
  */
 
-//applet:IF_IOSTAT(APPLET(iostat, _BB_DIR_BIN, _BB_SUID_DROP))
-
-//kbuild:lib-$(CONFIG_IOSTAT) += iostat.o
-
 //config:config IOSTAT
 //config:      bool "iostat"
 //config:      default y
 //config:      help
 //config:        Report CPU and I/O statistics
 
+//applet:IF_IOSTAT(APPLET(iostat, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_IOSTAT) += iostat.o
+
 #include "libbb.h"
-#include <sys/utsname.h>       /* Need struct utsname */
+#include <sys/utsname.h>  /* struct utsname */
 
-#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
-//#define debug(fmt, ...) ((void)0)
+//#define debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__)
+#define debug(fmt, ...) ((void)0)
 
-#define MAX_DEVICE_NAME                12
-#define CURRENT                                0
-#define LAST                           1
+#define MAX_DEVICE_NAME 12
+#define MAX_DEVICE_NAME_STR "12"
 
 #if 1
 typedef unsigned long long cputime_t;
@@ -39,44 +38,65 @@ typedef long icputime_t;
 # define CPUTIME_MAX (~0UL)
 #endif
 
-struct stats_cpu {
-       cputime_t cpu_user;
-       cputime_t cpu_nice;
-       cputime_t cpu_system;
-       cputime_t cpu_idle;
-       cputime_t cpu_iowait;
-       cputime_t cpu_steal;
-       cputime_t cpu_irq;
-       cputime_t cpu_softirq;
-       cputime_t cpu_guest;
+enum {
+       STATS_CPU_USER,
+       STATS_CPU_NICE,
+       STATS_CPU_SYSTEM,
+       STATS_CPU_IDLE,
+       STATS_CPU_IOWAIT,
+       STATS_CPU_IRQ,
+       STATS_CPU_SOFTIRQ,
+       STATS_CPU_STEAL,
+       STATS_CPU_GUEST,
+
+       GLOBAL_UPTIME,
+       SMP_UPTIME,
+
+       N_STATS_CPU,
 };
 
-struct stats_dev {
-       char dname[MAX_DEVICE_NAME];
+typedef struct {
+       cputime_t vector[N_STATS_CPU];
+} stats_cpu_t;
+
+typedef struct {
+       stats_cpu_t *prev;
+       stats_cpu_t *curr;
+       cputime_t itv;
+} stats_cpu_pair_t;
+
+typedef struct {
        unsigned long long rd_sectors;
        unsigned long long wr_sectors;
        unsigned long rd_ops;
        unsigned long wr_ops;
-};
+} stats_dev_data_t;
 
-/* List of devices entered on the command line */
-struct device_list {
-       char dname[MAX_DEVICE_NAME];
-};
+typedef struct stats_dev {
+       struct stats_dev *next;
+       char dname[MAX_DEVICE_NAME + 1];
+       stats_dev_data_t prev_data;
+       stats_dev_data_t curr_data;
+} stats_dev_t;
 
 /* Globals. Sort by size and access frequency. */
 struct globals {
        smallint show_all;
-       unsigned devlist_i;             /* Index to the list of devices */
        unsigned total_cpus;            /* Number of CPUs */
        unsigned clk_tck;               /* Number of clock ticks per second */
-       struct device_list *dlist;
-       struct stats_dev *saved_stats_dev;
+       llist_t *dev_name_list;         /* List of devices entered on the command line */
+       stats_dev_t *stats_dev_list;
        struct tm tmtime;
+       struct {
+               const char *str;
+               unsigned div;
+       } unit;
 };
 #define G (*ptr_to_globals)
 #define INIT_G() do { \
        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+       G.unit.str = "Blk"; \
+       G.unit.div = 1; \
 } while (0)
 
 /* Must match option string! */
@@ -89,11 +109,6 @@ enum {
        OPT_m = 1 << 5,
 };
 
-static ALWAYS_INLINE unsigned get_user_hz(void)
-{
-       return sysconf(_SC_CLK_TCK);
-}
-
 static ALWAYS_INLINE int this_is_smp(void)
 {
        return (G.total_cpus > 1);
@@ -101,39 +116,19 @@ static ALWAYS_INLINE int this_is_smp(void)
 
 static void print_header(void)
 {
-       char buf[16];
+       char buf[32];
        struct utsname uts;
 
-       if (uname(&uts) < 0)
-               bb_perror_msg_and_die("uname");
+       uname(&uts); /* never fails */
 
+       /* Date representation for the current locale */
        strftime(buf, sizeof(buf), "%x", &G.tmtime);
 
-       printf("%s %s (%s) \t%s \t_%s_\t(%d CPU)\n\n",
+       printf("%s %s (%s) \t%s \t_%s_\t(%u CPU)\n\n",
                        uts.sysname, uts.release, uts.nodename,
                        buf, uts.machine, G.total_cpus);
 }
 
-static int get_number_of_cpus(void)
-{
-#ifdef _SC_NPROCESSORS_CONF
-       return sysconf(_SC_NPROCESSORS_CONF);
-#else
-       char buf[128];
-       int n = 0;
-       FILE *fp;
-
-       fp = xfopen_for_read("/proc/cpuinfo");
-
-       while (fgets(buf, sizeof(buf), fp))
-               if (strncmp(buf, "processor\t:", 11) == 0)
-                       n++;
-
-       fclose(fp);
-       return n;
-#endif
-}
-
 static void get_localtime(struct tm *ptm)
 {
        time_t timer;
@@ -143,19 +138,30 @@ static void get_localtime(struct tm *ptm)
 
 static void print_timestamp(void)
 {
-       char buf[20];
+       char buf[64];
+       /* %x: date representation for the current locale */
+       /* %X: time representation for the current locale */
        strftime(buf, sizeof(buf), "%x %X", &G.tmtime);
-       printf("%s\n", buf);
+       puts(buf);
 }
 
-/* Does str start with "cpu"? */
-static int starts_with_cpu(const char *str)
+static cputime_t get_smp_uptime(void)
 {
-       return ((str[0] - 'c') | (str[1] - 'p') | (str[2] - 'u')) == 0;
+       FILE *fp;
+       unsigned long sec, dec;
+
+       fp = xfopen_for_read("/proc/uptime");
+
+       if (fscanf(fp, "%lu.%lu", &sec, &dec) != 2)
+               bb_error_msg_and_die("can't read '%s'", "/proc/uptime");
+
+       fclose(fp);
+
+       return (cputime_t)sec * G.clk_tck + dec * G.clk_tck / 100;
 }
 
 /* Fetch CPU statistics from /proc/stat */
-static void get_cpu_statistics(struct stats_cpu *sc)
+static void get_cpu_statistics(stats_cpu_t *sc)
 {
        FILE *fp;
        char buf[1024];
@@ -165,46 +171,30 @@ static void get_cpu_statistics(struct stats_cpu *sc)
        memset(sc, 0, sizeof(*sc));
 
        while (fgets(buf, sizeof(buf), fp)) {
-               /* Does the line starts with "cpu "? */
-               if (starts_with_cpu(buf) && buf[3] == ' ') {
-                       sscanf(buf + 4 + 1,
-                               "%"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"
-                               FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u %"FMT_DATA"u",
-                               &sc->cpu_user, &sc->cpu_nice, &sc->cpu_system,
-                               &sc->cpu_idle, &sc->cpu_iowait, &sc->cpu_irq,
-                               &sc->cpu_softirq, &sc->cpu_steal, &sc->cpu_guest);
+               int i;
+               char *ibuf;
+
+               /* Does the line start with "cpu "? */
+               if (!starts_with_cpu(buf) || buf[3] != ' ') {
+                       continue;
+               }
+               ibuf = buf + 4;
+               for (i = STATS_CPU_USER; i <= STATS_CPU_GUEST; i++) {
+                       ibuf = skip_whitespace(ibuf);
+                       sscanf(ibuf, "%"FMT_DATA"u", &sc->vector[i]);
+                       if (i != STATS_CPU_GUEST) {
+                               sc->vector[GLOBAL_UPTIME] += sc->vector[i];
+                       }
+                       ibuf = skip_non_whitespace(ibuf);
                }
+               break;
        }
 
-       fclose(fp);
-}
-
-static cputime_t get_smp_uptime(void)
-{
-       FILE *fp;
-       char buf[sizeof(long)*3 * 2 + 4];
-       unsigned long sec, dec;
-
-       fp = xfopen_for_read("/proc/uptime");
-
-       if (fgets(buf, sizeof(buf), fp))
-               if (sscanf(buf, "%lu.%lu", &sec, &dec) != 2)
-                       bb_error_msg_and_die("can't read /proc/uptime");
+       if (this_is_smp()) {
+               sc->vector[SMP_UPTIME] = get_smp_uptime();
+       }
 
        fclose(fp);
-
-       return (cputime_t)sec * G.clk_tck + dec * G.clk_tck / 100;
-}
-
-/*
- * Obtain current uptime in jiffies.
- * Uptime is sum of individual CPUs' uptimes.
- */
-static cputime_t get_uptime(const struct stats_cpu *sc)
-{
-       /* NB: Don't include cpu_guest, it is already in cpu_user */
-       return sc->cpu_user + sc->cpu_nice + sc->cpu_system + sc->cpu_idle +
-               + sc->cpu_iowait + sc->cpu_irq + sc->cpu_steal + sc->cpu_softirq;
 }
 
 static ALWAYS_INLINE cputime_t get_interval(cputime_t old, cputime_t new)
@@ -245,65 +235,55 @@ static double percent_value(cputime_t prev, cputime_t curr, cputime_t itv)
        return ((double)overflow_safe_sub(prev, curr)) / itv * 100;
 }
 
-static void print_stats_cpu_struct(const struct stats_cpu *p,
-               const struct stats_cpu *c, cputime_t itv)
+static void print_stats_cpu_struct(stats_cpu_pair_t *stats)
 {
-       printf("         %6.2f  %6.2f  %6.2f  %6.2f  %6.2f  %6.2f\n",
-               percent_value(p->cpu_user   , c->cpu_user   , itv),
-               percent_value(p->cpu_nice   , c->cpu_nice   , itv),
-               percent_value(p->cpu_system + p->cpu_softirq + p->cpu_irq,
-                       c->cpu_system + c->cpu_softirq + c->cpu_irq, itv),
-               percent_value(p->cpu_iowait , c->cpu_iowait , itv),
-               percent_value(p->cpu_steal  , c->cpu_steal  , itv),
-               percent_value(p->cpu_idle   , c->cpu_idle   , itv)
+       cputime_t *p = stats->prev->vector;
+       cputime_t *c = stats->curr->vector;
+       printf("        %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n",
+               percent_value(p[STATS_CPU_USER]  , c[STATS_CPU_USER]  , stats->itv),
+               percent_value(p[STATS_CPU_NICE]  , c[STATS_CPU_NICE]  , stats->itv),
+               percent_value(p[STATS_CPU_SYSTEM] + p[STATS_CPU_SOFTIRQ] + p[STATS_CPU_IRQ],
+                       c[STATS_CPU_SYSTEM] + c[STATS_CPU_SOFTIRQ] + c[STATS_CPU_IRQ], stats->itv),
+               percent_value(p[STATS_CPU_IOWAIT], c[STATS_CPU_IOWAIT], stats->itv),
+               percent_value(p[STATS_CPU_STEAL] , c[STATS_CPU_STEAL] , stats->itv),
+               percent_value(p[STATS_CPU_IDLE]  , c[STATS_CPU_IDLE]  , stats->itv)
        );
 }
 
-static void print_stats_dev_struct(const struct stats_dev *p,
-               const struct stats_dev *c, cputime_t itv)
+static void cpu_report(stats_cpu_pair_t *stats)
 {
-       int unit = 1;
+       /* Always print a header */
+       puts("avg-cpu:  %user   %nice %system %iowait  %steal   %idle");
 
-       if (option_mask32 & OPT_k)
-               unit = 2;
-       else if (option_mask32 & OPT_m)
-               unit = 2048;
+       /* Print current statistics */
+       print_stats_cpu_struct(stats);
+}
 
+static void print_stats_dev_struct(stats_dev_t *stats_dev, cputime_t itv)
+{
+       stats_dev_data_t *p = &stats_dev->prev_data;
+       stats_dev_data_t *c = &stats_dev->curr_data;
        if (option_mask32 & OPT_z)
                if (p->rd_ops == c->rd_ops && p->wr_ops == c->wr_ops)
                        return;
 
-       printf("%-13s", c->dname);
-       printf(" %8.2f %12.2f %12.2f %10llu %10llu \n",
-               percent_value(p->rd_ops + p->wr_ops ,
-               /**/              c->rd_ops + c->wr_ops , itv),
-               percent_value(p->rd_sectors, c->rd_sectors, itv) / unit,
-               percent_value(p->wr_sectors, c->wr_sectors, itv) / unit,
-               (c->rd_sectors - p->rd_sectors) / unit,
-               (c->wr_sectors - p->wr_sectors) / unit);
-}
-
-static void cpu_report(const struct stats_cpu *last,
-               const struct stats_cpu *cur,
-               cputime_t itv)
-{
-       /* Always print a header */
-       puts("avg-cpu:  %user   %nice %system %iowait  %steal   %idle");
-
-       /* Print current statistics */
-       print_stats_cpu_struct(last, cur, itv);
+       printf("%-13s %8.2f %12.2f %12.2f %10llu %10llu\n",
+               stats_dev->dname,
+               percent_value(p->rd_ops + p->wr_ops, c->rd_ops + c->wr_ops, itv),
+               percent_value(p->rd_sectors, c->rd_sectors, itv) / G.unit.div,
+               percent_value(p->wr_sectors, c->wr_sectors, itv) / G.unit.div,
+               (c->rd_sectors - p->rd_sectors) / G.unit.div,
+               (c->wr_sectors - p->wr_sectors) / G.unit.div
+       );
 }
 
 static void print_devstat_header(void)
 {
-       printf("Device:            tps");
-
-       if (option_mask32 & OPT_m)
-               puts("    MB_read/s    MB_wrtn/s    MB_read    MB_wrtn");
-       else if (option_mask32 & OPT_k)
-               puts("    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn");
-       else
-               puts("   Blk_read/s   Blk_wrtn/s   Blk_read   Blk_wrtn");
+       printf("Device:%15s%6s%s/s%6s%s/s%6s%s%6s%s\n",
+               "tps",
+               G.unit.str, "_read", G.unit.str, "_wrtn",
+               G.unit.str, "_read", G.unit.str, "_wrtn"
+       );
 }
 
 /*
@@ -315,172 +295,99 @@ static int is_partition(const char *dev)
        return ((dev[0] - 's') | (dev[1] - 'd') | (dev[2] - 'a')) == 0 && isdigit(dev[3]);
 }
 
-/*
- * Return number of numbers on cmdline.
- * Reasonable values are only 0 (no interval/count specified),
- * 1 (interval specified) and 2 (both interval and count specified)
- */
-static int numbers_on_cmdline(int argc, char *argv[])
+static stats_dev_t *stats_dev_find_or_new(const char *dev_name)
 {
-       int sum = 0;
+       stats_dev_t **curr = &G.stats_dev_list;
 
-       if (isdigit(argv[argc-1][0]))
-               sum++;
-       if (argc > 2 && isdigit(argv[argc-2][0]))
-               sum++;
+       while (*curr != NULL) {
+               if (strcmp((*curr)->dname, dev_name) == 0)
+                       return *curr;
+               curr = &(*curr)->next;
+       }
 
-       return sum;
+       *curr = xzalloc(sizeof(stats_dev_t));
+       strncpy((*curr)->dname, dev_name, MAX_DEVICE_NAME);
+       return *curr;
 }
 
-static int is_dev_in_dlist(const char *dev)
+static void stats_dev_free(stats_dev_t *stats_dev)
 {
-       int i;
-
-       /* Go through the device list */
-       for (i = 0; i < G.devlist_i; i++)
-               if (strcmp(G.dlist[i].dname, dev) == 0)
-                       /* Found a match */
-                       return 1;
-
-       /* No match found */
-       return 0;
+       if (stats_dev) {
+               stats_dev_free(stats_dev->next);
+               free(stats_dev);
+       }
 }
 
 static void do_disk_statistics(cputime_t itv)
 {
+       char buf[128];
+       char dev_name[MAX_DEVICE_NAME + 1];
+       unsigned long long rd_sec_or_dummy;
+       unsigned long long wr_sec_or_dummy;
+       stats_dev_data_t *curr_data;
+       stats_dev_t *stats_dev;
        FILE *fp;
        int rc;
-       int i = 0;
-       char buf[128];
-       unsigned major, minor;
-       unsigned long wr_ops, dummy;    /* %*lu for suppres the conversion wouldn't work */
-       unsigned long long rd_sec_or_wr_ops;
-       unsigned long long rd_sec_or_dummy, wr_sec_or_dummy, wr_sec;
-       struct stats_dev sd;
 
        fp = xfopen_for_read("/proc/diskstats");
-
        /* Read and possibly print stats from /proc/diskstats */
        while (fgets(buf, sizeof(buf), fp)) {
-               rc = sscanf(buf, "%u %u %s %lu %llu %llu %llu %lu %lu %llu %lu %lu %lu %lu",
-                       &major, &minor, sd.dname, &sd.rd_ops,
-                       &rd_sec_or_dummy, &rd_sec_or_wr_ops, &wr_sec_or_dummy,
-                       &wr_ops, &dummy, &wr_sec, &dummy, &dummy, &dummy, &dummy);
-
-               switch (rc) {
-               case 14:
-                       sd.wr_ops = wr_ops;
-                       sd.rd_sectors = rd_sec_or_wr_ops;
-                       sd.wr_sectors = wr_sec;
-                       break;
-               case 7:
-                       sd.rd_sectors = rd_sec_or_dummy;
-                       sd.wr_ops = (unsigned long)rd_sec_or_wr_ops;
-                       sd.wr_sectors = wr_sec_or_dummy;
-                       break;
-               default:
-                       break;
-               }
-
-               if (!G.devlist_i && !is_partition(sd.dname)) {
-                       /* User didn't specify device */
-                       if (!G.show_all && !sd.rd_ops && !sd.wr_ops) {
-                               /* Don't print unused device */
-                               continue;
-                       }
-                       print_stats_dev_struct(&G.saved_stats_dev[i], &sd, itv);
-                       G.saved_stats_dev[i] = sd;
-                       i++;
-               } else {
-                       /* Is device in device list? */
-                       if (is_dev_in_dlist(sd.dname)) {
-                               /* Print current statistics */
-                               print_stats_dev_struct(&G.saved_stats_dev[i], &sd, itv);
-                               G.saved_stats_dev[i] = sd;
-                               i++;
-                       } else
+               sscanf(buf, "%*s %*s %"MAX_DEVICE_NAME_STR"s", dev_name);
+               if (G.dev_name_list) {
+                       /* Is device name in list? */
+                       if (!llist_find_str(G.dev_name_list, dev_name))
                                continue;
+               } else if (is_partition(dev_name)) {
+                       continue;
                }
-       }
-}
-
-static void dev_report(cputime_t itv)
-{
-       /* Always print a header */
-       print_devstat_header();
-
-       /* Fetch current disk statistics */
-       do_disk_statistics(itv);
-}
-
-static void save_to_devlist(const char *dname)
-{
-       int i;
-       struct device_list *tmp = G.dlist;
-
-       if (strncmp(dname, "/dev/", 5) == 0)
-               /* We'll ignore prefix '/dev/' */
-               dname += 5;
-
-       /* Go through the list */
-       for (i = 0; i < G.devlist_i; i++, tmp++)
-               if (strcmp(tmp->dname, dname) == 0)
-                       /* Already in the list */
-                       return;
-
-       /* Add device name to the list */
-       strncpy(tmp->dname, dname, MAX_DEVICE_NAME - 1);
 
-       /* Update device list index */
-       G.devlist_i++;
-}
-
-static unsigned get_number_of_devices(void)
-{
-       FILE *fp;
-       char buf[128];
-       int rv;
-       unsigned n = 0;
-       unsigned long rd_ops, wr_ops;
-       char dname[MAX_DEVICE_NAME];
-
-       fp = xfopen_for_read("/proc/diskstats");
+               stats_dev = stats_dev_find_or_new(dev_name);
+               curr_data = &stats_dev->curr_data;
+
+               rc = sscanf(buf, "%*s %*s %*s %lu %llu %llu %llu %lu %*s %llu",
+                       &curr_data->rd_ops,
+                       &rd_sec_or_dummy,
+                       &curr_data->rd_sectors,
+                       &wr_sec_or_dummy,
+                       &curr_data->wr_ops,
+                       &curr_data->wr_sectors);
+               if (rc != 6) {
+                       curr_data->rd_sectors = rd_sec_or_dummy;
+                       curr_data->wr_sectors = wr_sec_or_dummy;
+                       //curr_data->rd_ops = ;
+                       curr_data->wr_ops = (unsigned long)curr_data->rd_sectors;
+               }
 
-       while (fgets(buf, sizeof(buf), fp)) {
-               rv = sscanf(buf, "%*d %*d %s %lu %*u %*u %*u %lu",
-                               dname, &rd_ops, &wr_ops);
-               if (rv == 2 || is_partition(dname))
-                       /* A partition */
+               if (!G.dev_name_list /* User didn't specify device */
+                && !G.show_all
+                && curr_data->rd_ops == 0
+                && curr_data->wr_ops == 0
+               ) {
+                       /* Don't print unused device */
                        continue;
-               if (!rd_ops && !wr_ops) {
-                       /* Unused device */
-                       if (!G.show_all)
-                               continue;
                }
-               n++;
+
+               /* Print current statistics */
+               print_stats_dev_struct(stats_dev, itv);
+               stats_dev->prev_data = *curr_data;
        }
 
        fclose(fp);
-       return n;
 }
 
-static int number_of_ALL_on_cmdline(char **argv)
+static void dev_report(cputime_t itv)
 {
-       int alls = 0;
-
-       /* Iterate over cmd line arguments, count "ALL" */
-       while (*argv)
-               if (strcmp(*argv++, "ALL") == 0)
-                       alls++;
+       /* Always print a header */
+       print_devstat_header();
 
-       return alls;
+       /* Fetch current disk statistics */
+       do_disk_statistics(itv);
 }
 
 //usage:#define iostat_trivial_usage
 //usage:       "[-c] [-d] [-t] [-z] [-k|-m] [ALL|BLOCKDEV...] [INTERVAL [COUNT]]"
 //usage:#define iostat_full_usage "\n\n"
 //usage:       "Report CPU and I/O statistics\n"
-//usage:     "\nOptions:"
 //usage:     "\n       -c      Show CPU utilization"
 //usage:     "\n       -d      Show device utilization"
 //usage:     "\n       -t      Print current time"
@@ -489,25 +396,25 @@ static int number_of_ALL_on_cmdline(char **argv)
 //usage:     "\n       -m      Use Mb/s"
 
 int iostat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int iostat_main(int argc, char **argv)
+int iostat_main(int argc UNUSED_PARAM, char **argv)
 {
-       int opt, dev_num;
-       unsigned interval = 0;
-       int count = 0;
-       cputime_t global_uptime[2] = { 0 };
-       cputime_t smp_uptime[2] = { 0 };
-       cputime_t itv;
-       struct stats_cpu stats_cur, stats_last;
+       int opt;
+       unsigned interval;
+       int count;
+       stats_cpu_t stats_data[2];
+       smallint current_stats;
 
        INIT_G();
 
-       memset(&stats_last, 0, sizeof(stats_last));
+       memset(&stats_data, 0, sizeof(stats_data));
 
        /* Get number of clock ticks per sec */
-       G.clk_tck = get_user_hz();
+       G.clk_tck = bb_clk_tck();
 
        /* Determine number of CPUs */
-       G.total_cpus = get_number_of_cpus();
+       G.total_cpus = get_cpu_count();
+       if (G.total_cpus == 0)
+               G.total_cpus = 1;
 
        /* Parse and process arguments */
        /* -k and -m are mutually exclusive */
@@ -518,64 +425,73 @@ int iostat_main(int argc, char **argv)
                opt |= OPT_c + OPT_d;
 
        argv += optind;
-       argc -= optind;
-
-       dev_num = argc - numbers_on_cmdline(argc, argv);
-       /* We don't want to allocate space for 'ALL' */
-       dev_num -= number_of_ALL_on_cmdline(argv);
-       if (dev_num > 0)
-               /* Make space for device list */
-               G.dlist = xzalloc(sizeof(G.dlist[0]) * dev_num);
 
        /* Store device names into device list */
        while (*argv && !isdigit(*argv[0])) {
-               if (strcmp(*argv, "ALL") != 0)
+               if (strcmp(*argv, "ALL") != 0) {
                        /* If not ALL, save device name */
-                       save_to_devlist(*argv);
-               else
+                       char *dev_name = skip_dev_pfx(*argv);
+                       if (!llist_find_str(G.dev_name_list, dev_name)) {
+                               llist_add_to(&G.dev_name_list, dev_name);
+                       }
+               } else {
                        G.show_all = 1;
+               }
                argv++;
        }
 
+       interval = 0;
+       count = 1;
        if (*argv) {
                /* Get interval */
-               interval = xatoi_u(*argv);
-               count = interval ? -1 : 1;
+               interval = xatoi_positive(*argv);
+               count = (interval != 0 ? -1 : 1);
                argv++;
                if (*argv)
                        /* Get count value */
-                       count = xatoi_u(*argv);
+                       count = xatoi_positive(*argv);
        }
 
-       /* Allocate space for device stats */
-       if (opt & OPT_d) {
-               G.saved_stats_dev = xzalloc(sizeof(G.saved_stats_dev[0]) *
-                               (dev_num ? dev_num : get_number_of_devices())
-               );
+       if (opt & OPT_m) {
+               G.unit.str = " MB";
+               G.unit.div = 2048;
+       }
+
+       if (opt & OPT_k) {
+               G.unit.str = " kB";
+               G.unit.div = 2;
        }
 
+       get_localtime(&G.tmtime);
+
        /* Display header */
        print_header();
 
+       current_stats = 0;
        /* Main loop */
        for (;;) {
+               stats_cpu_pair_t stats;
+
+               stats.prev = &stats_data[current_stats ^ 1];
+               stats.curr = &stats_data[current_stats];
+
                /* Fill the time structure */
                get_localtime(&G.tmtime);
 
                /* Fetch current CPU statistics */
-               get_cpu_statistics(&stats_cur);
-
-               /* Fetch current uptime */
-               global_uptime[CURRENT] = get_uptime(&stats_cur);
+               get_cpu_statistics(stats.curr);
 
                /* Get interval */
-               itv = get_interval(global_uptime[LAST], global_uptime[CURRENT]);
+               stats.itv = get_interval(
+                       stats.prev->vector[GLOBAL_UPTIME],
+                       stats.curr->vector[GLOBAL_UPTIME]
+               );
 
                if (opt & OPT_t)
                        print_timestamp();
 
                if (opt & OPT_c) {
-                       cpu_report(&stats_last, &stats_cur, itv);
+                       cpu_report(&stats);
                        if (opt & OPT_d)
                                /* Separate outputs by a newline */
                                bb_putchar('\n');
@@ -583,32 +499,31 @@ int iostat_main(int argc, char **argv)
 
                if (opt & OPT_d) {
                        if (this_is_smp()) {
-                               smp_uptime[CURRENT] = get_smp_uptime();
-                               itv = get_interval(smp_uptime[LAST], smp_uptime[CURRENT]);
-                               smp_uptime[LAST] = smp_uptime[CURRENT];
+                               stats.itv = get_interval(
+                                       stats.prev->vector[SMP_UPTIME],
+                                       stats.curr->vector[SMP_UPTIME]
+                               );
                        }
-                       dev_report(itv);
+                       dev_report(stats.itv);
                }
 
+               bb_putchar('\n');
+
                if (count > 0) {
                        if (--count == 0)
                                break;
                }
 
-               /* Backup current stats */
-               global_uptime[LAST] = global_uptime[CURRENT];
-               stats_last = stats_cur;
+               /* Swap stats */
+               current_stats ^= 1;
 
-               bb_putchar('\n');
                sleep(interval);
        }
 
-       bb_putchar('\n');
-
        if (ENABLE_FEATURE_CLEAN_UP) {
+               llist_free(G.dev_name_list, NULL);
+               stats_dev_free(G.stats_dev_list);
                free(&G);
-               free(G.dlist);
-               free(G.saved_stats_dev);
        }
 
        return EXIT_SUCCESS;