]> granicus.if.org Git - sysstat/blobdiff - sar.c
sar/sadf: Add extra flexibility in case of a change of file format
[sysstat] / sar.c
diff --git a/sar.c b/sar.c
index cde7e77972b07bffedde215a0bc0dfd612b5d919..70ce79c1b0a94beb4a59febf5937d5575994552e 100644 (file)
--- a/sar.c
+++ b/sar.c
@@ -1,6 +1,6 @@
 /*
  * sar: report system activity
- * (C) 1999-2017 by Sebastien GODARD (sysstat <at> orange.fr)
+ * (C) 1999-2019 by Sebastien GODARD (sysstat <at> orange.fr)
  *
  ***************************************************************************
  * This program is free software; you can redistribute it and/or modify it *
@@ -30,9 +30,6 @@
 
 #include "version.h"
 #include "sa.h"
-#include "common.h"
-#include "ioconf.h"
-#include "pr_stats.h"
 
 #ifdef USE_NLS
 #include <locale.h>
 char *sccsid(void) { return (SCCSID); }
 #endif
 
+#ifdef TEST
+extern time_t __unix_time;
+#endif
+
 /* Interval and count parameters */
 long interval = -1, count = 0;
 
 /* TRUE if a header line must be printed */
-int dis = TRUE;
+int dish = TRUE;
 /* TRUE if data read from file don't match current machine's endianness */
 int endian_mismatch = FALSE;
 /* TRUE if file's data come from a 64 bit machine */
 int arch_64 = FALSE;
+/* Number of decimal places */
+int dplaces_nr = -1;
 
 unsigned int flags = 0;
 unsigned int dm_major; /* Device-mapper major number */
@@ -118,10 +121,12 @@ void usage(char *progname)
        fprintf(stderr, _("Options are:\n"
                          "[ -A ] [ -B ] [ -b ] [ -C ] [ -D ] [ -d ] [ -F [ MOUNT ] ] [ -H ] [ -h ]\n"
                          "[ -p ] [ -q ] [ -r [ ALL ] ] [ -S ] [ -t ] [ -u [ ALL ] ] [ -V ]\n"
-                         "[ -v ] [ -W ] [ -w ] [ -y ] [ --human ] [ --sadc ]\n"
+                         "[ -v ] [ -W ] [ -w ] [ -y ] [ -z ]\n"
                          "[ -I { <int_list> | SUM | ALL } ] [ -P { <cpu_list> | ALL } ]\n"
                          "[ -m { <keyword> [,...] | ALL } ] [ -n { <keyword> [,...] | ALL } ]\n"
-                         "[ -j { ID | LABEL | PATH | UUID | ... } ]\n"
+                         "[ --dev=<dev_list> ] [ --fs=<fs_list> ] [ --iface=<iface_list> ]\n"
+                         "[ --dec={ 0 | 1 | 2 } ] [ --help ] [ --human ] [ --sadc ]\n"
+                         "[ -j { SID | ID | LABEL | PATH | UUID | ... } ]\n"
                          "[ -f [ <filename> ] | -o [ <filename> ] | -[0-9]+ ]\n"
                          "[ -i <interval> ] [ -s [ <hh:mm[:ss]> ] ] [ -e [ <hh:mm[:ss]> ] ]\n"));
        exit(1);
@@ -138,17 +143,17 @@ void usage(char *progname)
 void display_help(char *progname)
 {
        print_usage_title(stdout, progname);
-       printf(_("Main options and reports:\n"));
-       printf(_("\t-B\tPaging statistics\n"));
-       printf(_("\t-b\tI/O and transfer rate statistics\n"));
-       printf(_("\t-d\tBlock devices statistics\n"));
+       printf(_("Main options and reports (report name between square brackets):\n"));
+       printf(_("\t-B\tPaging statistics [A_PAGE]\n"));
+       printf(_("\t-b\tI/O and transfer rate statistics [A_IO]\n"));
+       printf(_("\t-d\tBlock devices statistics [A_DISK]\n"));
        printf(_("\t-F [ MOUNT ]\n"));
-       printf(_("\t\tFilesystems statistics\n"));
-       printf(_("\t-H\tHugepages utilization statistics\n"));
+       printf(_("\t\tFilesystems statistics [A_FS]\n"));
+       printf(_("\t-H\tHugepages utilization statistics [A_HUGE]\n"));
        printf(_("\t-I { <int_list> | SUM | ALL }\n"
-                "\t\tInterrupts statistics\n"));
+                "\t\tInterrupts statistics [A_IRQ]\n"));
        printf(_("\t-m { <keyword> [,...] | ALL }\n"
-                "\t\tPower management statistics\n"
+                "\t\tPower management statistics [A_PWR_...]\n"
                 "\t\tKeywords are:\n"
                 "\t\tCPU\tCPU instantaneous clock frequency\n"
                 "\t\tFAN\tFans speed\n"
@@ -157,7 +162,7 @@ void display_help(char *progname)
                 "\t\tTEMP\tDevices temperature\n"
                 "\t\tUSB\tUSB devices plugged into the system\n"));
        printf(_("\t-n { <keyword> [,...] | ALL }\n"
-                "\t\tNetwork statistics\n"
+                "\t\tNetwork statistics [A_NET_...]\n"
                 "\t\tKeywords are:\n"
                 "\t\tDEV\tNetwork interfaces\n"
                 "\t\tEDEV\tNetwork interfaces (errors)\n"
@@ -179,16 +184,16 @@ void display_help(char *progname)
                 "\t\tUDP6\tUDP traffic\t(v6)\n"
                 "\t\tFC\tFibre channel HBAs\n"
                 "\t\tSOFT\tSoftware-based network processing\n"));
