X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=sar.c;h=c49e777074cbb5abe23852045e8a322bd90a1109;hb=6a062551999a466d7da5efb39cd98da2a1b4d0cf;hp=7d2be8646bf765dc62e90a8d477b3180bd00f450;hpb=15157c76b1f1bc2923089dca24e7f65f2eb765ca;p=sysstat diff --git a/sar.c b/sar.c index 7d2be86..c49e777 100644 --- a/sar.c +++ b/sar.c @@ -1,6 +1,6 @@ /* * sar: report system activity - * (C) 1999-2017 by Sebastien GODARD (sysstat orange.fr) + * (C) 1999-2021 by Sebastien GODARD (sysstat orange.fr) * *************************************************************************** * This program is free software; you can redistribute it and/or modify it * @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -44,18 +45,24 @@ char *sccsid(void) { return (SCCSID); } #endif +#ifdef TEST +extern time_t __unix_time; +extern int __env; +#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 */ +uint64_t flags = 0; char timestamp[2][TIMESTAMP_LEN]; extern unsigned int rec_types_nr[]; @@ -114,11 +121,14 @@ void usage(char *progname) print_usage_title(stderr, 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 ] [ --help ] [ --human ] [ --sadc ]\n" + "[ -p ] [ -r [ ALL ] ] [ -S ] [ -t ] [ -u [ ALL ] ] [ -V ]\n" + "[ -v ] [ -W ] [ -w ] [ -y ] [ -z ]\n" "[ -I { | SUM | ALL } ] [ -P { | ALL } ]\n" "[ -m { [,...] | ALL } ] [ -n { [,...] | ALL } ]\n" - "[ -j { ID | LABEL | PATH | UUID | ... } ]\n" + "[ -q [ [,...] | ALL ] ]\n" + "[ --dev= ] [ --fs= ] [ --iface= ]\n" + "[ --dec={ 0 | 1 | 2 } ] [ --help ] [ --human ] [ --pretty ] [ --sadc ]\n" + "[ -j { SID | ID | LABEL | PATH | UUID | ... } ]\n" "[ -f [ ] | -o [ ] | -[0-9]+ ]\n" "[ -i ] [ -s [ ] ] [ -e [ ] ]\n")); exit(1); @@ -176,7 +186,13 @@ 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 [A_QUEUE]\n")); + printf(_("\t-q [ [,...] | PSI | ALL ]\n" + "\t\tSystem load and pressure-stall statistics\n" + "\t\tKeywords are:\n" + "\t\tLOAD\tQueue length and load average statistics [A_QUEUE]\n" + "\t\tCPU\tPressure-stall CPU statistics [A_PSI_CPU]\n" + "\t\tIO\tPressure-stall I/O statistics [A_PSI_IO]\n" + "\t\tMEM\tPressure-stall memory statistics [A_PSI_MEM]\n")); printf(_("\t-r [ ALL ]\n" "\t\tMemory utilization statistics [A_MEMORY]\n")); printf(_("\t-S\tSwap space utilization statistics [A_MEMORY]\n")); @@ -363,9 +379,9 @@ void write_stats_avg(int curr, int read_from_file, unsigned int act_id) /* Interval value in 1/100th of a second */ itv = get_interval(record_hdr[2].uptime_cs, record_hdr[curr].uptime_cs); - strncpy(timestamp[curr], _("Average:"), TIMESTAMP_LEN); - timestamp[curr][TIMESTAMP_LEN - 1] = '\0'; - strcpy(timestamp[!curr], timestamp[curr]); + strncpy(timestamp[curr], _("Average:"), sizeof(timestamp[curr])); + timestamp[curr][sizeof(timestamp[curr]) - 1] = '\0'; + memcpy(timestamp[!curr], timestamp[curr], sizeof(timestamp[!curr])); /* Test stdout */ TEST_STDOUT(STDOUT_FILENO); @@ -419,80 +435,67 @@ 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, rc = 0; unsigned long long itv; - static int cross_day = 0; + static int cross_day = FALSE; if (reset_cd) { /* * cross_day is a static variable that is set to 1 when the first * record of stats from a new day is read from a unique data file * (in the case where the file contains data from two consecutive - * days). When set to 1, every following records timestamp will + * days). When set to TRUE, every following records timestamp will * have its hour value increased by 24. * Yet when a new activity (being read from the file) is going to * be displayed, we start reading the file from the beginning * again, and so cross_day should be reset in this case. */ - cross_day = 0; + cross_day = FALSE; } /* Check time (1) */ - if (read_from_file) { - if (!next_slice(record_hdr[2].uptime_cs, record_hdr[curr].uptime_cs, - reset, interval)) - /* Not close enough to desired interval */ - return 0; - } - - if (!is_iso_time_fmt()) - flags |= S_F_PREFD_TIME_OUTPUT; + if (read_from_file && !next_slice(record_hdr[2].uptime_cs, record_hdr[curr].uptime_cs, + reset, interval)) + /* Not close enough to desired interval */ + return 0; /* 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)) { - 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; + (rectime.tm_hour < prev_hour)) { + cross_day = TRUE; } /* Check time (2) */ - if (use_tm_start && (datecmp(&rectime, &tm_start) < 0)) - /* it's too soon... */ + if (use_tm_end && (datecmp(&rectime, &tm_end, cross_day) > 0)) { + /* End time exceeded */ + *cnt = 0; return 0; + } /* Get interval value in 1/100th of a second */ get_itv_value(&record_hdr[curr], &record_hdr[!curr], &itv); - /* Check time (3) */ - if (use_tm_end && (datecmp(&rectime, &tm_end) > 0)) { - /* It's too late... */ - *cnt = 0; - return 0; - } - avg_count++; /* Test stdout */ @@ -506,10 +509,11 @@ int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start, if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) { /* Display current activity statistics */ (*act[i]->f_print)(act[i], !curr, curr, itv); + rc = 1; } } - return 1; + return rc; } /* @@ -547,7 +551,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); @@ -566,12 +570,13 @@ void write_stats_startup(int curr) * @buffer Buffer where data will be saved. * * RETURNS: - * 1 if end of file has been reached, 0 otherwise. + * 0 if all the data have been successfully read. + * Otherwise, return the number of bytes left to be read. *************************************************************************** */ -int sa_read(void *buffer, int size) +size_t sa_read(void *buffer, size_t size) { - int n; + ssize_t n; while (size) { @@ -581,7 +586,7 @@ int sa_read(void *buffer, int size) } if (!n) - return 1; /* EOF */ + return size; /* EOF */ size -= n; buffer = (char *) buffer + n; @@ -600,11 +605,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]; @@ -627,10 +634,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); @@ -658,6 +667,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); } @@ -669,16 +681,22 @@ void read_sadc_stat_bunch(int curr) 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) { + 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]); + 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 @@ -689,7 +707,11 @@ void read_sadc_stat_bunch(int curr) (size_t) act[p]->fsize * (size_t) act[p]->nr_ini * (size_t) act[p]->nr2); } } - if (sa_read(act[p]->buf[curr], act[p]->fsize * act[p]->nr[curr] * 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); } } @@ -697,7 +719,8 @@ void read_sadc_stat_bunch(int curr) /* *************************************************************************** - * Read stats for current activity from file and display them. + * Read current activity's statistics (located between two consecutive + * LINUX RESTART messages) from file and display them. * * IN: * @ifd Input file descriptor. @@ -712,6 +735,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. @@ -725,7 +749,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; @@ -759,61 +783,75 @@ 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, + flags, &sar_fmt); 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); - } - if ((lines >= rows) || !lines) { lines = 0; - dis = 1; + dish = TRUE; } else - dis = 0; - - if (!*eosaf && (rtype != R_RESTART)) { - - if (rtype == R_COMMENT) { - /* 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, - file_magic, &file_hdr, act, &sar_fmt, - endian_mismatch, arch_64); - if (next) { - /* A line of comment was actually displayed */ - lines++; - } - continue; - } + dish = FALSE; + + if (*eosaf || (rtype == R_RESTART)) + /* This is EOF or we have met a LINUX RESTART record: Stop now */ + break; - /* next is set to 1 when we were close enough to desired interval */ - next = write_stats(*curr, USE_SA_FILE, cnt, tm_start.use, tm_end.use, - *reset, act_id, reset_cd); - reset_cd = 0; - if (next && (*cnt > 0)) { - (*cnt)--; + if (rtype != R_COMMENT) { + /* Read the extra fields since it's not a special record */ + if (read_file_stat_bunch(act, *curr, ifd, file_hdr.sa_act_nr, file_actlst, + endian_mismatch, arch_64, file, file_magic, UEOF_STOP)) + /* Error or unexpected EOF */ + break; + } + else { + /* Display comment */ + next = print_special_record(&record_hdr[*curr], flags + S_F_LOCAL_TIME, + &tm_start, &tm_end, R_COMMENT, ifd, + &rectime, file, 0, + file_magic, &file_hdr, act, &sar_fmt, + endian_mismatch, arch_64); + 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; + } - if (next) { - davg++; - *curr ^= 1; + /* next is set to 1 when we were close enough to desired interval */ + next = write_stats(*curr, USE_SA_FILE, cnt, tm_start.use, tm_end.use, + *reset, act_id, reset_cd); + reset_cd = 0; + if (next && (*cnt > 0)) { + (*cnt)--; + } - if (inc) { - lines += inc; - } - else { - lines += act[p]->nr[*curr]; - } + if (next) { + davg++; + *curr ^= 1; + + if (inc) { + lines += inc; + } + else { + lines += act[p]->nr[*curr]; } - *reset = FALSE; } + *reset = FALSE; } - while (*cnt && !*eosaf && (rtype != R_RESTART)); + while (*cnt); + + /* + * 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); @@ -856,6 +894,19 @@ 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 + if (rc == FILE_MAGIC_SIZE) { + /* + * No data (0 byte) have been sent by sadc. + * This is probably because no activities have been collected + * ("Requested activities not available"). In this case, don't + * display an error message: Exit now. + */ + exit(3); + } print_read_error(INCONSISTENT_INPUT_DATA); } @@ -866,16 +917,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/%zu rec_size=%u/%zu\n", __FUNCTION__, + file_hdr.act_size, FILE_ACTIVITY_SIZE, file_hdr.rec_size, RECORD_HEADER_SIZE); +#endif print_read_error(INCONSISTENT_INPUT_DATA); } @@ -883,6 +944,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); } @@ -895,6 +959,18 @@ 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); } @@ -937,8 +1013,8 @@ 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, - &file_actlst, id_seq, FALSE, &endian_mismatch, &arch_64); + check_file_actlst(&ifd, from_file, act, flags, &file_magic, &file_hdr, + &file_actlst, id_seq, &endian_mismatch, &arch_64); /* Perform required allocations */ allocate_structures(act); @@ -954,7 +1030,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), flags, &sar_fmt)) { /* End of sa data file */ return; } @@ -963,7 +1039,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 { @@ -971,12 +1047,14 @@ void read_stats_from_file(char from_file[]) * OK: Previous record was not a special one. * So read now the extra fields. */ - read_file_stat_bunch(act, 0, ifd, file_hdr.sa_act_nr, - file_actlst, endian_mismatch, arch_64, - from_file, &file_magic); + if (read_file_stat_bunch(act, 0, ifd, file_hdr.sa_act_nr, + file_actlst, endian_mismatch, arch_64, + from_file, &file_magic, UEOF_STOP)) + /* Possible unexpected EOF */ + return; + 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. @@ -985,8 +1063,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); @@ -1020,7 +1098,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; @@ -1034,43 +1112,55 @@ 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 */ + if (cnt == 0) { + /* + * 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 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), flags, &sar_fmt); 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); + if (eosaf || (rtype == R_RESTART)) + break; + + if (rtype != R_COMMENT) { + if (read_file_stat_bunch(act, curr, ifd, file_hdr.sa_act_nr, + file_actlst, endian_mismatch, arch_64, + from_file, &file_magic, UEOF_STOP)) + /* Possible unexpected EOF */ + break; } - else if (!eosaf && (rtype == R_COMMENT)) { - /* This was a COMMENT record: print it */ + else { + /* 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); } } - while (!eosaf && (rtype != R_RESTART)); + while (1); } /* The last record we read was a RESTART one: Print it */ 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); } @@ -1149,8 +1239,8 @@ void read_stats(void) /* Print results */ if (!dis_hdr) { - dis = lines / rows; - if (dis) { + dish = lines / rows; + if (dish) { lines %= rows; } lines++; @@ -1178,7 +1268,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); } @@ -1191,7 +1281,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]; @@ -1207,9 +1297,6 @@ int main(int argc, char **argv) init_nls(); #endif - /* Init color strings */ - init_colors(); - tm_start.use = tm_end.use = FALSE; /* Allocate and init activity bitmaps */ @@ -1225,6 +1312,28 @@ 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]); @@ -1236,18 +1345,24 @@ int main(int argc, char **argv) opt++; } - else if (!strcmp(argv[opt], "-h")) { - /* - * Make output easier to read by a human. - * Option -h implies --human and -p (pretty-print). - */ - flags |= S_F_HUMAN_READ + S_F_UNIT + S_F_DEV_PRETTY; + else if (!strcmp(argv[opt], "--pretty")) { + /* Display an easy-to-read report */ + flags |= S_F_PRETTY; + 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]); } } @@ -1273,8 +1388,8 @@ int main(int argc, char **argv) /* Save stats to a file */ if ((argv[++opt]) && strncmp(argv[opt], "-", 1) && (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) { - strncpy(to_file, argv[opt++], MAX_FILE_LEN); - to_file[MAX_FILE_LEN - 1] = '\0'; + strncpy(to_file, argv[opt++], sizeof(to_file)); + to_file[sizeof(to_file) - 1] = '\0'; } else { strcpy(to_file, "-"); @@ -1289,8 +1404,8 @@ int main(int argc, char **argv) /* Read stats from a file */ if ((argv[++opt]) && strncmp(argv[opt], "-", 1) && (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) { - strncpy(from_file, argv[opt++], MAX_FILE_LEN); - from_file[MAX_FILE_LEN - 1] = '\0'; + strncpy(from_file, argv[opt++], sizeof(from_file)); + from_file[sizeof(from_file) - 1] = '\0'; /* Check if this is an alternate directory for sa files */ check_alt_sa_dir(from_file, day_offset, -1); } @@ -1344,6 +1459,29 @@ int main(int argc, char **argv) } } + else if (!strcmp(argv[opt], "-q")) { + if (!argv[++opt]) { + SELECT_ACTIVITY(A_QUEUE); + } + /* Parse option -q */ + else if (parse_sar_q_opt(argv, &opt, act)) { + SELECT_ACTIVITY(A_QUEUE); + } + } + +#ifdef TEST + else if (!strncmp(argv[opt], "--getenv", 8)) { + __env = TRUE; + opt++; + } + + 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) && @@ -1394,9 +1532,12 @@ int main(int argc, char **argv) } } + /* Init color strings */ + init_colors(); + /* '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); } @@ -1412,6 +1553,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, @@ -1428,10 +1573,6 @@ int main(int argc, char **argv) usage(argv[0]); } - if (USE_PRETTY_OPTION(flags)) { - dm_major = get_devmap_major(); - } - if (!count) { /* * count parameter not set: Display all the contents of the file @@ -1443,6 +1584,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) { @@ -1452,7 +1597,7 @@ int main(int argc, char **argv) /* Read stats from file */ read_stats_from_file(from_file); - /* Free stuctures and activity bitmaps */ + /* Free structures and activity bitmaps */ free_bitmaps(act); free_structures(act); @@ -1510,8 +1655,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]) { @@ -1545,7 +1696,14 @@ int main(int argc, char **argv) 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