/*
* sar: report system activity
- * (C) 1999-2019 by Sebastien GODARD (sysstat <at> orange.fr)
+ * (C) 1999-2021 by Sebastien GODARD (sysstat <at> orange.fr)
*
***************************************************************************
* This program is free software; you can redistribute it and/or modify it *
int dplaces_nr = -1;
uint64_t flags = 0;
-unsigned int dm_major; /* Device-mapper major number */
char timestamp[2][TIMESTAMP_LEN];
extern unsigned int rec_types_nr[];
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"
+ "[ -p ] [ -r [ ALL ] ] [ -S ] [ -t ] [ -u [ ALL ] ] [ -V ]\n"
"[ -v ] [ -W ] [ -w ] [ -y ] [ -z ]\n"
"[ -I { <int_list> | SUM | ALL } ] [ -P { <cpu_list> | ALL } ]\n"
"[ -m { <keyword> [,...] | ALL } ] [ -n { <keyword> [,...] | ALL } ]\n"
- "[ --dev=<dev_list> ] [ --fs=<fs_list> ] [ --iface=<iface_list> ]\n"
- "[ --dec={ 0 | 1 | 2 } ] [ --help ] [ --human ] [ --sadc ]\n"
+ "[ -q [ <keyword> [,...] | ALL ] ]\n"
+ "[ --dev=<dev_list> ] [ --fs=<fs_list> ] [ --iface=<iface_list> ] "
+ "[ --int=<int_list> ]\n"
+ "[ --dec={ 0 | 1 | 2 } ] [ --help ] [ --human ] [ --pretty ] [ --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"));
"\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 [ <keyword> [,...] | 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"));
/* 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';
- memcpy(timestamp[!curr], timestamp[curr], TIMESTAMP_LEN);
+ 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);
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, prev_hour;
+ 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 (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],
if (use_tm_start && record_hdr[!curr].ust_time &&
(record_hdr[curr].ust_time > record_hdr[!curr].ust_time) &&
(rectime.tm_hour < prev_hour)) {
- cross_day = 1;
+ cross_day = TRUE;
}
/* Check time (2) */
- if (use_tm_start && (datecmp(&rectime, &tm_start, cross_day) < 0))
- /* it's too soon... */
- 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, cross_day) > 0)) {
- /* It's too late... */
+ /* 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);
+
avg_count++;
/* Test stdout */
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;
}
/*
* @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, size_t size)
+size_t sa_read(void *buffer, size_t size)
{
ssize_t n;
}
if (!n)
- return 1; /* EOF */
+ return size; /* EOF */
size -= n;
buffer = (char *) buffer + n;
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
/*
***************************************************************************
- * 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.
* 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, UEOF_STOP, b_size, flags);
+ &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, UEOF_STOP);
- }
-
if ((lines >= rows) || !lines) {
lines = 0;
- dish = 1;
+ dish = TRUE;
}
else
- dish = 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, 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;
- }
+ 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
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);
}
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__,
+ 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);
/* Read file headers and activity list */
check_file_actlst(&ifd, from_file, act, flags, &file_magic, &file_hdr,
- &file_actlst, id_seq, FALSE, &endian_mismatch, &arch_64);
+ &file_actlst, id_seq, &endian_mismatch, &arch_64);
/* Perform required allocations */
allocate_structures(act);
*/
do {
if (read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[0], &file_hdr,
- arch_64, endian_mismatch, UEOF_STOP, sizeof(rec_hdr_tmp), flags)) {
+ arch_64, endian_mismatch, UEOF_STOP, sizeof(rec_hdr_tmp), flags, &sar_fmt)) {
/* End of sa data file */
return;
}
* 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, UEOF_STOP);
+ 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))
/*
}
}
}
- if (!cnt) {
+ 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
do {
/* Read next record header */
eosaf = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[curr],
- &file_hdr, arch_64, endian_mismatch, UEOF_STOP, sizeof(rec_hdr_tmp), flags);
+ &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, UEOF_STOP);
+ 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, from_file, 0,
endian_mismatch, arch_64);
}
}
- while (!eosaf && (rtype != R_RESTART));
+ while (1);
}
/* The last record we read was a RESTART one: Print it */
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);
+ parse_sa_devices(argv[opt], act[p], MAX_DEV_LEN, &opt, 6, NO_RANGE);
}
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);
+ parse_sa_devices(argv[opt], act[p], MAX_FS_LEN, &opt, 5, NO_RANGE);
}
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);
+ parse_sa_devices(argv[opt], act[p], MAX_IFACE_LEN, &opt, 8, NO_RANGE);
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 (!strncmp(argv[opt], "--int=", 6)) {
+ /* Parse interrupts names entered on the command line */
+ p = get_activity_position(act, A_IRQ, EXIT_IF_NOT_FOUND);
+ parse_sa_devices(argv[opt], act[p], MAX_SA_IRQ_LEN, &opt, 6, NR_IRQS);
+ }
+
else if (!strcmp(argv[opt], "--help")) {
/* Display help message */
display_help(argv[0]);
opt++;
}
+ 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);
opt++;
}
- else if (!strcmp(argv[opt], "-I")) {
- /* Parse -I option */
- if (parse_sar_I_opt(argv, &opt, &flags, act)) {
- usage(argv[0]);
- }
- }
-
else if (!strcmp(argv[opt], "-D")) {
/* Option to tell sar to write to saYYYYMMDD data files */
flags |= S_F_SA_YYYYMMDD;
/* 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, "-");
/* 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);
}
}
}
+ 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;
exit(1);
}
if (USE_OPTION_A(flags)) {
- /* Set -P ALL -I ALL if needed */
+ /* Set -P ALL if needed */
set_bitmaps(act, &flags);
}
/* Use time start or option -i only when reading stats from a file */
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
/* 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);