-       printf(_("\t-q\tQueue length and load average statistics\n"));
+       printf(_("\t-q\tQueue length and load average statistics [A_QUEUE]\n"));
        printf(_("\t-r [ ALL ]\n"
-                "\t\tMemory utilization statistics\n"));
-       printf(_("\t-S\tSwap space utilization statistics\n"));
+                "\t\tMemory utilization statistics [A_MEMORY]\n"));
+       printf(_("\t-S\tSwap space utilization statistics [A_MEMORY]\n"));
        printf(_("\t-u [ ALL ]\n"
-                "\t\tCPU utilization statistics\n"));
-       printf(_("\t-v\tKernel tables statistics\n"));
-       printf(_("\t-W\tSwapping statistics\n"));
-       printf(_("\t-w\tTask creation and system switching statistics\n"));
-       printf(_("\t-y\tTTY devices statistics\n"));
+                "\t\tCPU utilization statistics [A_CPU]\n"));
+       printf(_("\t-v\tKernel tables statistics [A_KTABLES]\n"));
+       printf(_("\t-W\tSwapping statistics [A_SWAP]\n"));
+       printf(_("\t-w\tTask creation and system switching statistics [A_PCSW]\n"));
+       printf(_("\t-y\tTTY devices statistics [A_SERIAL]\n"));
        exit(0);
 }
 
@@ -334,7 +339,7 @@ int check_line_hdr(void)
                                        rc = TRUE;
                                }
                        }
-                       else if (act[i]->nr > 1) {
+                       else if (act[i]->nr_ini > 1) {
                                rc = TRUE;
                        }
                        /* Stop now since we have only one selected activity */
@@ -368,7 +373,7 @@ void write_stats_avg(int curr, int read_from_file, unsigned int act_id)
 
        strncpy(timestamp[curr], _("Average:"), TIMESTAMP_LEN);
        timestamp[curr][TIMESTAMP_LEN - 1] = '\0';
-       strcpy(timestamp[!curr], timestamp[curr]);
+       memcpy(timestamp[!curr], timestamp[curr], TIMESTAMP_LEN);
 
        /* Test stdout */
        TEST_STDOUT(STDOUT_FILENO);
@@ -378,7 +383,7 @@ void write_stats_avg(int curr, int read_from_file, unsigned int act_id)
                if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
                        continue;
 
-               if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
+               if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
                        /* Display current average activity statistics */
                        (*act[i]->f_print_avg)(act[i], 2, curr, itv);
                }
@@ -396,6 +401,7 @@ void write_stats_avg(int curr, int read_from_file, unsigned int act_id)
 /*
  ***************************************************************************
  * Print system statistics.
+ * This is called when we read stats either from a file or from sadc.
  *
  * IN:
  * @curr               Index in array for current sample statistics.
@@ -421,7 +427,7 @@ void write_stats_avg(int curr, int read_from_file, unsigned int act_id)
 int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start,
                int use_tm_end, int reset, unsigned int act_id, int reset_cd)
 {
-       int i;
+       int i, prev_hour;
        unsigned long long itv;
        static int cross_day = 0;
 
@@ -447,41 +453,35 @@ int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start,
                        return 0;
        }
 
-       if (!is_iso_time_fmt())
-               flags |= S_F_PREFD_TIME_OUTPUT;
-
        /* Get then set previous timestamp */
        if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[!curr],
-                                          &rectime, NULL))
+                                          &rectime))
                return 0;
+       prev_hour = rectime.tm_hour;
        set_record_timestamp_string(flags, &record_hdr[!curr],
                                    NULL, timestamp[!curr], TIMESTAMP_LEN, &rectime);
 
        /* Get then set current timestamp */
        if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[curr],
-                                          &rectime, NULL))
+                                          &rectime))
                return 0;
        set_record_timestamp_string(flags, &record_hdr[curr],
                                    NULL, timestamp[curr], TIMESTAMP_LEN, &rectime);
 
