]> granicus.if.org Git - sysstat/commitdiff
sar/sadf: Add extra flexibility in case of a change of file format
authorSebastien GODARD <sysstat@users.noreply.github.com>
Sat, 14 Sep 2019 08:00:58 +0000 (10:00 +0200)
committerSebastien GODARD <sysstat@users.noreply.github.com>
Sat, 14 Sep 2019 08:00:58 +0000 (10:00 +0200)
Add some extra code to sar and sadf so that they can read new
structures saved in daily binary data files. No changes in binary data
files are planned at the present time, but should a change happen in a
(hopefully) distant future, current sar and sadf commands will be able
to read this new format.

Signed-off-by: Sebastien GODARD <sysstat@users.noreply.github.com>
sa.h
sa_common.c
sadf.c
sar.c

diff --git a/sa.h b/sa.h
index 3082485bb0d323d17baa15ce0779ff2b5638ba62..51e2a6cd595c1a7d99f84d3144a0aa83a97b735f 100644 (file)
--- a/sa.h
+++ b/sa.h
@@ -365,17 +365,49 @@ struct svg_hdr_parm {
  *     |
  *     |--                         --|
  *     |                             |
- *     | file_activity structure     | sa_act_nr
+ *     | file_activity structure     | x file_header:sa_act_nr
  *     |                             |
- *     |--                         --|
+ *     |---------                  --|
+ *             |
+ *             | extra_desc structure (exists only if file_header:extra_next != 0)
+ *             |
+ *             |--                         --|
+ *             |                             |
+ *             | unknown extra structure(s)  | x extra_desc:extra_nr
+ *             |                             |
+ *             |--                         --|
+ *             |
+ *             | (extra_desc structure (exists only if previous extra_desc:extra_next != 0))
+ *             |
+ *             |--
+ *             |
+ *             | (...)
+ *             |
+ *     |---------                  --|
  *     |                             |
  *     | record_header structure     |
  *     |                             |
- *     |--                           |
+ *     |---------                  --|
+ *             |
+ *             | extra_desc structure (exists only if record_header:extra_next != 0)
+ *             |
+ *             |--                         --|
+ *             |                             |
+ *             | unknown extra structure(s)  | x extra_desc:extra_nr
+ *             |                             |
+ *             |--                         --|
+ *             |
+ *             | (extra_desc structure (exists only if previous extra_desc:extra_next != 0))
+ *             |
+ *             |--
+ *             |
+ *             | (...)
+ *             |
+ *     |---------                  --|
  *     |(__nr_t)                     |
  *     |--                           |
  *     |                             |
- *     | Statistics structure(s)     | * <count>
+ *     | Statistics structure(s)     | x <count>
  *     |                             |
  *     |--                           |
  *     |(__nr_t)                     |
@@ -402,6 +434,12 @@ struct svg_hdr_parm {
  * 2 for 1 CPU and SMP kernel (CPU "all" and CPU 0), etc.
  * Of course we display the real number of CPU (e.g. "1" for 1 CPU and SMP
  * kernel) with the LINUX RESTART message.
+ *
+ * If the record_header's type is R_EXTRA then we find only a list of extra
+ * structures following the record_header structure but no statistics ones.
+ * Note that extra structures may exist for all record_header types
+ * (R_STATS, R_COMMENT, R_RESTART...). For R_COMMENT and R_RESTART records,
+ * the extra structures will be found after the comment or the number of CPU.
  ***************************************************************************
  */
 
@@ -514,6 +552,10 @@ struct file_header {
         */
        unsigned int act_size;
        unsigned int rec_size;
+       /*
+        * TRUE if an extra_desc structure exists.
+        */
+       unsigned int extra_next;
        /*
         * Current day and month.
         * No need to save DST (Daylight Saving Time) flag, since it is not taken
@@ -547,7 +589,7 @@ struct file_header {
 #define FILE_HEADER_SIZE       (sizeof(struct file_header))
 #define FILE_HEADER_ULL_NR     1       /* Nr of unsigned long long in file_header structure */
 #define FILE_HEADER_UL_NR      1       /* Nr of unsigned long in file_header structure */
-#define FILE_HEADER_U_NR       11      /* Nr of [unsigned] int in file_header structure */
+#define FILE_HEADER_U_NR       12      /* Nr of [unsigned] int in file_header structure */
 /* The values below are used for sanity check */
 #define MIN_FILE_HEADER_SIZE   0
 #define MAX_FILE_HEADER_SIZE   8192
@@ -604,6 +646,35 @@ struct file_activity {
 #define FILE_ACTIVITY_UL_NR    0       /* Nr of unsigned long in file_activity structure */
 #define FILE_ACTIVITY_U_NR     9       /* Nr of [unsigned] int in file_activity structure */
 
+/*
+ * Description of an extra structure.
+ * The composition of this structure should not change in time.
+ */
+struct extra_desc {
+       /*
+        * Number of extra structures to read.
+        */
+       unsigned int extra_nr;
+       /*
+        * Size of an extra structure.
+        */
+       unsigned int extra_size;
+       /*
+        * TRUE if another extra_desc structure exists after
+        * all the extra structures.
+        */
+       unsigned int extra_next;
+       /*
+        * Description of an extra structure
+        * (nr of "long long", nr of "long" and nr of "int").
+        */
+       unsigned int extra_types_nr[3];
+};
+
+#define EXTRA_DESC_SIZE                (sizeof(struct extra_desc))
+#define EXTRA_DESC_ULL_NR      0       /* Nr of unsigned long long in extra_desc structure */
+#define EXTRA_DESC_UL_NR       0       /* Nr of unsigned long in extra_desc structure */
+#define EXTRA_DESC_U_NR                6       /* Nr of [unsigned] int in extra_desc structure */
 
 /* Record type */
 /*
@@ -627,6 +698,11 @@ struct file_activity {
  * a comment.
  */
 #define R_COMMENT      4
+/*
+ * R_EXTRA means that extra structures are following current
+ * record_header structure, but no statistics structures.
+ */
+#define R_EXTRA                5
 
 /* Maximum length of a comment */
 #define MAX_COMMENT_LEN        64
@@ -641,6 +717,10 @@ struct record_header {
         * Timestamp (number of seconds since the epoch).
         */
        unsigned long long ust_time;
+       /*
+        * TRUE if an extra_desc structure exists.
+        */
+       unsigned int extra_next;
        /*
         * Record type: R_STATS, R_RESTART,...
         */
@@ -658,7 +738,7 @@ struct record_header {
 #define MAX_RECORD_HEADER_SIZE 512     /* Used for sanity check */
 #define RECORD_HEADER_ULL_NR   2       /* Nr of unsigned long long in record_header structure */
 #define RECORD_HEADER_UL_NR    0       /* Nr of unsigned long in record_header structure */
-#define RECORD_HEADER_U_NR     0       /* Nr of unsigned int in record_header structure */
+#define RECORD_HEADER_U_NR     1       /* Nr of unsigned int in record_header structure */
 
 
 /*
@@ -1341,6 +1421,8 @@ void print_collect_error
        (void);
 int set_default_file
        (char *, int, int);
+int skip_extra_struct
+       (int, int, int);
 int write_all
        (int, const void *, int);
 
index 1b18dc00f6627eb6c76057ae338a7bd714a6c59c..c60d302cd512598c8df67ef97c0a7b8f4bd9ac51 100644 (file)
@@ -52,6 +52,7 @@ extern unsigned int dm_major;
 unsigned int hdr_types_nr[] = {FILE_HEADER_ULL_NR, FILE_HEADER_UL_NR, FILE_HEADER_U_NR};
 unsigned int act_types_nr[] = {FILE_ACTIVITY_ULL_NR, FILE_ACTIVITY_UL_NR, FILE_ACTIVITY_U_NR};
 unsigned int rec_types_nr[] = {RECORD_HEADER_ULL_NR, RECORD_HEADER_UL_NR, RECORD_HEADER_U_NR};
+unsigned int extra_desc_types_nr[] = {EXTRA_DESC_ULL_NR, EXTRA_DESC_UL_NR, EXTRA_DESC_U_NR};
 unsigned int nr_types_nr[]  = {0, 0, 1};
 
 /*
@@ -1448,6 +1449,58 @@ int sa_fread(int ifd, void *buffer, size_t size, int mode, int oneof)
        return 0;
 }
 
+/*
+ ***************************************************************************
+ * Skip unknown extra structures present in file.
+ *
+ * IN:
+ * @ifd                System activity data file descriptor.
+ * @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.
+ *
+ * RETURNS:
+ * -1 on error, 0 otherwise.
+ ***************************************************************************
+ */
+int skip_extra_struct(int ifd, int endian_mismatch, int arch_64)
+{
+       int i;
+       struct extra_desc xtra_d;
+
+       do {
+               /* Read extra structure description */
+               sa_fread(ifd, &xtra_d, EXTRA_DESC_SIZE, HARD_SIZE, UEOF_STOP);
+
+               /*
+                * We don't need to remap as the extra_desc structure won't change,
+                * but we may need to normalize endianness anyway.
+                */
+               if (endian_mismatch) {
+                       swap_struct(extra_desc_types_nr, &xtra_d, arch_64);
+               }
+
+               /* Check values consistency */
+               if (MAP_SIZE(xtra_d.extra_types_nr) > xtra_d.extra_size) {
+#ifdef DEBUG
+                       fprintf(stderr, "%s: extra_size=%u types=%d,%d,%d\n",
+                               __FUNCTION__, xtra_d.extra_size,
+                               xtra_d.extra_types_nr[0], xtra_d.extra_types_nr[1], xtra_d.extra_types_nr[2]);
+#endif
+                       return -1;
+               }
+
+               /* Ignore current unknown extra structures */
+               for (i = 0; i < xtra_d.extra_nr; i++) {
+                       if (lseek(ifd, xtra_d.extra_size, SEEK_CUR) < xtra_d.extra_size)
+                               return -1;
+               }
+       }
+       while (xtra_d.extra_next);
+
+       return 0;
+}
+
 /*
  ***************************************************************************
  * Read the record header of current sample and process it.
@@ -1481,20 +1534,32 @@ int read_record_hdr(int ifd, void *buffer, struct record_header *record_hdr,
 {
        int rc;
 
-       if ((rc = sa_fread(ifd, buffer, (size_t) file_hdr->rec_size, SOFT_SIZE, oneof)) != 0)
-               /* End of sa data file */
-               return rc;
+       do {
+               if ((rc = sa_fread(ifd, buffer, (size_t) file_hdr->rec_size, SOFT_SIZE, oneof)) != 0)
+                       /* End of sa data file */
+                       return rc;
 
-       /* Remap record header structure to that expected by current version */
-       if (remap_struct(rec_types_nr, file_hdr->rec_types_nr, buffer,
-                        file_hdr->rec_size, RECORD_HEADER_SIZE, b_size) < 0)
-               return 2;
-       memcpy(record_hdr, buffer, RECORD_HEADER_SIZE);
+               /* Remap record header structure to that expected by current version */
+               if (remap_struct(rec_types_nr, file_hdr->rec_types_nr, buffer,
+                                file_hdr->rec_size, RECORD_HEADER_SIZE, b_size) < 0)
+                       return 2;
+               memcpy(record_hdr, buffer, RECORD_HEADER_SIZE);
 
-       /* Normalize endianness */
-       if (endian_mismatch) {
-               swap_struct(rec_types_nr, record_hdr, arch_64);
+               /* Normalize endianness */
+               if (endian_mismatch) {
+                       swap_struct(rec_types_nr, record_hdr, arch_64);
+               }
+
+               /*
+                * Skip unknown extra structures if present.
+                * This will be done later for R_COMMENT and R_RESTART records, as extra structures
+                * are saved after the comment or the number of CPU.
+                */
+               if ((record_hdr->record_type != R_COMMENT) && (record_hdr->record_type != R_RESTART) &&
+                   record_hdr->extra_next && (skip_extra_struct(ifd, endian_mismatch, arch_64) < 0))
+                       return 2;
        }
+       while (record_hdr->record_type == R_EXTRA);
 
        return 0;
 }
@@ -2089,6 +2154,13 @@ void check_file_actlst(int *ifd, char *dfile, struct activity *act[], unsigned i
                exit(1);
        }
 
+       /*
+        * Check if there are some extra structures.
+        * We will just skip them as they are unknown for now.
+        */
+       if (file_hdr->extra_next && (skip_extra_struct(*ifd, *endian_mismatch, *arch_64) < 0))
+               goto format_error;
+
        return;
 
 format_error:
@@ -2956,6 +3028,10 @@ int print_special_record(struct record_header *record_hdr, unsigned int l_flags,
                        }
                }
 
+               /* Ignore unknown extra structures if present */
+               if (record_hdr->extra_next && (skip_extra_struct(ifd, endian_mismatch, arch_64) < 0))
+                       return 0;
+
                if (!dp)
                        return 0;
 
@@ -2971,6 +3047,10 @@ int print_special_record(struct record_header *record_hdr, unsigned int l_flags,
                /* Read and replace non printable chars in comment */
                replace_nonprintable_char(ifd, file_comment);
 
+               /* Ignore unknown extra structures if present */
+               if (record_hdr->extra_next && (skip_extra_struct(ifd, endian_mismatch, arch_64) < 0))
+                       return 0;
+
                if (!dp || !DISPLAY_COMMENT(l_flags))
                        return 0;
 
diff --git a/sadf.c b/sadf.c
index 78b6b3db80916282750c4bae3f3b20586777250f..496665cf80d2ff5c76f97d756e65583d610aca36 100644 (file)
--- a/sadf.c
+++ b/sadf.c
@@ -250,12 +250,16 @@ int read_next_sample(int ifd, int action, int curr, char *file, int *rtype, int
                if (action & IGNORE_COMMENT) {
                        /* Ignore COMMENT record */
                        if (lseek(ifd, MAX_COMMENT_LEN, SEEK_CUR) < MAX_COMMENT_LEN) {
-                               perror("lseek");
                                if (oneof == UEOF_CONT)
                                        return 2;
                                close(ifd);
                                exit(2);
                        }
+
+                       /* Ignore unknown extra structures if present */
+                       if (record_hdr[curr].extra_next && (skip_extra_struct(ifd, endian_mismatch, arch_64) < 0))
+                               return 2;
+
                        if (action & SET_TIMESTAMPS) {
                                sa_get_record_timestamp_struct(flags, &record_hdr[curr],
                                                               rectime);
@@ -279,6 +283,10 @@ int read_next_sample(int ifd, int action, int curr, char *file, int *rtype, int
                        if (!(action & DONT_READ_CPU_NR)) {
                                file_hdr.sa_cpu_nr = read_nr_value(ifd, file, file_magic,
                                                                   endian_mismatch, arch_64, TRUE);
+
+                               /* Ignore unknown extra structures if present */
+                               if (record_hdr[curr].extra_next && (skip_extra_struct(ifd, endian_mismatch, arch_64) < 0))
+                                       return 2;
                        }
                        if (action & SET_TIMESTAMPS) {
                                sa_get_record_timestamp_struct(flags, &record_hdr[curr], rectime);
diff --git a/sar.c b/sar.c
index 959b027e117a7176fc5e1450bd2817d8eb12a6ba..70ce79c1b0a94beb4a59febf5937d5575994552e 100644 (file)
--- a/sar.c
+++ b/sar.c
@@ -841,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);
        }
@@ -1094,9 +1100,16 @@ void read_stats_from_file(char from_file[])
                                }
                        }
                }
-
                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],