]> 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 7029801604cc4e3f04f84cb3daf6f860f1467d1d..70ce79c1b0a94beb4a59febf5937d5575994552e 100644 (file)
--- a/sar.c
+++ b/sar.c
@@ -1,6 +1,6 @@
 /*
  * sar: report system activity
- * (C) 1999-2018 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 *
 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 */
@@ -76,10 +80,6 @@ struct record_header record_hdr[3];
  */
 unsigned int id_seq[NR_ACT];
 
-/* Devices entered on the command line */
-struct sa_dlist *st_iface_list = NULL, *st_dev_list = NULL;
-int dlst_iface_idx = 0, dlst_dev_idx = 0;
-
 struct tm rectime;
 
 /* Contain the date specified by -s and -e options */
@@ -124,9 +124,9 @@ void usage(char *progname)
                          "[ -v ] [ -W ] [ -w ] [ -y ] [ -z ]\n"
                          "[ -I { <int_list> | SUM | ALL } ] [ -P { <cpu_list> | ALL } ]\n"
                          "[ -m { <keyword> [,...] | ALL } ] [ -n { <keyword> [,...] | ALL } ]\n"
-                         "[ --dec={ 0 | 1 | 2 } ] [ --dev=<dev_list> ] [ --iface=<iface_list> ]\n"
-                         "[ --help ] [ --human ] [ --sadc ]\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);
@@ -373,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);
@@ -427,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;
 
@@ -453,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;
 
@@ -495,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;
@@ -555,7 +549,7 @@ void write_stats_startup(int curr)
        }
 
        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);
@@ -608,11 +602,13 @@ int sa_read(void *buffer, size_t 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];
 
@@ -635,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);
@@ -734,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.
@@ -747,7 +746,7 @@ 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;
@@ -781,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, file, file_magic);
+                                            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)) {
 
@@ -803,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;
@@ -837,6 +841,12 @@ void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf
        }
        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);
        }
@@ -943,7 +953,7 @@ void read_header_data(void)
                                        __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]);
+                                       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... */
@@ -988,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 */
@@ -1005,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;
                        }
@@ -1014,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 {
@@ -1024,10 +1034,9 @@ void read_stats_from_file(char from_file[])
                                 */
                                read_file_stat_bunch(act, 0, ifd, file_hdr.sa_act_nr,
                                                     file_actlst, endian_mismatch, arch_64,
-                                                    from_file, &file_magic);
+                                                    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.
@@ -1036,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);
@@ -1071,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;
@@ -1085,31 +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,
-                                                            from_file, &file_magic);
+                                                            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);
                                }
@@ -1121,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);
                }
@@ -1200,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++;
@@ -1229,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);
        }
@@ -1242,7 +1258,7 @@ void read_stats(void)
  */
 int main(int argc, char **argv)
 {
-       int i, rc, opt = 1, args_idx = 1;
+       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];
@@ -1278,14 +1294,24 @@ int main(int argc, char **argv)
 
                else if (!strncmp(argv[opt], "--dev=", 6)) {
                        /* Parse devices entered on the command line */
-                       parse_sa_devices(argc, argv, &st_dev_list,
-                                        &dlst_dev_idx, &opt, 6);
+                       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 */
-                       parse_sa_devices(argc, argv, &st_iface_list,
-                                        &dlst_iface_idx, &opt, 8);
+                       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")) {
@@ -1310,7 +1336,7 @@ int main(int argc, char **argv)
 
                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]);
                        }
                }
@@ -1407,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) &&
@@ -1459,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);
        }
 
@@ -1475,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,
@@ -1506,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) {
@@ -1518,12 +1560,6 @@ int main(int argc, char **argv)
                /* Free stuctures and activity bitmaps */
                free_bitmaps(act);
                free_structures(act);
-               if (st_iface_list) {
-                       free(st_iface_list);
-               }
-               if (st_dev_list) {
-                       free(st_dev_list);
-               }
 
                return 0;
        }
@@ -1579,6 +1615,12 @@ 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");
 
@@ -1648,12 +1690,6 @@ int main(int argc, char **argv)
        /* Free structures and activity bitmaps */
        free_bitmaps(act);
        free_structures(act);
-       if (st_iface_list) {
-               free(st_iface_list);
-       }
-       if (st_dev_list) {
-               free(st_dev_list);
-       }
 
        return 0;
 }