-       /* Check if we are beginning a new day */
+       /*
+        * Check if we are beginning a new day.
+        * Use rectime.tm_hour and prev_hour instead of record_hdr[].hour for comparison
+        * to take into account the current timezone (hours displayed will depend on the
+        * TZ variable value).
+        */
        if (use_tm_start && record_hdr[!curr].ust_time &&
            (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) &&
-           (record_hdr[curr].hour < record_hdr[!curr].hour)) {
+           (rectime.tm_hour < prev_hour)) {
                cross_day = 1;
        }
 
-       if (cross_day) {
-               /*
-                * This is necessary if we want to properly handle something like:
-                * sar -s time_start -e time_end with
-                * time_start(day D) > time_end(day D+1)
-                */
-               rectime.tm_hour +=24;
-       }
-
        /* Check time (2) */
-       if (use_tm_start && (datecmp(&rectime, &tm_start) < 0))
+       if (use_tm_start && (datecmp(&rectime, &tm_start, cross_day) < 0))
                /* it's too soon... */
                return 0;
 
@@ -489,7 +489,7 @@ int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start,
        get_itv_value(&record_hdr[curr], &record_hdr[!curr], &itv);
 
        /* Check time (3) */
-       if (use_tm_end && (datecmp(&rectime, &tm_end) > 0)) {
+       if (use_tm_end && (datecmp(&rectime, &tm_end, cross_day) > 0)) {
                /* It's too late... */
                *cnt = 0;
                return 0;
@@ -505,7 +505,7 @@ int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start,
                if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
                        continue;
 
-               if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
+               if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
                        /* Display current activity statistics */
                        (*act[i]->f_print)(act[i], !curr, curr, itv);
                }
@@ -535,14 +535,21 @@ void write_stats_startup(int curr)
        record_hdr[!curr].ust_time    = record_hdr[curr].ust_time;
 
        for (i = 0; i < NR_ACT; i++) {
-               if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
+               if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
+                       /*
+                        * Using nr[curr] and not nr[!curr] below because we initialize
+                        * reference structures for each structure that has been
+                        * currently read in memory.
+                        * No problem with buffers allocation since they all have the
+                        * same size.
+                        */
                        memset(act[i]->buf[!curr], 0,
-                              (size_t) act[i]->msize * (size_t) act[i]->nr * (size_t) act[i]->nr2);
+                              (size_t) act[i]->msize * (size_t) act[i]->nr[curr] * (size_t) act[i]->nr2);
                }
        }
 
        flags |= S_F_SINCE_BOOT;
-       dis = TRUE;
+       dish = TRUE;
 
        write_stats(curr, USE_SADC, &count, NO_TM_START, NO_TM_END, NO_RESET,
                    ALL_ACTIVITIES, TRUE);
@@ -564,9 +571,9 @@ void write_stats_startup(int curr)
  * 1 if end of file has been reached, 0 otherwise.
  ***************************************************************************
  */
