2 * sar: report system activity
3 * (C) 1999-2020 by Sebastien GODARD (sysstat <at> orange.fr)
5 ***************************************************************************
6 * This program is free software; you can redistribute it and/or modify it *
7 * under the terms of the GNU General Public License as published by the *
8 * Free Software Foundation; either version 2 of the License, or (at your *
9 * option) any later version. *
11 * This program is distributed in the hope that it will be useful, but *
12 * WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY *
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
16 * You should have received a copy of the GNU General Public License along *
17 * with this program; if not, write to the Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA *
19 ***************************************************************************
38 #define _(string) gettext(string)
40 #define _(string) (string)
44 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
45 char *sccsid(void) { return (SCCSID); }
49 extern time_t __unix_time;
53 /* Interval and count parameters */
54 long interval = -1, count = 0;
56 /* TRUE if a header line must be printed */
58 /* TRUE if data read from file don't match current machine's endianness */
59 int endian_mismatch = FALSE;
60 /* TRUE if file's data come from a 64 bit machine */
62 /* Number of decimal places */
67 char timestamp[2][TIMESTAMP_LEN];
68 extern unsigned int rec_types_nr[];
70 unsigned long avg_count = 0;
73 struct file_header file_hdr;
75 /* Current record header */
76 struct record_header record_hdr[3];
80 * This array must always be entirely filled (even with trailing zeros).
82 unsigned int id_seq[NR_ACT];
86 /* Contain the date specified by -s and -e options */
87 struct tstamp tm_start, tm_end;
89 char *args[MAX_ARGV_NR];
91 extern struct activity *act[];
92 extern struct report_format sar_fmt;
94 struct sigaction int_act;
95 int sigint_caught = 0;
98 ***************************************************************************
99 * Print usage title message.
102 * @progname Name of sysstat command
103 ***************************************************************************
105 void print_usage_title(FILE *fp, char *progname)
107 fprintf(fp, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
112 ***************************************************************************
113 * Print usage and exit.
116 * @progname Name of sysstat command
117 ***************************************************************************
119 void usage(char *progname)
121 print_usage_title(stderr, progname);
122 fprintf(stderr, _("Options are:\n"
123 "[ -A ] [ -B ] [ -b ] [ -C ] [ -D ] [ -d ] [ -F [ MOUNT ] ] [ -H ] [ -h ]\n"
124 "[ -p ] [ -r [ ALL ] ] [ -S ] [ -t ] [ -u [ ALL ] ] [ -V ]\n"
125 "[ -v ] [ -W ] [ -w ] [ -y ] [ -z ]\n"
126 "[ -I { <int_list> | SUM | ALL } ] [ -P { <cpu_list> | ALL } ]\n"
127 "[ -m { <keyword> [,...] | ALL } ] [ -n { <keyword> [,...] | ALL } ]\n"
128 "[ -q [ <keyword> [,...] | ALL ] ]\n"
129 "[ --dev=<dev_list> ] [ --fs=<fs_list> ] [ --iface=<iface_list> ]\n"
130 "[ --dec={ 0 | 1 | 2 } ] [ --help ] [ --human ] [ --pretty ] [ --sadc ]\n"
131 "[ -j { SID | ID | LABEL | PATH | UUID | ... } ]\n"
132 "[ -f [ <filename> ] | -o [ <filename> ] | -[0-9]+ ]\n"
133 "[ -i <interval> ] [ -s [ <hh:mm[:ss]> ] ] [ -e [ <hh:mm[:ss]> ] ]\n"));
138 ***************************************************************************
139 * Display a short help message and exit.
142 * @progname Name of sysstat command
143 ***************************************************************************
145 void display_help(char *progname)
147 print_usage_title(stdout, progname);
148 printf(_("Main options and reports (report name between square brackets):\n"));
149 printf(_("\t-B\tPaging statistics [A_PAGE]\n"));
150 printf(_("\t-b\tI/O and transfer rate statistics [A_IO]\n"));
151 printf(_("\t-d\tBlock devices statistics [A_DISK]\n"));
152 printf(_("\t-F [ MOUNT ]\n"));
153 printf(_("\t\tFilesystems statistics [A_FS]\n"));
154 printf(_("\t-H\tHugepages utilization statistics [A_HUGE]\n"));
155 printf(_("\t-I { <int_list> | SUM | ALL }\n"
156 "\t\tInterrupts statistics [A_IRQ]\n"));
157 printf(_("\t-m { <keyword> [,...] | ALL }\n"
158 "\t\tPower management statistics [A_PWR_...]\n"
159 "\t\tKeywords are:\n"
160 "\t\tCPU\tCPU instantaneous clock frequency\n"
161 "\t\tFAN\tFans speed\n"
162 "\t\tFREQ\tCPU average clock frequency\n"
163 "\t\tIN\tVoltage inputs\n"
164 "\t\tTEMP\tDevices temperature\n"
165 "\t\tUSB\tUSB devices plugged into the system\n"));
166 printf(_("\t-n { <keyword> [,...] | ALL }\n"
167 "\t\tNetwork statistics [A_NET_...]\n"
168 "\t\tKeywords are:\n"
169 "\t\tDEV\tNetwork interfaces\n"
170 "\t\tEDEV\tNetwork interfaces (errors)\n"
171 "\t\tNFS\tNFS client\n"
172 "\t\tNFSD\tNFS server\n"
173 "\t\tSOCK\tSockets\t(v4)\n"
174 "\t\tIP\tIP traffic\t(v4)\n"
175 "\t\tEIP\tIP traffic\t(v4) (errors)\n"
176 "\t\tICMP\tICMP traffic\t(v4)\n"
177 "\t\tEICMP\tICMP traffic\t(v4) (errors)\n"
178 "\t\tTCP\tTCP traffic\t(v4)\n"
179 "\t\tETCP\tTCP traffic\t(v4) (errors)\n"
180 "\t\tUDP\tUDP traffic\t(v4)\n"
181 "\t\tSOCK6\tSockets\t(v6)\n"
182 "\t\tIP6\tIP traffic\t(v6)\n"
183 "\t\tEIP6\tIP traffic\t(v6) (errors)\n"
184 "\t\tICMP6\tICMP traffic\t(v6)\n"
185 "\t\tEICMP6\tICMP traffic\t(v6) (errors)\n"
186 "\t\tUDP6\tUDP traffic\t(v6)\n"
187 "\t\tFC\tFibre channel HBAs\n"
188 "\t\tSOFT\tSoftware-based network processing\n"));
189 printf(_("\t-q [ <keyword> [,...] | PSI | ALL ]\n"
190 "\t\tSystem load and pressure-stall statistics\n"
191 "\t\tKeywords are:\n"
192 "\t\tLOAD\tQueue length and load average statistics [A_QUEUE]\n"
193 "\t\tCPU\tPressure-stall CPU statistics [A_PSI_CPU]\n"
194 "\t\tIO\tPressure-stall I/O statistics [A_PSI_IO]\n"
195 "\t\tMEM\tPressure-stall memory statistics [A_PSI_MEM]\n"));
196 printf(_("\t-r [ ALL ]\n"
197 "\t\tMemory utilization statistics [A_MEMORY]\n"));
198 printf(_("\t-S\tSwap space utilization statistics [A_MEMORY]\n"));
199 printf(_("\t-u [ ALL ]\n"
200 "\t\tCPU utilization statistics [A_CPU]\n"));
201 printf(_("\t-v\tKernel tables statistics [A_KTABLES]\n"));
202 printf(_("\t-W\tSwapping statistics [A_SWAP]\n"));
203 printf(_("\t-w\tTask creation and system switching statistics [A_PCSW]\n"));
204 printf(_("\t-y\tTTY devices statistics [A_SERIAL]\n"));
209 ***************************************************************************
210 * Give a hint to the user about where is located the data collector.
211 ***************************************************************************
213 void which_sadc(void)
217 if (stat(SADC_PATH, &buf) < 0) {
218 printf(_("Data collector will be sought in PATH\n"));
221 printf(_("Data collector found: %s\n"), SADC_PATH);
227 ***************************************************************************
228 * SIGINT signal handler.
231 * @sig Signal number.
232 ***************************************************************************
234 void int_handler(int sig)
237 printf("\n"); /* Skip "^C" displayed on screen */
242 ***************************************************************************
243 * Init some structures.
244 ***************************************************************************
246 void init_structures(void)
250 for (i = 0; i < 3; i++)
251 memset(&record_hdr[i], 0, RECORD_HEADER_SIZE);
255 ***************************************************************************
256 * Allocate memory for sadc args.
259 * @i Argument number.
260 * @ltemp Argument value.
261 ***************************************************************************
263 void salloc(int i, char *ltemp)
265 if ((args[i] = (char *) malloc(strlen(ltemp) + 1)) == NULL) {
269 strcpy(args[i], ltemp);
273 ***************************************************************************
274 * Display an error message.
277 * @error_code Code of error message to display.
278 ***************************************************************************
280 void print_read_error(int error_code)
282 switch (error_code) {
284 case END_OF_DATA_UNEXPECTED:
285 /* Happens when the data collector doesn't send enough data */
286 fprintf(stderr, _("End of data collecting unexpected\n"));
290 /* Strange data sent by sadc...! */
291 fprintf(stderr, _("Inconsistent input data\n"));
298 ***************************************************************************
299 * Check that every selected activity actually belongs to the sequence list.
300 * If not, then the activity should be unselected since it will not be sent
301 * by sadc. An activity can be not sent if its number of items is zero.
304 * @act_nr Size of sequence list.
305 ***************************************************************************
307 void reverse_check_act(unsigned int act_nr)
311 for (i = 0; i < NR_ACT; i++) {
313 if (IS_SELECTED(act[i]->options)) {
315 for (j = 0; j < act_nr; j++) {
316 if (id_seq[j] == act[i]->id)
320 act[i]->options &= ~AO_SELECTED;
326 ***************************************************************************
327 * Determine if a stat header line has to be displayed.
330 * TRUE if a header line has to be displayed.
331 ***************************************************************************
333 int check_line_hdr(void)
337 /* Get number of options entered on the command line */
338 if (get_activity_nr(act, AO_SELECTED, COUNT_OUTPUTS) > 1)
341 for (i = 0; i < NR_ACT; i++) {
342 if (IS_SELECTED(act[i]->options)) {
343 /* Special processing for activities using a bitmap */
344 if (act[i]->bitmap) {
345 if (count_bits(act[i]->bitmap->b_array,
346 BITMAP_SIZE(act[i]->bitmap->b_size)) > 1) {
350 else if (act[i]->nr_ini > 1) {
353 /* Stop now since we have only one selected activity */
362 ***************************************************************************
363 * Print statistics average.
366 * @curr Index in array for current sample statistics.
367 * @read_from_file Set to TRUE if stats are read from a system activity
369 * @act_id Activity that can be displayed, or ~0 for all.
370 * Remember that when reading stats from a file, only
371 * one activity can be displayed at a time.
372 ***************************************************************************
374 void write_stats_avg(int curr, int read_from_file, unsigned int act_id)
377 unsigned long long itv;
379 /* Interval value in 1/100th of a second */
380 itv = get_interval(record_hdr[2].uptime_cs, record_hdr[curr].uptime_cs);
382 strncpy(timestamp[curr], _("Average:"), sizeof(timestamp[curr]));
383 timestamp[curr][sizeof(timestamp[curr]) - 1] = '\0';
384 memcpy(timestamp[!curr], timestamp[curr], sizeof(timestamp[!curr]));
387 TEST_STDOUT(STDOUT_FILENO);
389 for (i = 0; i < NR_ACT; i++) {
391 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
394 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
395 /* Display current average activity statistics */
396 (*act[i]->f_print_avg)(act[i], 2, curr, itv);
400 if (read_from_file) {
402 * Reset number of lines printed only if we read stats
403 * from a system activity file.
410 ***************************************************************************
411 * Print system statistics.
412 * This is called when we read stats either from a file or from sadc.
415 * @curr Index in array for current sample statistics.
416 * @read_from_file Set to TRUE if stats are read from a system activity
418 * @use_tm_start Set to TRUE if option -s has been used.
419 * @use_tm_end Set to TRUE if option -e has been used.
420 * @reset Set to TRUE if last_uptime variable should be
421 * reinitialized (used in next_slice() function).
422 * @act_id Activity that can be displayed or ~0 for all.
423 * Remember that when reading stats from a file, only
424 * one activity can be displayed at a time.
425 * @reset_cd TRUE if static cross_day variable should be reset
429 * @cnt Number of remaining lines to display.
432 * 1 if stats have been successfully displayed, and 0 otherwise.
433 ***************************************************************************
435 int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start,
436 int use_tm_end, int reset, unsigned int act_id, int reset_cd)
439 unsigned long long itv;
440 static int cross_day = FALSE;
444 * cross_day is a static variable that is set to 1 when the first
445 * record of stats from a new day is read from a unique data file
446 * (in the case where the file contains data from two consecutive
447 * days). When set to TRUE, every following records timestamp will
448 * have its hour value increased by 24.
449 * Yet when a new activity (being read from the file) is going to
450 * be displayed, we start reading the file from the beginning
451 * again, and so cross_day should be reset in this case.
457 if (read_from_file && !next_slice(record_hdr[2].uptime_cs, record_hdr[curr].uptime_cs,
459 /* Not close enough to desired interval */
462 /* Get then set previous timestamp */
463 if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[!curr],
466 prev_hour = rectime.tm_hour;
467 set_record_timestamp_string(flags, &record_hdr[!curr],
468 NULL, timestamp[!curr], TIMESTAMP_LEN, &rectime);
470 /* Get then set current timestamp */
471 if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[curr],
474 set_record_timestamp_string(flags, &record_hdr[curr],
475 NULL, timestamp[curr], TIMESTAMP_LEN, &rectime);
478 * Check if we are beginning a new day.
479 * Use rectime.tm_hour and prev_hour instead of record_hdr[].hour for comparison
480 * to take into account the current timezone (hours displayed will depend on the
481 * TZ variable value).
483 if (use_tm_start && record_hdr[!curr].ust_time &&
484 (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) &&
485 (rectime.tm_hour < prev_hour)) {
490 if (use_tm_start && (datecmp(&rectime, &tm_start, cross_day) < 0))
491 /* it's too soon... */
494 /* Get interval value in 1/100th of a second */
495 get_itv_value(&record_hdr[curr], &record_hdr[!curr], &itv);
498 if (use_tm_end && (datecmp(&rectime, &tm_end, cross_day) > 0)) {
499 /* It's too late... */
507 TEST_STDOUT(STDOUT_FILENO);
509 for (i = 0; i < NR_ACT; i++) {
511 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
514 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
515 /* Display current activity statistics */
516 (*act[i]->f_print)(act[i], !curr, curr, itv);
524 ***************************************************************************
525 * Display stats since system startup.
528 * @curr Index in array for current sample statistics.
529 ***************************************************************************
531 void write_stats_startup(int curr)
535 /* Set to 0 previous structures corresponding to boot time */
536 memset(&record_hdr[!curr], 0, RECORD_HEADER_SIZE);
537 record_hdr[!curr].record_type = R_STATS;
538 record_hdr[!curr].hour = record_hdr[curr].hour;
539 record_hdr[!curr].minute = record_hdr[curr].minute;
540 record_hdr[!curr].second = record_hdr[curr].second;
541 record_hdr[!curr].ust_time = record_hdr[curr].ust_time;
543 for (i = 0; i < NR_ACT; i++) {
544 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
546 * Using nr[curr] and not nr[!curr] below because we initialize
547 * reference structures for each structure that has been
548 * currently read in memory.
549 * No problem with buffers allocation since they all have the
552 memset(act[i]->buf[!curr], 0,
553 (size_t) act[i]->msize * (size_t) act[i]->nr[curr] * (size_t) act[i]->nr2);
557 flags |= S_F_SINCE_BOOT;
560 write_stats(curr, USE_SADC, &count, NO_TM_START, NO_TM_END, NO_RESET,
561 ALL_ACTIVITIES, TRUE);
567 ***************************************************************************
568 * Read data sent by the data collector.
571 * @size Number of bytes of data to read.
574 * @buffer Buffer where data will be saved.
577 * 0 if all the data have been successfully read.
578 * Otherwise, return the number of bytes left to be read.
579 ***************************************************************************
581 size_t sa_read(void *buffer, size_t size)
587 if ((n = read(STDIN_FILENO, buffer, size)) < 0) {
593 return size; /* EOF */
596 buffer = (char *) buffer + n;
603 ***************************************************************************
604 * Display a restart message (contents of a R_RESTART record).
607 * @tab Number of tabulations (unused here).
608 * @action Action expected from current function (unused here).
609 * @cur_date Date string of current restart message (unused here).
610 * @cur_time Time string of current restart message.
611 * @utc True if @cur_time is expressed in UTC (unused here).
612 * @file_hdr System activity file standard header.
613 * @record_hdr Current record header (unused here).
614 ***************************************************************************
616 __printf_funct_t print_sar_restart(int *tab, int action, char *cur_date, char *cur_time,
617 int utc, struct file_header *file_hdr,
618 struct record_header *record_hdr)
622 printf("\n%-11s", cur_time);
623 sprintf(restart, " LINUX RESTART\t(%d CPU)\n",
624 file_hdr->sa_cpu_nr > 1 ? file_hdr->sa_cpu_nr - 1 : 1);
625 cprintf_s(IS_RESTART, "%s", restart);
630 ***************************************************************************
631 * Display a comment (contents of R_COMMENT record).
634 * @tab Number of tabulations (unused here).
635 * @action Action expected from current function (unused here).
636 * @cur_date Date string of current comment (unused here).
637 * @cur_time Time string of current comment.
638 * @utc True if @cur_time is expressed in UTC (unused here).
639 * @comment Comment to display.
640 * @file_hdr System activity file standard header (unused here).
641 * @record_hdr Current record header (unused here).
642 ***************************************************************************
644 __print_funct_t print_sar_comment(int *tab, int action, char *cur_date, char *cur_time, int utc,
645 char *comment, struct file_header *file_hdr,
646 struct record_header *record_hdr)
648 printf("%-11s", cur_time);
649 cprintf_s(IS_COMMENT, " COM %s\n", comment);
653 ***************************************************************************
654 * Read the various statistics sent by the data collector (sadc).
657 * @curr Index in array for current sample statistics.
658 ***************************************************************************
660 void read_sadc_stat_bunch(int curr)
664 /* Read record header (type is always R_STATS since it is read from sadc) */
665 if (sa_read(&record_hdr[curr], RECORD_HEADER_SIZE)) {
667 * SIGINT (sent by sadc) is likely to be received
668 * while we are stuck in sa_read().
669 * If this happens then no data have to be read.
675 fprintf(stderr, "%s: Record header\n", __FUNCTION__);
677 print_read_error(END_OF_DATA_UNEXPECTED);
680 for (i = 0; i < NR_ACT; i++) {
684 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
686 if (HAS_COUNT_FUNCTION(act[p]->options)) {
687 if (sa_read(&(act[p]->nr[curr]), sizeof(__nr_t))) {
689 fprintf(stderr, "%s: Nb of items\n", __FUNCTION__);
691 print_read_error(END_OF_DATA_UNEXPECTED);
693 if ((act[p]->nr[curr] > act[p]->nr_max) || (act[p]->nr[curr] < 0)) {
695 fprintf(stderr, "%s: %s: nr=%d nr_max=%d\n",
696 __FUNCTION__, act[p]->name, act[p]->nr[curr], act[p]->nr_max);
698 print_read_error(INCONSISTENT_INPUT_DATA);
700 if (act[p]->nr[curr] > act[p]->nr_allocated) {
701 reallocate_all_buffers(act[p], act[p]->nr[curr]);
706 * For persistent activities, we must make sure that no statistics
707 * from a previous iteration remain, especially if the number
708 * of structures read is smaller than @nr_ini.
710 if (HAS_PERSISTENT_VALUES(act[p]->options)) {
711 memset(act[p]->buf[curr], 0,
712 (size_t) act[p]->fsize * (size_t) act[p]->nr_ini * (size_t) act[p]->nr2);
715 if (sa_read(act[p]->buf[curr],
716 (size_t) act[p]->fsize * (size_t) act[p]->nr[curr] * (size_t) act[p]->nr2)) {
718 fprintf(stderr, "%s: Statistics\n", __FUNCTION__);
720 print_read_error(END_OF_DATA_UNEXPECTED);
726 ***************************************************************************
727 * Read current activity's statistics (located between two consecutive
728 * LINUX RESTART messages) from file and display them.
731 * @ifd Input file descriptor.
732 * @fpos Position in file where reading must start.
733 * @curr Index in array for current sample statistics.
734 * @rows Number of rows of screen.
735 * @act_id Activity to display.
736 * @file_actlst List of activities in file.
737 * @file Name of file being read.
738 * @file_magic file_magic structure filled with file magic header data.
739 * @rec_hdr_tmp Temporary buffer where current record header will be saved.
741 * TRUE if file's data don't match current machine's endianness.
742 * @arch_64 TRUE if file's data come from a 64 bit machine.
743 * @b_size Size of @rec_hdr_tmp buffer.
746 * @curr Index in array for next sample statistics.
747 * @cnt Number of remaining lines of stats to write.
748 * @eosaf Set to TRUE if EOF (end of file) has been reached.
749 * @reset Set to TRUE if last_uptime variable should be reinitialized
750 * (used in next_slice() function).
751 ***************************************************************************
753 void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf,
754 int rows, unsigned int act_id, int *reset,
755 struct file_activity *file_actlst, char *file,
756 struct file_magic *file_magic, void *rec_hdr_tmp,
757 int endian_mismatch, int arch_64, size_t b_size)
760 unsigned long lines = 0;
762 int davg = 0, next, inc = 0;
764 if (lseek(ifd, fpos, SEEK_SET) < fpos) {
770 * Restore the first stats collected.
771 * Used to compute the rate displayed on the first line.
773 copy_structures(act, id_seq, record_hdr, !*curr, 2);
777 /* Assess number of lines printed when a bitmap is used */
778 p = get_activity_position(act, act_id, EXIT_IF_NOT_FOUND);
779 if (act[p]->bitmap) {
780 inc = count_bits(act[p]->bitmap->b_array,
781 BITMAP_SIZE(act[p]->bitmap->b_size));
787 * Display <count> lines of stats.
788 * Start with reading current sample's record header.
790 *eosaf = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[*curr],
791 &file_hdr, arch_64, endian_mismatch, UEOF_STOP, b_size, flags);
792 rtype = record_hdr[*curr].record_type;
794 if ((lines >= rows) || !lines) {
801 if (*eosaf || (rtype == R_RESTART))
802 /* This is EOF or we have met a LINUX RESTART record: Stop now */
805 if (rtype != R_COMMENT) {
806 /* Read the extra fields since it's not a special record */
807 read_file_stat_bunch(act, *curr, ifd, file_hdr.sa_act_nr, file_actlst,
808 endian_mismatch, arch_64, file, file_magic, UEOF_STOP);
811 /* Display comment */
812 next = print_special_record(&record_hdr[*curr], flags + S_F_LOCAL_TIME,
813 &tm_start, &tm_end, R_COMMENT, ifd,
815 file_magic, &file_hdr, act, &sar_fmt,
816 endian_mismatch, arch_64);
819 * A line of comment was actually displayed: Count it in the
820 * total number of displayed lines.
821 * If no lines of stats had been previously displayed, ignore it
822 * to make sure the header line will be displayed.
829 /* next is set to 1 when we were close enough to desired interval */
830 next = write_stats(*curr, USE_SA_FILE, cnt, tm_start.use, tm_end.use,
831 *reset, act_id, reset_cd);
833 if (next && (*cnt > 0)) {
845 lines += act[p]->nr[*curr];
853 * At this moment, if we had a R_RESTART record, we still haven't read
854 * the number of CPU following it (nor the possible extra structures).
855 * But in this case, we always have @cnt != 0.
859 write_stats_avg(!*curr, USE_SA_FILE, act_id);
866 ***************************************************************************
867 * Read header data sent by sadc.
868 ***************************************************************************
870 void read_header_data(void)
872 struct file_magic file_magic;
873 struct file_activity file_act;
877 /* Read magic header */
878 rc = sa_read(&file_magic, FILE_MAGIC_SIZE);
880 sprintf(version, "%d.%d.%d.%d",
881 file_magic.sysstat_version,
882 file_magic.sysstat_patchlevel,
883 file_magic.sysstat_sublevel,
884 file_magic.sysstat_extraversion);
885 if (!file_magic.sysstat_extraversion) {
886 version[strlen(version) - 2] = '\0';
889 if (rc || (file_magic.sysstat_magic != SYSSTAT_MAGIC) ||
890 (file_magic.format_magic != FORMAT_MAGIC) ||
891 strcmp(version, VERSION)) {
893 /* sar and sadc commands are not consistent */
894 if (!rc && (file_magic.sysstat_magic == SYSSTAT_MAGIC)) {
896 _("Using a wrong data collector from a different sysstat version\n"));
900 fprintf(stderr, "%s: sysstat_magic=%x format_magic=%x version=%s\n",
901 __FUNCTION__, file_magic.sysstat_magic, file_magic.format_magic, version);
903 if (rc == FILE_MAGIC_SIZE) {
905 * No data (0 byte) have been sent by sadc.
906 * This is probably because no activities have been collected
907 * ("Requested activities not available"). In this case, don't
908 * display an error message: Exit now.
912 print_read_error(INCONSISTENT_INPUT_DATA);
917 * No need to take into account file_magic.header_size. We are sure that
918 * sadc and sar are from the same version (we have checked FORMAT_MAGIC
919 * but also VERSION above) and thus the size of file_header is FILE_HEADER_SIZE.
921 if (sa_read(&file_hdr, FILE_HEADER_SIZE)) {
923 fprintf(stderr, "%s: File header\n", __FUNCTION__);
925 print_read_error(END_OF_DATA_UNEXPECTED);
928 /* All activities are not necessarily selected, but NR_ACT is a max */
929 if (file_hdr.sa_act_nr > NR_ACT) {
931 fprintf(stderr, "%s: sa_act_nr=%d\n", __FUNCTION__, file_hdr.sa_act_nr);
933 print_read_error(INCONSISTENT_INPUT_DATA);
936 if ((file_hdr.act_size != FILE_ACTIVITY_SIZE) ||
937 (file_hdr.rec_size != RECORD_HEADER_SIZE)) {
939 fprintf(stderr, "%s: act_size=%u/%zu rec_size=%u/%zu\n", __FUNCTION__,
940 file_hdr.act_size, FILE_ACTIVITY_SIZE, file_hdr.rec_size, RECORD_HEADER_SIZE);
942 print_read_error(INCONSISTENT_INPUT_DATA);
945 /* Read activity list */
946 for (i = 0; i < file_hdr.sa_act_nr; i++) {
948 if (sa_read(&file_act, FILE_ACTIVITY_SIZE)) {
950 fprintf(stderr, "%s: File activity (%d)\n", __FUNCTION__, i);
952 print_read_error(END_OF_DATA_UNEXPECTED);
955 p = get_activity_position(act, file_act.id, RESUME_IF_NOT_FOUND);
957 if ((p < 0) || (act[p]->fsize != file_act.size)
958 || (act[p]->gtypes_nr[0] != file_act.types_nr[0])
959 || (act[p]->gtypes_nr[1] != file_act.types_nr[1])
960 || (act[p]->gtypes_nr[2] != file_act.types_nr[2])
961 || (file_act.nr <= 0)
962 || (file_act.nr2 <= 0)
963 || (act[p]->magic != file_act.magic)) {
966 fprintf(stderr, "%s: p=%d\n", __FUNCTION__, p);
969 fprintf(stderr, "%s: %s: size=%d/%d magic=%x/%x nr=%d nr2=%d types=%d,%d,%d/%d,%d,%d\n",
970 __FUNCTION__, act[p]->name, act[p]->fsize, file_act.size,
971 act[p]->magic, file_act.magic, file_act.nr, file_act.nr2,
972 act[p]->gtypes_nr[0], act[p]->gtypes_nr[1], act[p]->gtypes_nr[2],
973 file_act.types_nr[0], file_act.types_nr[1], file_act.types_nr[2]);
976 /* Remember that we are reading data from sadc and not from a file... */
977 print_read_error(INCONSISTENT_INPUT_DATA);
980 id_seq[i] = file_act.id; /* We necessarily have "i < NR_ACT" */
981 act[p]->nr_ini = file_act.nr;
982 act[p]->nr2 = file_act.nr2;
989 /* Check that all selected activties are actually sent by sadc */
990 reverse_check_act(file_hdr.sa_act_nr);
996 ***************************************************************************
997 * Read statistics from a system activity data file.
1000 * @from_file Input file name.
1001 ***************************************************************************
1003 void read_stats_from_file(char from_file[])
1005 struct file_magic file_magic;
1006 struct file_activity *file_actlst = NULL;
1007 char rec_hdr_tmp[MAX_RECORD_HEADER_SIZE];
1010 int rows, eosaf = TRUE, reset = FALSE;
1014 /* Get window size */
1015 rows = get_win_height();
1017 /* Read file headers and activity list */
1018 check_file_actlst(&ifd, from_file, act, flags, &file_magic, &file_hdr,
1019 &file_actlst, id_seq, &endian_mismatch, &arch_64);
1021 /* Perform required allocations */
1022 allocate_structures(act);
1024 /* Print report header */
1025 print_report_hdr(flags, &rectime, &file_hdr);
1027 /* Read system statistics from file */
1030 * If this record is a special (RESTART or COMMENT) one, print it and
1031 * (try to) get another one.
1034 if (read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[0], &file_hdr,
1035 arch_64, endian_mismatch, UEOF_STOP, sizeof(rec_hdr_tmp), flags)) {
1036 /* End of sa data file */
1040 rtype = record_hdr[0].record_type;
1041 if ((rtype == R_RESTART) || (rtype == R_COMMENT)) {
1042 print_special_record(&record_hdr[0], flags + S_F_LOCAL_TIME,
1043 &tm_start, &tm_end, rtype, ifd,
1044 &rectime, from_file, 0, &file_magic,
1045 &file_hdr, act, &sar_fmt, endian_mismatch, arch_64);
1049 * OK: Previous record was not a special one.
1050 * So read now the extra fields.
1052 read_file_stat_bunch(act, 0, ifd, file_hdr.sa_act_nr,
1053 file_actlst, endian_mismatch, arch_64,
1054 from_file, &file_magic, UEOF_STOP);
1055 if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME,
1056 &record_hdr[0], &rectime))
1058 * An error was detected.
1059 * The timestamp hasn't been updated.
1064 while ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
1065 (tm_start.use && (datecmp(&rectime, &tm_start, FALSE) < 0)) ||
1066 (tm_end.use && (datecmp(&rectime, &tm_end, FALSE) >= 0)));
1068 /* Save the first stats collected. Will be used to compute the average */
1069 copy_structures(act, id_seq, record_hdr, 2, 0);
1071 reset = TRUE; /* Set flag to reset last_uptime variable */
1073 /* Save current file position */
1074 if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) {
1080 * Read and write stats located between two possible Linux restarts.
1081 * Activities that should be displayed are saved in id_seq[] array.
1082 * Since we are reading from a file, we print all the stats for an
1083 * activity before displaying the next activity.
1084 * id_seq[] has been created in check_file_actlst(), retaining only
1085 * activities known by current sysstat version.
1087 for (i = 0; i < NR_ACT; i++) {
1092 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
1093 if (!IS_SELECTED(act[p]->options))
1096 if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) {
1097 handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, rows,
1098 act[p]->id, &reset, file_actlst,
1099 from_file, &file_magic, rec_hdr_tmp,
1100 endian_mismatch, arch_64, sizeof(rec_hdr_tmp));
1103 unsigned int optf, msk;
1105 optf = act[p]->opt_flags;
1107 for (msk = 1; msk < 0x100; msk <<= 1) {
1108 if ((act[p]->opt_flags & 0xff) & msk) {
1109 act[p]->opt_flags &= (0xffffff00 + msk);
1111 handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf,
1112 rows, act[p]->id, &reset, file_actlst,
1113 from_file, &file_magic, rec_hdr_tmp,
1114 endian_mismatch, arch_64, sizeof(rec_hdr_tmp));
1115 act[p]->opt_flags = optf;
1122 * Go to next Linux restart, if possible.
1123 * Note: If we have @cnt == 0 then the last record we read was not a R_RESTART one
1124 * (else we would have had @cnt != 0, i.e. we would have stopped reading previous activity
1125 * because such a R_RESTART record would have been read, not because all the <count> lines
1126 * had been printed).
1127 * Remember @cnt is decremented only when a real line of stats have been displayed
1128 * (not when a special record has been read).
1131 /* Read next record header */
1132 eosaf = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[curr],
1133 &file_hdr, arch_64, endian_mismatch, UEOF_STOP, sizeof(rec_hdr_tmp), flags);
1134 rtype = record_hdr[curr].record_type;
1136 if (eosaf || (rtype == R_RESTART))
1139 if (rtype != R_COMMENT) {
1140 read_file_stat_bunch(act, curr, ifd, file_hdr.sa_act_nr,
1141 file_actlst, endian_mismatch, arch_64,
1142 from_file, &file_magic, UEOF_STOP);
1145 /* This was a COMMENT record: print it */
1146 print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
1147 &tm_start, &tm_end, R_COMMENT, ifd,
1148 &rectime, from_file, 0,
1149 &file_magic, &file_hdr, act, &sar_fmt,
1150 endian_mismatch, arch_64);
1156 /* The last record we read was a RESTART one: Print it */
1157 if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) {
1158 print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
1159 &tm_start, &tm_end, R_RESTART, ifd,
1160 &rectime, from_file, 0,
1161 &file_magic, &file_hdr, act, &sar_fmt,
1162 endian_mismatch, arch_64);
1173 ***************************************************************************
1174 * Read statistics sent by sadc, the data collector.
1175 ***************************************************************************
1177 void read_stats(void)
1180 unsigned long lines;
1184 /* Don't buffer data if redirected to a pipe... */
1185 setbuf(stdout, NULL);
1187 /* Read stats header */
1190 if (!get_activity_nr(act, AO_SELECTED, COUNT_ACTIVITIES)) {
1191 /* Requested activities not available: Exit */
1192 print_collect_error();
1195 /* Determine if a stat line header has to be displayed */
1196 dis_hdr = check_line_hdr();
1198 lines = rows = get_win_height();
1200 /* Perform required allocations */
1201 allocate_structures(act);
1203 /* Print report header */
1204 print_report_hdr(flags, &rectime, &file_hdr);
1206 /* Read system statistics sent by the data collector */
1207 read_sadc_stat_bunch(0);
1210 /* Display stats since boot time and exit */
1211 write_stats_startup(0);
1214 /* Save the first stats collected. Will be used to compute the average */
1215 copy_structures(act, id_seq, record_hdr, 2, 0);
1217 /* Set a handler for SIGINT */
1218 memset(&int_act, 0, sizeof(int_act));
1219 int_act.sa_handler = int_handler;
1220 int_act.sa_flags = SA_RESTART;
1221 sigaction(SIGINT, &int_act, NULL);
1227 read_sadc_stat_bunch(curr);
1228 if (sigint_caught) {
1230 * SIGINT signal caught (it is sent by sadc).
1231 * => Display average stats.
1233 curr ^= 1; /* No data retrieved from last read */
1239 dish = lines / rows;
1245 write_stats(curr, USE_SADC, &count, NO_TM_START, tm_end.use,
1246 NO_RESET, ALL_ACTIVITIES, TRUE);
1248 if (record_hdr[curr].record_type == R_LAST_STATS) {
1249 /* File rotation is happening: Re-read header data sent by sadc */
1251 allocate_structures(act);
1264 * Print statistics average.
1265 * At least one line of stats must have been displayed for this.
1266 * (There may be no lines at all if we press Ctrl/C immediately).
1270 write_stats_avg(curr, USE_SADC, ALL_ACTIVITIES);
1275 ***************************************************************************
1276 * Main entry to the sar program.
1277 ***************************************************************************
1279 int main(int argc, char **argv)
1281 int i, rc, opt = 1, args_idx = 1, p, q;
1284 char from_file[MAX_FILE_LEN], to_file[MAX_FILE_LEN];
1287 /* Compute page shift in kB */
1290 from_file[0] = to_file[0] = '\0';
1293 /* Init National Language Support */
1297 tm_start.use = tm_end.use = FALSE;
1299 /* Allocate and init activity bitmaps */
1300 allocate_bitmaps(act);
1304 /* Process options */
1305 while (opt < argc) {
1307 if (!strcmp(argv[opt], "--sadc")) {
1312 else if (!strncmp(argv[opt], "--dev=", 6)) {
1313 /* Parse devices entered on the command line */
1314 p = get_activity_position(act, A_DISK, EXIT_IF_NOT_FOUND);
1315 parse_sa_devices(argv[opt], act[p], MAX_DEV_LEN, &opt, 6);
1318 else if (!strncmp(argv[opt], "--fs=", 5)) {
1319 /* Parse devices entered on the command line */
1320 p = get_activity_position(act, A_FS, EXIT_IF_NOT_FOUND);
1321 parse_sa_devices(argv[opt], act[p], MAX_FS_LEN, &opt, 5);
1324 else if (!strncmp(argv[opt], "--iface=", 8)) {
1325 /* Parse devices entered on the command line */
1326 p = get_activity_position(act, A_NET_DEV, EXIT_IF_NOT_FOUND);
1327 parse_sa_devices(argv[opt], act[p], MAX_IFACE_LEN, &opt, 8);
1328 q = get_activity_position(act, A_NET_EDEV, EXIT_IF_NOT_FOUND);
1329 act[q]->item_list = act[p]->item_list;
1330 act[q]->item_list_sz = act[p]->item_list_sz;
1331 act[q]->options |= AO_LIST_ON_CMDLINE;
1334 else if (!strcmp(argv[opt], "--help")) {
1335 /* Display help message */
1336 display_help(argv[0]);
1339 else if (!strcmp(argv[opt], "--human")) {
1340 /* Display sizes in a human readable format */
1345 else if (!strcmp(argv[opt], "--pretty")) {
1346 /* Display an easy-to-read report */
1347 flags |= S_F_PRETTY;
1351 else if (!strncmp(argv[opt], "--dec=", 6) && (strlen(argv[opt]) == 7)) {
1352 /* Get number of decimal places */
1353 dplaces_nr = atoi(argv[opt] + 6);
1354 if ((dplaces_nr < 0) || (dplaces_nr > 2)) {
1360 else if (!strcmp(argv[opt], "-I")) {
1361 /* Parse -I option */
1362 if (parse_sar_I_opt(argv, &opt, &flags, act)) {
1367 else if (!strcmp(argv[opt], "-D")) {
1368 /* Option to tell sar to write to saYYYYMMDD data files */
1369 flags |= S_F_SA_YYYYMMDD;
1373 else if (!strcmp(argv[opt], "-P")) {
1374 /* Parse -P option */
1375 if (parse_sa_P_opt(argv, &opt, &flags, act)) {
1380 else if (!strcmp(argv[opt], "-o")) {
1382 /* Output file already specified */
1385 /* Save stats to a file */
1386 if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1387 (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1388 strncpy(to_file, argv[opt++], sizeof(to_file));
1389 to_file[sizeof(to_file) - 1] = '\0';
1392 strcpy(to_file, "-");
1396 else if (!strcmp(argv[opt], "-f")) {
1397 if (from_file[0] || day_offset) {
1398 /* Input file already specified */
1401 /* Read stats from a file */
1402 if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1403 (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1404 strncpy(from_file, argv[opt++], sizeof(from_file));
1405 from_file[sizeof(from_file) - 1] = '\0';
1406 /* Check if this is an alternate directory for sa files */
1407 check_alt_sa_dir(from_file, day_offset, -1);
1410 set_default_file(from_file, day_offset, -1);
1414 else if (!strcmp(argv[opt], "-s")) {
1415 /* Get time start */
1416 if (parse_timestamp(argv, &opt, &tm_start, DEF_TMSTART)) {
1421 else if (!strcmp(argv[opt], "-e")) {
1423 if (parse_timestamp(argv, &opt, &tm_end, DEF_TMEND)) {
1428 else if (!strcmp(argv[opt], "-i")) {
1429 if (!argv[++opt] || (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1432 interval = atol(argv[opt++]);
1436 flags |= S_F_INTERVAL_SET;
1439 else if (!strcmp(argv[opt], "-m")) {
1443 /* Parse option -m */
1444 if (parse_sar_m_opt(argv, &opt, act)) {
1449 else if (!strcmp(argv[opt], "-n")) {
1453 /* Parse option -n */
1454 if (parse_sar_n_opt(argv, &opt, act)) {
1459 else if (!strcmp(argv[opt], "-q")) {
1461 SELECT_ACTIVITY(A_QUEUE);
1463 /* Parse option -q */
1464 else if (parse_sar_q_opt(argv, &opt, act)) {
1465 SELECT_ACTIVITY(A_QUEUE);
1470 else if (!strncmp(argv[opt], "--getenv", 8)) {
1475 else if (!strncmp(argv[opt], "--unix_time=", 12)) {
1476 if (strspn(argv[opt] + 12, DIGITS) != strlen(argv[opt] + 12)) {
1479 __unix_time = atoll(argv[opt++] + 12);
1482 else if ((strlen(argv[opt]) > 1) &&
1483 (strlen(argv[opt]) < 4) &&
1484 !strncmp(argv[opt], "-", 1) &&
1485 (strspn(argv[opt] + 1, DIGITS) == (strlen(argv[opt]) - 1))) {
1486 if (from_file[0] || day_offset) {
1487 /* Input file already specified */
1490 day_offset = atoi(argv[opt++] + 1);
1493 else if (!strncmp(argv[opt], "-", 1)) {
1494 /* Other options not previously tested */
1495 if ((rc = parse_sar_opt(argv, &opt, act, &flags, C_SAR)) != 0) {
1504 else if (interval < 0) {
1506 if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1509 interval = atol(argv[opt++]);
1516 /* Get count value */
1517 if ((strspn(argv[opt], DIGITS) != strlen(argv[opt])) ||
1522 /* Count parameter already set */
1525 count = atol(argv[opt++]);
1532 /* Init color strings */
1535 /* 'sar' is equivalent to 'sar -f' */
1537 (((interval < 0) || INTERVAL_SET(flags)) && !from_file[0] && !to_file[0])) {
1538 set_default_file(from_file, day_offset, -1);
1541 if (tm_start.use && tm_end.use && (tm_end.tm_hour < tm_start.tm_hour)) {
1542 tm_end.tm_hour += 24;
1546 * Check option dependencies.
1548 /* You read from a file OR you write to it... */
1549 if (from_file[0] && to_file[0]) {
1550 fprintf(stderr, _("-f and -o options are mutually exclusive\n"));
1553 if (USE_OPTION_A(flags)) {
1554 /* Set -P ALL -I ALL if needed */
1555 set_bitmaps(act, &flags);
1557 /* Use time start or option -i only when reading stats from a file */
1558 if ((tm_start.use || INTERVAL_SET(flags)) && !from_file[0]) {
1560 _("Not reading from a system activity file (use -f option)\n"));
1563 /* Don't print stats since boot time if -o or -f options are used */
1564 if (!interval && (from_file[0] || to_file[0])) {
1568 /* Cannot enter a day shift with -o option */
1569 if (to_file[0] && day_offset) {
1575 * count parameter not set: Display all the contents of the file
1576 * or generate a report continuously.
1581 /* Default is CPU activity... */
1582 select_default_activity(act);
1584 /* Check S_TIME_FORMAT variable contents */
1585 if (!is_iso_time_fmt())
1586 flags |= S_F_PREFD_TIME_OUTPUT;
1588 /* Reading stats from file: */
1594 /* Read stats from file */
1595 read_stats_from_file(from_file);
1597 /* Free structures and activity bitmaps */
1599 free_structures(act);
1604 /* Reading stats from sadc: */
1606 /* Create anonymous pipe */
1607 if (pipe(fd) == -1) {
1620 if (dup2(fd[1], STDOUT_FILENO) < 0) {
1627 * Prepare options for sadc.
1632 /* Interval value */
1636 else if (!interval) {
1639 * Display stats since system startup: Set <interval> to 1.
1640 * <count> arg will also be set to 1 below.
1642 salloc(args_idx++, ltemp);
1645 sprintf(ltemp, "%ld", interval);
1647 salloc(args_idx++, ltemp);
1651 sprintf(ltemp, "%ld", count + 1);
1652 salloc(args_idx++, ltemp);
1657 sprintf(ltemp, "--unix_time=%ld", __unix_time);
1658 salloc(args_idx++, ltemp);
1661 /* Flags to be passed to sadc */
1662 salloc(args_idx++, "-Z");
1664 /* Writing data to a file (option -o) */
1666 /* Set option -D if entered */
1667 if (USE_SA_YYYYMMDD(flags)) {
1668 salloc(args_idx++, "-D");
1670 /* Collect all possible activities (option -S XALL for sadc) */
1671 salloc(args_idx++, "-S");
1672 salloc(args_idx++, K_XALL);
1674 salloc(args_idx++, to_file);
1678 * If option -o hasn't been used, then tell sadc
1679 * to collect only activities that will be displayed.
1681 salloc(args_idx++, "-S");
1682 strcpy(ltemp, K_A_NULL);
1683 for (i = 0; i < NR_ACT; i++) {
1684 if (IS_SELECTED(act[i]->options)) {
1686 strcat(ltemp, act[i]->name);
1689 salloc(args_idx++, ltemp);
1692 /* Last arg is NULL */
1693 args[args_idx] = NULL;
1695 /* Call now the data collector */
1697 fprintf(stderr, "%s: 1.sadc: %s\n", __FUNCTION__, SADC_PATH);
1700 execv(SADC_PATH, args);
1702 fprintf(stderr, "%s: 2.sadc: %s\n", __FUNCTION__, SADC);
1706 * Note: Don't use execl/execlp since we don't have a fixed number of
1707 * args to give to sadc.
1709 fprintf(stderr, _("Cannot find the data collector (%s)\n"), SADC);
1714 default: /* Parent */
1715 if (dup2(fd[0], STDIN_FILENO) < 0) {
1721 /* Get now the statistics */
1727 /* Free structures and activity bitmaps */
1729 free_structures(act);