/*
* 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 */
*/
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 */
"[ -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);
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);
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;
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;
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;
}
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);
* @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];
* @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);
* @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.
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;
* 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)) {
/* 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;
}
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);
}
__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... */
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 */
*/
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;
}
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 {
*/
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.
}
}
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);
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;
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);
}
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);
}
/* Print results */
if (!dis_hdr) {
- dis = lines / rows;
- if (dis) {
+ dish = lines / rows;
+ if (dish) {
lines %= rows;
}
lines++;
* 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);
}
*/
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];
else if (!strncmp(argv[opt], "--dev=", 6)) {
/* Parse devices entered on the command line */
- parse_sa_devices(argv[opt], &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(argv[opt], &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")) {
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]);
}
}
}
}
+#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) &&
/* '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);
}
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,
/* 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) {
/* 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;
}
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");
/* 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;
}