-int sa_read(void *buffer, int size)
+int sa_read(void *buffer, size_t size)
 {
-       int n;
+       ssize_t n;
 
        while (size) {
 
@@ -595,11 +602,13 @@ int sa_read(void *buffer, int size)
  * @cur_date   Date string of current restart message (unused here).
  * @cur_time   Time string of current restart message.
  * @utc                True if @cur_time is expressed in UTC (unused here).
- * @file_hdr   System activity file standard header (unused here).
+ * @file_hdr   System activity file standard header.
+ * @record_hdr Current record header (unused here).
  ***************************************************************************
  */
 __printf_funct_t print_sar_restart(int *tab, int action, char *cur_date, char *cur_time,
-                                 int utc, struct file_header *file_hdr)
+                                 int utc, struct file_header *file_hdr,
+                                 struct record_header *record_hdr)
 {
        char restart[64];
 
@@ -622,10 +631,12 @@ __printf_funct_t print_sar_restart(int *tab, int action, char *cur_date, char *c
  * @utc                True if @cur_time is expressed in UTC (unused here).
  * @comment    Comment to display.
  * @file_hdr   System activity file standard header (unused here).
+ * @record_hdr Current record header (unused here).
  ***************************************************************************
  */
 __print_funct_t print_sar_comment(int *tab, int action, char *cur_date, char *cur_time, int utc,
-                                 char *comment, struct file_header *file_hdr)
+                                 char *comment, struct file_header *file_hdr,
+                                 struct record_header *record_hdr)
 {
        printf("%-11s", cur_time);
        cprintf_s(IS_COMMENT, "  COM %s\n", comment);
@@ -653,6 +664,9 @@ void read_sadc_stat_bunch(int curr)
                if (sigint_caught)
                        return;
 
+#ifdef DEBUG
+               fprintf(stderr, "%s: Record header\n", __FUNCTION__);
+#endif
                print_read_error(END_OF_DATA_UNEXPECTED);
        }
 
@@ -662,7 +676,40 @@ void read_sadc_stat_bunch(int curr)
                        continue;
                p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
 
-               if (sa_read(act[p]->buf[curr], act[p]->fsize * act[p]->nr * act[p]->nr2)) {
+               if (HAS_COUNT_FUNCTION(act[p]->options)) {
+                       if (sa_read(&(act[p]->nr[curr]), sizeof(__nr_t))) {
+#ifdef DEBUG
+                               fprintf(stderr, "%s: Nb of items\n", __FUNCTION__);
+#endif
+                               print_read_error(END_OF_DATA_UNEXPECTED);
+                       }
+                       if ((act[p]->nr[curr] > act[p]->nr_max) || (act[p]->nr[curr] < 0)) {
+#ifdef DEBUG
+                               fprintf(stderr, "%s: %s: nr=%d nr_max=%d\n",
+                                       __FUNCTION__, act[p]->name, act[p]->nr[curr], act[p]->nr_max);
+#endif
+                               print_read_error(INCONSISTENT_INPUT_DATA);
+                       }
+                       if (act[p]->nr[curr] > act[p]->nr_allocated) {
+                               reallocate_all_buffers(act[p], act[p]->nr[curr]);
+                       }
+
+
+                       /*
+                        * For persistent activities, we must make sure that no statistics
+                         * from a previous iteration remain, especially if the number
+                         * of structures read is smaller than @nr_ini.
+                         */
+                        if (HAS_PERSISTENT_VALUES(act[p]->options)) {
+                            memset(act[p]->buf[curr], 0,
+                                   (size_t) act[p]->fsize * (size_t) act[p]->nr_ini * (size_t) act[p]->nr2);
+                        }
+                }
+               if (sa_read(act[p]->buf[curr],
+                           (size_t) act[p]->fsize * (size_t) act[p]->nr[curr] * (size_t) act[p]->nr2)) {
+#ifdef DEBUG
+                       fprintf(stderr, "%s: Statistics\n", __FUNCTION__);
+#endif
                        print_read_error(END_OF_DATA_UNEXPECTED);
                }
        }
@@ -685,6 +732,7 @@ void read_sadc_stat_bunch(int curr)
  * @endian_mismatch
  *             TRUE if file's data don't match current machine's endianness.
  * @arch_64    TRUE if file's data come from a 64 bit machine.
+ * @b_size     Size of @rec_hdr_tmp buffer.
  *
  * OUT:
  * @curr       Index in array for next sample statistics.
@@ -698,12 +746,12 @@ void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf
                           int rows, unsigned int act_id, int *reset,
                           struct file_activity *file_actlst, char *file,
                           struct file_magic *file_magic, void *rec_hdr_tmp,
-                          int endian_mismatch, int arch_64)
+                          int endian_mismatch, int arch_64, size_t b_size)
 {
        int p, reset_cd;
        unsigned long lines = 0;
        unsigned char rtype;
-       int davg = 0, next, inc;
+       int davg = 0, next, inc = 0;
 
        if (lseek(ifd, fpos, SEEK_SET) < fpos) {
                perror("lseek");
@@ -718,16 +766,12 @@ void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf
 
        *cnt  = count;
 
-       /* Assess number of lines printed */
+       /* Assess number of lines printed when a bitmap is used */
        p = get_activity_position(act, act_id, EXIT_IF_NOT_FOUND);
        if (act[p]->bitmap) {
                inc = count_bits(act[p]->bitmap->b_array,
                                 BITMAP_SIZE(act[p]->bitmap->b_size));
        }
-       else {
-               inc = act[p]->nr;
-       }
-
        reset_cd = 1;
 
        do {
@@ -736,21 +780,21 @@ void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf
                 * Start with reading current sample's record header.
                 */
                *eosaf = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[*curr],
-                                        &file_hdr, arch_64, endian_mismatch);
+                                        &file_hdr, arch_64, endian_mismatch, UEOF_STOP, b_size);
                rtype = record_hdr[*curr].record_type;
 
                if (!*eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
                        /* Read the extra fields since it's not a special record */
                        read_file_stat_bunch(act, *curr, ifd, file_hdr.sa_act_nr, file_actlst,
-                                            endian_mismatch, arch_64);
+                                            endian_mismatch, arch_64, file, file_magic, UEOF_STOP);
                }
 
                if ((lines >= rows) || !lines) {
                        lines = 0;
-                       dis = 1;
+                       dish = 1;
                }
                else
-                       dis = 0;
+                       dish = 0;
 
                if (!*eosaf && (rtype != R_RESTART)) {
 
@@ -758,11 +802,16 @@ void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf
                                /* Display comment */
                                next = print_special_record(&record_hdr[*curr], flags + S_F_LOCAL_TIME,
                                                            &tm_start, &tm_end, R_COMMENT, ifd,
-                                                           &rectime, NULL, file, 0,
+                                                           &rectime, file, 0,
                                                            file_magic, &file_hdr, act, &sar_fmt,
                                                            endian_mismatch, arch_64);
-                               if (next) {
-                                       /* A line of comment was actually displayed */
+                               if (next && lines) {
+                                       /*
+                                        * A line of comment was actually displayed: Count it in the
+                                        * total number of displayed lines.
+                                        * If no lines of stats had been previously displayed, ignore it
+                                        * to make sure the header line will be displayed.
+                                        */
                                        lines++;
                                }
                                continue;
@@ -775,16 +824,29 @@ void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf
                        if (next && (*cnt > 0)) {
                                (*cnt)--;
                        }
+
                        if (next) {
                                davg++;
-                               *curr ^=1;
-                               lines += inc;
+                               *curr ^= 1;
+
+                               if (inc) {
+                                       lines += inc;
+                               }
+                               else {
+                                       lines += act[p]->nr[*curr];
+                               }
                        }
                        *reset = FALSE;
                }
        }
        while (*cnt && !*eosaf && (rtype != R_RESTART));
 
+       /*
+        * At this moment, if we had a R_RESTART record, we still haven't read
+        * the number of CPU following it (nor the possible extra structures).
+        * But in this case, we always have @cnt != 0.
+        */
+
        if (davg) {
                write_stats_avg(!*curr, USE_SA_FILE, act_id);
        }
@@ -826,6 +888,10 @@ void read_header_data(void)
                                _("Using a wrong data collector from a different sysstat version\n"));
                }
 
+#ifdef DEBUG
+               fprintf(stderr, "%s: sysstat_magic=%x format_magic=%x version=%s\n",
+                       __FUNCTION__, file_magic.sysstat_magic, file_magic.format_magic, version);
+#endif
                print_read_error(INCONSISTENT_INPUT_DATA);
        }
 
@@ -836,16 +902,26 @@ void read_header_data(void)
         * but also VERSION above) and thus the size of file_header is FILE_HEADER_SIZE.
         */
        if (sa_read(&file_hdr, FILE_HEADER_SIZE)) {
+#ifdef DEBUG
+               fprintf(stderr, "%s: File header\n", __FUNCTION__);
+#endif
                print_read_error(END_OF_DATA_UNEXPECTED);
        }
 
        /* All activities are not necessarily selected, but NR_ACT is a max */
        if (file_hdr.sa_act_nr > NR_ACT) {
+#ifdef DEBUG
+               fprintf(stderr, "%s: sa_act_nr=%d\n", __FUNCTION__, file_hdr.sa_act_nr);
+#endif
                print_read_error(INCONSISTENT_INPUT_DATA);
        }
 
        if ((file_hdr.act_size != FILE_ACTIVITY_SIZE) ||
            (file_hdr.rec_size != RECORD_HEADER_SIZE)) {
+#ifdef DEBUG
+               fprintf(stderr, "%s: act_size=%u/%lu rec_size=%u/%lu\n", __FUNCTION__,
+                       file_hdr.act_size, FILE_ACTIVITY_SIZE, file_hdr.rec_size, RECORD_HEADER_SIZE);
+#endif
                print_read_error(INCONSISTENT_INPUT_DATA);
        }
 
@@ -853,6 +929,9 @@ void read_header_data(void)
        for (i = 0; i < file_hdr.sa_act_nr; i++) {
 
                if (sa_read(&file_act, FILE_ACTIVITY_SIZE)) {
+#ifdef DEBUG
+                       fprintf(stderr, "%s: File activity (%d)\n", __FUNCTION__, i);
+#endif
                        print_read_error(END_OF_DATA_UNEXPECTED);
                }
 
@@ -865,13 +944,25 @@ void read_header_data(void)
                            || (file_act.nr <= 0)
                            || (file_act.nr2 <= 0)
                            || (act[p]->magic != file_act.magic)) {
+#ifdef DEBUG
+                       if (p < 0) {
+                               fprintf(stderr, "%s: p=%d\n", __FUNCTION__, p);
+                       }
+                       else {
+                               fprintf(stderr, "%s: %s: size=%d/%d magic=%x/%x nr=%d nr2=%d types=%d,%d,%d/%d,%d,%d\n",
+                                       __FUNCTION__, act[p]->name, act[p]->fsize, file_act.size,
+                                       act[p]->magic, file_act.magic, file_act.nr, file_act.nr2,
+                                       act[p]->gtypes_nr[0], act[p]->gtypes_nr[1], act[p]->gtypes_nr[2],
+                                       file_act.types_nr[0], file_act.types_nr[1], file_act.types_nr[2]);
+                       }
+#endif
                        /* Remember that we are reading data from sadc and not from a file... */
                        print_read_error(INCONSISTENT_INPUT_DATA);
                }
 
-               id_seq[i]   = file_act.id;      /* We necessarily have "i < NR_ACT" */
-               act[p]->nr  = file_act.nr;
-               act[p]->nr2 = file_act.nr2;
+               id_seq[i]      = file_act.id;   /* We necessarily have "i < NR_ACT" */
+               act[p]->nr_ini = file_act.nr;
+               act[p]->nr2    = file_act.nr2;
        }
 
        while (i < NR_ACT) {
@@ -907,7 +998,7 @@ void read_stats_from_file(char from_file[])
        rows = get_win_height();
 
        /* Read file headers and activity list */
-       check_file_actlst(&ifd, from_file, act, &file_magic, &file_hdr,
+       check_file_actlst(&ifd, from_file, act, flags, &file_magic, &file_hdr,
                          &file_actlst, id_seq, FALSE, &endian_mismatch, &arch_64);
 
        /* Perform required allocations */
@@ -924,7 +1015,7 @@ void read_stats_from_file(char from_file[])
                 */
                do {
                        if (read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[0], &file_hdr,
-                                           arch_64, endian_mismatch)) {
+                                           arch_64, endian_mismatch, UEOF_STOP, sizeof(rec_hdr_tmp))) {
                                /* End of sa data file */
                                return;
                        }
@@ -933,7 +1024,7 @@ void read_stats_from_file(char from_file[])
                        if ((rtype == R_RESTART) || (rtype == R_COMMENT)) {
                                print_special_record(&record_hdr[0], flags + S_F_LOCAL_TIME,
                                                     &tm_start, &tm_end, rtype, ifd,
-                                                    &rectime, NULL, from_file, 0, &file_magic,
+                                                    &rectime, from_file, 0, &file_magic,
                                                     &file_hdr, act, &sar_fmt, endian_mismatch, arch_64);
                        }
                        else {
@@ -942,10 +1033,10 @@ void read_stats_from_file(char from_file[])
                                 * So read now the extra fields.
                                 */
                                read_file_stat_bunch(act, 0, ifd, file_hdr.sa_act_nr,
-                                                    file_actlst, endian_mismatch, arch_64);
+                                                    file_actlst, endian_mismatch, arch_64,
+                                                    from_file, &file_magic, UEOF_STOP);
                                if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME,
-                                                                  &record_hdr[0],
-                                                                  &rectime, NULL))
+                                                                  &record_hdr[0], &rectime))
                                        /*
                                         * An error was detected.
                                         * The timestamp hasn't been updated.
@@ -954,8 +1045,8 @@ void read_stats_from_file(char from_file[])
                        }
                }
                while ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
-                      (tm_start.use && (datecmp(&rectime, &tm_start) < 0)) ||
-                      (tm_end.use && (datecmp(&rectime, &tm_end) >=0)));
+                      (tm_start.use && (datecmp(&rectime, &tm_start, FALSE) < 0)) ||
+                      (tm_end.use && (datecmp(&rectime, &tm_end, FALSE) >= 0)));
 
                /* Save the first stats collected. Will be used to compute the average */
                copy_structures(act, id_seq, record_hdr, 2, 0);
@@ -971,6 +1062,10 @@ void read_stats_from_file(char from_file[])
                /*
                 * Read and write stats located between two possible Linux restarts.
                 * Activities that should be displayed are saved in id_seq[] array.
+                * Since we are reading from a file, we print all the stats for an
+                * activity before displaying the next activity.
+                * id_seq[] has been created in check_file_actlst(), retaining only
+                * activities known by current sysstat version.
                 */
                for (i = 0; i < NR_ACT; i++) {
 
@@ -985,7 +1080,7 @@ void read_stats_from_file(char from_file[])
                                handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, rows,
                                                      act[p]->id, &reset, file_actlst,
                                                      from_file, &file_magic, rec_hdr_tmp,
-                                                     endian_mismatch, arch_64);
+                                                     endian_mismatch, arch_64, sizeof(rec_hdr_tmp));
                        }
                        else {
                                unsigned int optf, msk;
@@ -999,30 +1094,38 @@ void read_stats_from_file(char from_file[])
                                                handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf,
                                                                      rows, act[p]->id, &reset, file_actlst,
                                                                      from_file, &file_magic, rec_hdr_tmp,
-                                                                     endian_mismatch, arch_64);
+                                                                     endian_mismatch, arch_64, sizeof(rec_hdr_tmp));
                                                act[p]->opt_flags = optf;
                                        }
                                }
                        }
                }
-
                if (!cnt) {
-                       /* Go to next Linux restart, if possible */
+                       /*
+                        * Go to next Linux restart, if possible.
+                        * Note: If we have @cnt == 0 then the last record we read was not a R_RESTART one
+                        * (else we would have had @cnt != 0, i.e. we would have stopped reading previous activity
+                        * because such a R_RESTART record would have been read, not because all the <count> lines
+                        * had been printed).
+                        * Remember @cnt is decremented only when a real line of stats have been displayed
+                        * (not when a special record has been read).
+                        */
                        do {
                                /* Read next record header */
                                eosaf = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[curr],
-                                                       &file_hdr, arch_64, endian_mismatch);
+                                                       &file_hdr, arch_64, endian_mismatch, UEOF_STOP, sizeof(rec_hdr_tmp));
                                rtype = record_hdr[curr].record_type;
 
                                if (!eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
                                        read_file_stat_bunch(act, curr, ifd, file_hdr.sa_act_nr,
-                                                            file_actlst, endian_mismatch, arch_64);
+                                                            file_actlst, endian_mismatch, arch_64,
+                                                            from_file, &file_magic, UEOF_STOP);
                                }
                                else if (!eosaf && (rtype == R_COMMENT)) {
                                        /* This was a COMMENT record: print it */
                                        print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
                                                             &tm_start, &tm_end, R_COMMENT, ifd,
-                                                            &rectime, NULL, from_file, 0,
+                                                            &rectime, from_file, 0,
                                                             &file_magic, &file_hdr, act, &sar_fmt,
                                                             endian_mismatch, arch_64);
                                }
@@ -1034,7 +1137,7 @@ void read_stats_from_file(char from_file[])
                if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) {
                        print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
                                             &tm_start, &tm_end, R_RESTART, ifd,
-                                            &rectime, NULL, from_file, 0,
+                                            &rectime, from_file, 0,
                                             &file_magic, &file_hdr, act, &sar_fmt,
                                             endian_mismatch, arch_64);
                }
@@ -1065,8 +1168,8 @@ void read_stats(void)
        read_header_data();
 
        if (!get_activity_nr(act, AO_SELECTED, COUNT_ACTIVITIES)) {
-               fprintf(stderr, _("Requested activities not available\n"));
-               exit(1);
+               /* Requested activities not available: Exit */
+               print_collect_error();
        }
 
        /* Determine if a stat line header has to be displayed */
@@ -1113,8 +1216,8 @@ void read_stats(void)
 
                /* Print results */
                if (!dis_hdr) {
-                       dis = lines / rows;
-                       if (dis) {
+                       dish = lines / rows;
+                       if (dish) {
                                lines %= rows;
                        }
                        lines++;
@@ -1142,7 +1245,7 @@ void read_stats(void)
         * At least one line of stats must have been displayed for this.
         * (There may be no lines at all if we press Ctrl/C immediately).
         */
-       dis = dis_hdr;
+       dish = dis_hdr;
        if (avg_count) {
                write_stats_avg(curr, USE_SADC, ALL_ACTIVITIES);
        }
@@ -1155,11 +1258,11 @@ void read_stats(void)
  */
 int main(int argc, char **argv)
 {
-       int i, rc, opt = 1, args_idx = 2;
+       int i, rc, opt = 1, args_idx = 1, p, q;
        int fd[2];
        int day_offset = 0;
        char from_file[MAX_FILE_LEN], to_file[MAX_FILE_LEN];
-       char ltemp[20];
+       char ltemp[1024];
 
        /* Compute page shift in kB */
        get_kb_shift();
@@ -1189,15 +1292,51 @@ int main(int argc, char **argv)
                        which_sadc();
                }
 
+               else if (!strncmp(argv[opt], "--dev=", 6)) {
+                       /* Parse devices entered on the command line */
+                       p = get_activity_position(act, A_DISK, EXIT_IF_NOT_FOUND);
+                       parse_sa_devices(argv[opt], act[p], MAX_DEV_LEN, &opt, 6);
+               }
+
+               else if (!strncmp(argv[opt], "--fs=", 5)) {
+                       /* Parse devices entered on the command line */
+                       p = get_activity_position(act, A_FS, EXIT_IF_NOT_FOUND);
+                       parse_sa_devices(argv[opt], act[p], MAX_FS_LEN, &opt, 5);
+               }
+
+               else if (!strncmp(argv[opt], "--iface=", 8)) {
+                       /* Parse devices entered on the command line */
+                       p = get_activity_position(act, A_NET_DEV, EXIT_IF_NOT_FOUND);
+                       parse_sa_devices(argv[opt], act[p], MAX_IFACE_LEN, &opt, 8);
+                       q = get_activity_position(act, A_NET_EDEV, EXIT_IF_NOT_FOUND);
+                       act[q]->item_list = act[p]->item_list;
+                       act[q]->item_list_sz = act[p]->item_list_sz;
+                       act[q]->options |= AO_LIST_ON_CMDLINE;
+               }
+
+               else if (!strcmp(argv[opt], "--help")) {
+                       /* Display help message */
+                       display_help(argv[0]);
+               }
+
                else if (!strcmp(argv[opt], "--human")) {
                        /* Display sizes in a human readable format */
                        flags |= S_F_UNIT;
                        opt++;
                }
 
+               else if (!strncmp(argv[opt], "--dec=", 6) && (strlen(argv[opt]) == 7)) {
+                       /* Get number of decimal places */
+                       dplaces_nr = atoi(argv[opt] + 6);
+                       if ((dplaces_nr < 0) || (dplaces_nr > 2)) {
+                               usage(argv[0]);
+                       }
+                       opt++;
+               }
+
                else if (!strcmp(argv[opt], "-I")) {
                        /* Parse -I option */
-                       if (parse_sar_I_opt(argv, &opt, act)) {
+                       if (parse_sar_I_opt(argv, &opt, &flags, act)) {
                                usage(argv[0]);
                        }
                }
@@ -1263,11 +1402,6 @@ int main(int argc, char **argv)
                        }
                }
 
-               else if (!strcmp(argv[opt], "-h")) {
-                       /* Display help message */
-                       display_help(argv[0]);
-               }
-
                else if (!strcmp(argv[opt], "-i")) {
                        if (!argv[++opt] || (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
                                usage(argv[0]);
@@ -1299,6 +1433,14 @@ int main(int argc, char **argv)
                        }
                }
 
+#ifdef TEST
+               else if (!strncmp(argv[opt], "--unix_time=", 12)) {
+                       if (strspn(argv[opt] + 12, DIGITS) != strlen(argv[opt] + 12)) {
+                               usage(argv[0]);
+                       }
+                       __unix_time = atoll(argv[opt++] + 12);
+               }
+#endif
                else if ((strlen(argv[opt]) > 1) &&
                         (strlen(argv[opt]) < 4) &&
                         !strncmp(argv[opt], "-", 1) &&
@@ -1351,7 +1493,7 @@ int main(int argc, char **argv)
 
        /* 'sar' is equivalent to 'sar -f' */
        if ((argc == 1) ||
-           ((interval < 0) && !from_file[0] && !to_file[0])) {
+           (((interval < 0) || INTERVAL_SET(flags)) && !from_file[0] && !to_file[0])) {
                set_default_file(from_file, day_offset, -1);
        }
 
@@ -1367,6 +1509,10 @@ int main(int argc, char **argv)
                fprintf(stderr, _("-f and -o options are mutually exclusive\n"));
                exit(1);
        }
+       if (USE_OPTION_A(flags)) {
+               /* Set -P ALL -I ALL if needed */
+               set_bitmaps(act, &flags);
+       }
        /* Use time start or option -i only when reading stats from a file */
        if ((tm_start.use || INTERVAL_SET(flags)) && !from_file[0]) {
                fprintf(stderr,
@@ -1398,6 +1544,10 @@ int main(int argc, char **argv)
        /* Default is CPU activity... */
        select_default_activity(act);
 
+       /* Check S_TIME_FORMAT variable contents */
+       if (!is_iso_time_fmt())
+               flags |= S_F_PREFD_TIME_OUTPUT;
+
        /* Reading stats from file: */
        if (from_file[0]) {
                if (interval < 0) {
@@ -1448,11 +1598,16 @@ int main(int argc, char **argv)
                }
                else if (!interval) {
                        strcpy(ltemp, "1");
+                       /*
+                        * Display stats since system startup: Set <interval> to 1.
+                        * <count> arg will also be set to 1 below.
+                        */
+                       salloc(args_idx++, ltemp);
                }
                else {
                        sprintf(ltemp, "%ld", interval);
                }
-               salloc(1, ltemp);
+               salloc(args_idx++, ltemp);
 
                /* Count number */
                if (count >= 0) {
@@ -1460,8 +1615,14 @@ int main(int argc, char **argv)
                        salloc(args_idx++, ltemp);
                }
 
+#ifdef TEST
+               if (__unix_time) {
+                       sprintf(ltemp, "--unix_time=%ld", __unix_time);
+                       salloc(args_idx++, ltemp);
+               }
+#endif
                /* Flags to be passed to sadc */
-               salloc(args_idx++, "-z");
+               salloc(args_idx++, "-Z");
 
                /* Writing data to a file (option -o) */
                if (to_file[0]) {
@@ -1480,27 +1641,29 @@ int main(int argc, char **argv)
                         * If option -o hasn't been used, then tell sadc
                         * to collect only activities that will be displayed.
                         */
-                       int act_id = 0;
-
+                       salloc(args_idx++, "-S");
+                       strcpy(ltemp, K_A_NULL);
                        for (i = 0; i < NR_ACT; i++) {
                                if (IS_SELECTED(act[i]->options)) {
-                                       act_id |= act[i]->group;
+                                       strcat(ltemp, ",");
+                                       strcat(ltemp, act[i]->name);
                                }
                        }
-                       if (act_id) {
-                               act_id <<= 8;
-                               snprintf(ltemp, 19, "%d", act_id);
-                               ltemp[19] = '\0';
-                               salloc(args_idx++, "-S");
-                               salloc(args_idx++, ltemp);
-                       }
+                       salloc(args_idx++, ltemp);
                }
 
                /* Last arg is NULL */
                args[args_idx] = NULL;
 
                /* Call now the data collector */
+#ifdef DEBUG
+               fprintf(stderr, "%s: 1.sadc: %s\n", __FUNCTION__, SADC_PATH);
+#endif
+
                execv(SADC_PATH, args);
+#ifdef DEBUG
+               fprintf(stderr, "%s: 2.sadc: %s\n", __FUNCTION__, SADC);
+#endif
                execvp(SADC, args);
                /*
                 * Note: Don't use execl/execlp since we don't have a fixed number of