2 * sar: report system activity
3 * (C) 1999-2018 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 ***************************************************************************
37 #define _(string) gettext(string)
39 #define _(string) (string)
43 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
44 char *sccsid(void) { return (SCCSID); }
47 /* Interval and count parameters */
48 long interval = -1, count = 0;
50 /* TRUE if a header line must be printed */
52 /* TRUE if data read from file don't match current machine's endianness */
53 int endian_mismatch = FALSE;
54 /* TRUE if file's data come from a 64 bit machine */
56 /* Number of decimal places */
59 unsigned int flags = 0;
60 unsigned int dm_major; /* Device-mapper major number */
62 char timestamp[2][TIMESTAMP_LEN];
63 extern unsigned int rec_types_nr[];
65 unsigned long avg_count = 0;
68 struct file_header file_hdr;
70 /* Current record header */
71 struct record_header record_hdr[3];
75 * This array must always be entirely filled (even with trailing zeros).
77 unsigned int id_seq[NR_ACT];
79 /* Devices entered on the command line */
80 struct sa_dlist *st_dev_list = NULL;
85 /* Contain the date specified by -s and -e options */
86 struct tstamp tm_start, tm_end;
88 char *args[MAX_ARGV_NR];
90 extern struct activity *act[];
91 extern struct report_format sar_fmt;
93 struct sigaction int_act;
94 int sigint_caught = 0;
97 ***************************************************************************
98 * Print usage title message.
101 * @progname Name of sysstat command
102 ***************************************************************************
104 void print_usage_title(FILE *fp, char *progname)
106 fprintf(fp, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
111 ***************************************************************************
112 * Print usage and exit.
115 * @progname Name of sysstat command
116 ***************************************************************************
118 void usage(char *progname)
120 print_usage_title(stderr, progname);
121 fprintf(stderr, _("Options are:\n"
122 "[ -A ] [ -B ] [ -b ] [ -C ] [ -D ] [ -d ] [ -F [ MOUNT ] ] [ -H ] [ -h ]\n"
123 "[ -p ] [ -q ] [ -r [ ALL ] ] [ -S ] [ -t ] [ -u [ ALL ] ] [ -V ]\n"
124 "[ -v ] [ -W ] [ -w ] [ -y ] [ -z ]\n"
125 "[ -I { <int_list> | SUM | ALL } ] [ -P { <cpu_list> | ALL } ]\n"
126 "[ -m { <keyword> [,...] | ALL } ] [ -n { <keyword> [,...] | ALL } ]\n"
127 "[ --dec={ 0 | 1 | 2 } ] [ --iface=<iface_list> ]\n"
128 "[ --help ] [ --human ] [ --sadc ]\n"
129 "[ -j { ID | LABEL | PATH | UUID | ... } ]\n"
130 "[ -f [ <filename> ] | -o [ <filename> ] | -[0-9]+ ]\n"
131 "[ -i <interval> ] [ -s [ <hh:mm[:ss]> ] ] [ -e [ <hh:mm[:ss]> ] ]\n"));
136 ***************************************************************************
137 * Display a short help message and exit.
140 * @progname Name of sysstat command
141 ***************************************************************************
143 void display_help(char *progname)
145 print_usage_title(stdout, progname);
146 printf(_("Main options and reports (report name between square brackets):\n"));
147 printf(_("\t-B\tPaging statistics [A_PAGE]\n"));
148 printf(_("\t-b\tI/O and transfer rate statistics [A_IO]\n"));
149 printf(_("\t-d\tBlock devices statistics [A_DISK]\n"));
150 printf(_("\t-F [ MOUNT ]\n"));
151 printf(_("\t\tFilesystems statistics [A_FS]\n"));
152 printf(_("\t-H\tHugepages utilization statistics [A_HUGE]\n"));
153 printf(_("\t-I { <int_list> | SUM | ALL }\n"
154 "\t\tInterrupts statistics [A_IRQ]\n"));
155 printf(_("\t-m { <keyword> [,...] | ALL }\n"
156 "\t\tPower management statistics [A_PWR_...]\n"
157 "\t\tKeywords are:\n"
158 "\t\tCPU\tCPU instantaneous clock frequency\n"
159 "\t\tFAN\tFans speed\n"
160 "\t\tFREQ\tCPU average clock frequency\n"
161 "\t\tIN\tVoltage inputs\n"
162 "\t\tTEMP\tDevices temperature\n"
163 "\t\tUSB\tUSB devices plugged into the system\n"));
164 printf(_("\t-n { <keyword> [,...] | ALL }\n"
165 "\t\tNetwork statistics [A_NET_...]\n"
166 "\t\tKeywords are:\n"
167 "\t\tDEV\tNetwork interfaces\n"
168 "\t\tEDEV\tNetwork interfaces (errors)\n"
169 "\t\tNFS\tNFS client\n"
170 "\t\tNFSD\tNFS server\n"
171 "\t\tSOCK\tSockets\t(v4)\n"
172 "\t\tIP\tIP traffic\t(v4)\n"
173 "\t\tEIP\tIP traffic\t(v4) (errors)\n"
174 "\t\tICMP\tICMP traffic\t(v4)\n"
175 "\t\tEICMP\tICMP traffic\t(v4) (errors)\n"
176 "\t\tTCP\tTCP traffic\t(v4)\n"
177 "\t\tETCP\tTCP traffic\t(v4) (errors)\n"
178 "\t\tUDP\tUDP traffic\t(v4)\n"
179 "\t\tSOCK6\tSockets\t(v6)\n"
180 "\t\tIP6\tIP traffic\t(v6)\n"
181 "\t\tEIP6\tIP traffic\t(v6) (errors)\n"
182 "\t\tICMP6\tICMP traffic\t(v6)\n"
183 "\t\tEICMP6\tICMP traffic\t(v6) (errors)\n"
184 "\t\tUDP6\tUDP traffic\t(v6)\n"
185 "\t\tFC\tFibre channel HBAs\n"
186 "\t\tSOFT\tSoftware-based network processing\n"));
187 printf(_("\t-q\tQueue length and load average statistics [A_QUEUE]\n"));
188 printf(_("\t-r [ ALL ]\n"
189 "\t\tMemory utilization statistics [A_MEMORY]\n"));
190 printf(_("\t-S\tSwap space utilization statistics [A_MEMORY]\n"));
191 printf(_("\t-u [ ALL ]\n"
192 "\t\tCPU utilization statistics [A_CPU]\n"));
193 printf(_("\t-v\tKernel tables statistics [A_KTABLES]\n"));
194 printf(_("\t-W\tSwapping statistics [A_SWAP]\n"));
195 printf(_("\t-w\tTask creation and system switching statistics [A_PCSW]\n"));
196 printf(_("\t-y\tTTY devices statistics [A_SERIAL]\n"));
201 ***************************************************************************
202 * Give a hint to the user about where is located the data collector.
203 ***************************************************************************
205 void which_sadc(void)
209 if (stat(SADC_PATH, &buf) < 0) {
210 printf(_("Data collector will be sought in PATH\n"));
213 printf(_("Data collector found: %s\n"), SADC_PATH);
219 ***************************************************************************
220 * SIGINT signal handler.
223 * @sig Signal number.
224 ***************************************************************************
226 void int_handler(int sig)
229 printf("\n"); /* Skip "^C" displayed on screen */
234 ***************************************************************************
235 * Init some structures.
236 ***************************************************************************
238 void init_structures(void)
242 for (i = 0; i < 3; i++)
243 memset(&record_hdr[i], 0, RECORD_HEADER_SIZE);
247 ***************************************************************************
248 * Allocate memory for sadc args.
251 * @i Argument number.
252 * @ltemp Argument value.
253 ***************************************************************************
255 void salloc(int i, char *ltemp)
257 if ((args[i] = (char *) malloc(strlen(ltemp) + 1)) == NULL) {
261 strcpy(args[i], ltemp);
265 ***************************************************************************
266 * Display an error message.
269 * @error_code Code of error message to display.
270 ***************************************************************************
272 void print_read_error(int error_code)
274 switch (error_code) {
276 case END_OF_DATA_UNEXPECTED:
277 /* Happens when the data collector doesn't send enough data */
278 fprintf(stderr, _("End of data collecting unexpected\n"));
282 /* Strange data sent by sadc...! */
283 fprintf(stderr, _("Inconsistent input data\n"));
290 ***************************************************************************
291 * Check that every selected activity actually belongs to the sequence list.
292 * If not, then the activity should be unselected since it will not be sent
293 * by sadc. An activity can be not sent if its number of items is zero.
296 * @act_nr Size of sequence list.
297 ***************************************************************************
299 void reverse_check_act(unsigned int act_nr)
303 for (i = 0; i < NR_ACT; i++) {
305 if (IS_SELECTED(act[i]->options)) {
307 for (j = 0; j < act_nr; j++) {
308 if (id_seq[j] == act[i]->id)
312 act[i]->options &= ~AO_SELECTED;
318 ***************************************************************************
319 * Determine if a stat header line has to be displayed.
322 * TRUE if a header line has to be displayed.
323 ***************************************************************************
325 int check_line_hdr(void)
329 /* Get number of options entered on the command line */
330 if (get_activity_nr(act, AO_SELECTED, COUNT_OUTPUTS) > 1)
333 for (i = 0; i < NR_ACT; i++) {
334 if (IS_SELECTED(act[i]->options)) {
335 /* Special processing for activities using a bitmap */
336 if (act[i]->bitmap) {
337 if (count_bits(act[i]->bitmap->b_array,
338 BITMAP_SIZE(act[i]->bitmap->b_size)) > 1) {
342 else if (act[i]->nr_ini > 1) {
345 /* Stop now since we have only one selected activity */
354 ***************************************************************************
355 * Print statistics average.
358 * @curr Index in array for current sample statistics.
359 * @read_from_file Set to TRUE if stats are read from a system activity
361 * @act_id Activity that can be displayed, or ~0 for all.
362 * Remember that when reading stats from a file, only
363 * one activity can be displayed at a time.
364 ***************************************************************************
366 void write_stats_avg(int curr, int read_from_file, unsigned int act_id)
369 unsigned long long itv;
371 /* Interval value in 1/100th of a second */
372 itv = get_interval(record_hdr[2].uptime_cs, record_hdr[curr].uptime_cs);
374 strncpy(timestamp[curr], _("Average:"), TIMESTAMP_LEN);
375 timestamp[curr][TIMESTAMP_LEN - 1] = '\0';
376 strcpy(timestamp[!curr], timestamp[curr]);
379 TEST_STDOUT(STDOUT_FILENO);
381 for (i = 0; i < NR_ACT; i++) {
383 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
386 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
387 /* Display current average activity statistics */
388 (*act[i]->f_print_avg)(act[i], 2, curr, itv);
392 if (read_from_file) {
394 * Reset number of lines printed only if we read stats
395 * from a system activity file.
402 ***************************************************************************
403 * Print system statistics.
404 * This is called when we read stats either from a file or from sadc.
407 * @curr Index in array for current sample statistics.
408 * @read_from_file Set to TRUE if stats are read from a system activity
410 * @use_tm_start Set to TRUE if option -s has been used.
411 * @use_tm_end Set to TRUE if option -e has been used.
412 * @reset Set to TRUE if last_uptime variable should be
413 * reinitialized (used in next_slice() function).
414 * @act_id Activity that can be displayed or ~0 for all.
415 * Remember that when reading stats from a file, only
416 * one activity can be displayed at a time.
417 * @reset_cd TRUE if static cross_day variable should be reset
421 * @cnt Number of remaining lines to display.
424 * 1 if stats have been successfully displayed, and 0 otherwise.
425 ***************************************************************************
427 int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start,
428 int use_tm_end, int reset, unsigned int act_id, int reset_cd)
431 unsigned long long itv;
432 static int cross_day = 0;
436 * cross_day is a static variable that is set to 1 when the first
437 * record of stats from a new day is read from a unique data file
438 * (in the case where the file contains data from two consecutive
439 * days). When set to 1, every following records timestamp will
440 * have its hour value increased by 24.
441 * Yet when a new activity (being read from the file) is going to
442 * be displayed, we start reading the file from the beginning
443 * again, and so cross_day should be reset in this case.
449 if (read_from_file) {
450 if (!next_slice(record_hdr[2].uptime_cs, record_hdr[curr].uptime_cs,
452 /* Not close enough to desired interval */
456 if (!is_iso_time_fmt())
457 flags |= S_F_PREFD_TIME_OUTPUT;
459 /* Get then set previous timestamp */
460 if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[!curr],
463 set_record_timestamp_string(flags, &record_hdr[!curr],
464 NULL, timestamp[!curr], TIMESTAMP_LEN, &rectime);
466 /* Get then set current timestamp */
467 if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[curr],
470 set_record_timestamp_string(flags, &record_hdr[curr],
471 NULL, timestamp[curr], TIMESTAMP_LEN, &rectime);
473 /* Check if we are beginning a new day */
474 if (use_tm_start && record_hdr[!curr].ust_time &&
475 (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) &&
476 (record_hdr[curr].hour < record_hdr[!curr].hour)) {
482 * This is necessary if we want to properly handle something like:
483 * sar -s time_start -e time_end with
484 * time_start(day D) > time_end(day D+1)
486 rectime.tm_hour += 24;
490 if (use_tm_start && (datecmp(&rectime, &tm_start) < 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) > 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 * 1 if end of file has been reached, 0 otherwise.
578 ***************************************************************************
580 int sa_read(void *buffer, size_t size)
586 if ((n = read(STDIN_FILENO, buffer, size)) < 0) {
595 buffer = (char *) buffer + n;
602 ***************************************************************************
603 * Display a restart message (contents of a R_RESTART record).
606 * @tab Number of tabulations (unused here).
607 * @action Action expected from current function (unused here).
608 * @cur_date Date string of current restart message (unused here).
609 * @cur_time Time string of current restart message.
610 * @utc True if @cur_time is expressed in UTC (unused here).
611 * @file_hdr System activity file standard header (unused here).
612 ***************************************************************************
614 __printf_funct_t print_sar_restart(int *tab, int action, char *cur_date, char *cur_time,
615 int utc, struct file_header *file_hdr)
619 printf("\n%-11s", cur_time);
620 sprintf(restart, " LINUX RESTART\t(%d CPU)\n",
621 file_hdr->sa_cpu_nr > 1 ? file_hdr->sa_cpu_nr - 1 : 1);
622 cprintf_s(IS_RESTART, "%s", restart);
627 ***************************************************************************
628 * Display a comment (contents of R_COMMENT record).
631 * @tab Number of tabulations (unused here).
632 * @action Action expected from current function (unused here).
633 * @cur_date Date string of current comment (unused here).
634 * @cur_time Time string of current comment.
635 * @utc True if @cur_time is expressed in UTC (unused here).
636 * @comment Comment to display.
637 * @file_hdr System activity file standard header (unused here).
638 ***************************************************************************
640 __print_funct_t print_sar_comment(int *tab, int action, char *cur_date, char *cur_time, int utc,
641 char *comment, struct file_header *file_hdr)
643 printf("%-11s", cur_time);
644 cprintf_s(IS_COMMENT, " COM %s\n", comment);
648 ***************************************************************************
649 * Read the various statistics sent by the data collector (sadc).
652 * @curr Index in array for current sample statistics.
653 ***************************************************************************
655 void read_sadc_stat_bunch(int curr)
659 /* Read record header (type is always R_STATS since it is read from sadc) */
660 if (sa_read(&record_hdr[curr], RECORD_HEADER_SIZE)) {
662 * SIGINT (sent by sadc) is likely to be received
663 * while we are stuck in sa_read().
664 * If this happens then no data have to be read.
670 fprintf(stderr, "%s: Record header\n", __FUNCTION__);
672 print_read_error(END_OF_DATA_UNEXPECTED);
675 for (i = 0; i < NR_ACT; i++) {
679 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
681 if (HAS_COUNT_FUNCTION(act[p]->options)) {
682 if (sa_read(&(act[p]->nr[curr]), sizeof(__nr_t))) {
684 fprintf(stderr, "%s: Nb of items\n", __FUNCTION__);
686 print_read_error(END_OF_DATA_UNEXPECTED);
688 if ((act[p]->nr[curr] > act[p]->nr_max) || (act[p]->nr[curr] < 0)) {
690 fprintf(stderr, "%s: %s: nr=%d nr_max=%d\n",
691 __FUNCTION__, act[p]->name, act[p]->nr[curr], act[p]->nr_max);
693 print_read_error(INCONSISTENT_INPUT_DATA);
695 if (act[p]->nr[curr] > act[p]->nr_allocated) {
696 reallocate_all_buffers(act[p], act[p]->nr[curr]);
701 * For persistent activities, we must make sure that no statistics
702 * from a previous iteration remain, especially if the number
703 * of structures read is smaller than @nr_ini.
705 if (HAS_PERSISTENT_VALUES(act[p]->options)) {
706 memset(act[p]->buf[curr], 0,
707 (size_t) act[p]->fsize * (size_t) act[p]->nr_ini * (size_t) act[p]->nr2);
710 if (sa_read(act[p]->buf[curr],
711 (size_t) act[p]->fsize * (size_t) act[p]->nr[curr] * (size_t) act[p]->nr2)) {
713 fprintf(stderr, "%s: Statistics\n", __FUNCTION__);
715 print_read_error(END_OF_DATA_UNEXPECTED);
721 ***************************************************************************
722 * Read stats for current activity from file and display them.
725 * @ifd Input file descriptor.
726 * @fpos Position in file where reading must start.
727 * @curr Index in array for current sample statistics.
728 * @rows Number of rows of screen.
729 * @act_id Activity to display.
730 * @file_actlst List of activities in file.
731 * @file Name of file being read.
732 * @file_magic file_magic structure filled with file magic header data.
733 * @rec_hdr_tmp Temporary buffer where current record header will be saved.
735 * TRUE if file's data don't match current machine's endianness.
736 * @arch_64 TRUE if file's data come from a 64 bit machine.
739 * @curr Index in array for next sample statistics.
740 * @cnt Number of remaining lines of stats to write.
741 * @eosaf Set to TRUE if EOF (end of file) has been reached.
742 * @reset Set to TRUE if last_uptime variable should be reinitialized
743 * (used in next_slice() function).
744 ***************************************************************************
746 void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf,
747 int rows, unsigned int act_id, int *reset,
748 struct file_activity *file_actlst, char *file,
749 struct file_magic *file_magic, void *rec_hdr_tmp,
750 int endian_mismatch, int arch_64)
753 unsigned long lines = 0;
755 int davg = 0, next, inc = 0;
757 if (lseek(ifd, fpos, SEEK_SET) < fpos) {
763 * Restore the first stats collected.
764 * Used to compute the rate displayed on the first line.
766 copy_structures(act, id_seq, record_hdr, !*curr, 2);
770 /* Assess number of lines printed when a bitmap is used */
771 p = get_activity_position(act, act_id, EXIT_IF_NOT_FOUND);
772 if (act[p]->bitmap) {
773 inc = count_bits(act[p]->bitmap->b_array,
774 BITMAP_SIZE(act[p]->bitmap->b_size));
780 * Display <count> lines of stats.
781 * Start with reading current sample's record header.
783 *eosaf = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[*curr],
784 &file_hdr, arch_64, endian_mismatch);
785 rtype = record_hdr[*curr].record_type;
787 if (!*eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
788 /* Read the extra fields since it's not a special record */
789 read_file_stat_bunch(act, *curr, ifd, file_hdr.sa_act_nr, file_actlst,
790 endian_mismatch, arch_64, file, file_magic);
793 if ((lines >= rows) || !lines) {
800 if (!*eosaf && (rtype != R_RESTART)) {
802 if (rtype == R_COMMENT) {
803 /* Display comment */
804 next = print_special_record(&record_hdr[*curr], flags + S_F_LOCAL_TIME,
805 &tm_start, &tm_end, R_COMMENT, ifd,
806 &rectime, NULL, file, 0,
807 file_magic, &file_hdr, act, &sar_fmt,
808 endian_mismatch, arch_64);
810 /* A line of comment was actually displayed */
816 /* next is set to 1 when we were close enough to desired interval */
817 next = write_stats(*curr, USE_SA_FILE, cnt, tm_start.use, tm_end.use,
818 *reset, act_id, reset_cd);
820 if (next && (*cnt > 0)) {
832 lines += act[p]->nr[*curr];
838 while (*cnt && !*eosaf && (rtype != R_RESTART));
841 write_stats_avg(!*curr, USE_SA_FILE, act_id);
848 ***************************************************************************
849 * Read header data sent by sadc.
850 ***************************************************************************
852 void read_header_data(void)
854 struct file_magic file_magic;
855 struct file_activity file_act;
859 /* Read magic header */
860 rc = sa_read(&file_magic, FILE_MAGIC_SIZE);
862 sprintf(version, "%d.%d.%d.%d",
863 file_magic.sysstat_version,
864 file_magic.sysstat_patchlevel,
865 file_magic.sysstat_sublevel,
866 file_magic.sysstat_extraversion);
867 if (!file_magic.sysstat_extraversion) {
868 version[strlen(version) - 2] = '\0';
871 if (rc || (file_magic.sysstat_magic != SYSSTAT_MAGIC) ||
872 (file_magic.format_magic != FORMAT_MAGIC) ||
873 strcmp(version, VERSION)) {
875 /* sar and sadc commands are not consistent */
876 if (!rc && (file_magic.sysstat_magic == SYSSTAT_MAGIC)) {
878 _("Using a wrong data collector from a different sysstat version\n"));
882 fprintf(stderr, "%s: sysstat_magic=%x format_magic=%x version=%s\n",
883 __FUNCTION__, file_magic.sysstat_magic, file_magic.format_magic, version);
885 print_read_error(INCONSISTENT_INPUT_DATA);
890 * No need to take into account file_magic.header_size. We are sure that
891 * sadc and sar are from the same version (we have checked FORMAT_MAGIC
892 * but also VERSION above) and thus the size of file_header is FILE_HEADER_SIZE.
894 if (sa_read(&file_hdr, FILE_HEADER_SIZE)) {
896 fprintf(stderr, "%s: File header\n", __FUNCTION__);
898 print_read_error(END_OF_DATA_UNEXPECTED);
901 /* All activities are not necessarily selected, but NR_ACT is a max */
902 if (file_hdr.sa_act_nr > NR_ACT) {
904 fprintf(stderr, "%s: sa_act_nr=%d\n", __FUNCTION__, file_hdr.sa_act_nr);
906 print_read_error(INCONSISTENT_INPUT_DATA);
909 if ((file_hdr.act_size != FILE_ACTIVITY_SIZE) ||
910 (file_hdr.rec_size != RECORD_HEADER_SIZE)) {
912 fprintf(stderr, "%s: act_size=%u/%lu rec_size=%u/%lu\n", __FUNCTION__,
913 file_hdr.act_size, FILE_ACTIVITY_SIZE, file_hdr.rec_size, RECORD_HEADER_SIZE);
915 print_read_error(INCONSISTENT_INPUT_DATA);
918 /* Read activity list */
919 for (i = 0; i < file_hdr.sa_act_nr; i++) {
921 if (sa_read(&file_act, FILE_ACTIVITY_SIZE)) {
923 fprintf(stderr, "%s: File activity (%d)\n", __FUNCTION__, i);
925 print_read_error(END_OF_DATA_UNEXPECTED);
928 p = get_activity_position(act, file_act.id, RESUME_IF_NOT_FOUND);
930 if ((p < 0) || (act[p]->fsize != file_act.size)
931 || (act[p]->gtypes_nr[0] != file_act.types_nr[0])
932 || (act[p]->gtypes_nr[1] != file_act.types_nr[1])
933 || (act[p]->gtypes_nr[2] != file_act.types_nr[2])
934 || (file_act.nr <= 0)
935 || (file_act.nr2 <= 0)
936 || (act[p]->magic != file_act.magic)) {
939 fprintf(stderr, "%s: p=%d\n", __FUNCTION__, p);
942 fprintf(stderr, "%s: %s: size=%d/%d magic=%x/%x nr=%d nr2=%d types=%d,%d,%d/%d,%d,%d\n",
943 __FUNCTION__, act[p]->name, act[p]->fsize, file_act.size,
944 act[p]->magic, file_act.magic, file_act.nr, file_act.nr2,
945 act[p]->gtypes_nr[0], act[p]->gtypes_nr[1], act[p]->gtypes_nr[2],
946 file_act.types_nr[0], file_act.types_nr[1],file_act.types_nr[2]);
949 /* Remember that we are reading data from sadc and not from a file... */
950 print_read_error(INCONSISTENT_INPUT_DATA);
953 id_seq[i] = file_act.id; /* We necessarily have "i < NR_ACT" */
954 act[p]->nr_ini = file_act.nr;
955 act[p]->nr2 = file_act.nr2;
962 /* Check that all selected activties are actually sent by sadc */
963 reverse_check_act(file_hdr.sa_act_nr);
969 ***************************************************************************
970 * Read statistics from a system activity data file.
973 * @from_file Input file name.
974 ***************************************************************************
976 void read_stats_from_file(char from_file[])
978 struct file_magic file_magic;
979 struct file_activity *file_actlst = NULL;
980 char rec_hdr_tmp[MAX_RECORD_HEADER_SIZE];
983 int rows, eosaf = TRUE, reset = FALSE;
987 /* Get window size */
988 rows = get_win_height();
990 /* Read file headers and activity list */
991 check_file_actlst(&ifd, from_file, act, &file_magic, &file_hdr,
992 &file_actlst, id_seq, FALSE, &endian_mismatch, &arch_64);
994 /* Perform required allocations */
995 allocate_structures(act);
997 /* Print report header */
998 print_report_hdr(flags, &rectime, &file_hdr);
1000 /* Read system statistics from file */
1003 * If this record is a special (RESTART or COMMENT) one, print it and
1004 * (try to) get another one.
1007 if (read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[0], &file_hdr,
1008 arch_64, endian_mismatch)) {
1009 /* End of sa data file */
1013 rtype = record_hdr[0].record_type;
1014 if ((rtype == R_RESTART) || (rtype == R_COMMENT)) {
1015 print_special_record(&record_hdr[0], flags + S_F_LOCAL_TIME,
1016 &tm_start, &tm_end, rtype, ifd,
1017 &rectime, NULL, from_file, 0, &file_magic,
1018 &file_hdr, act, &sar_fmt, endian_mismatch, arch_64);
1022 * OK: Previous record was not a special one.
1023 * So read now the extra fields.
1025 read_file_stat_bunch(act, 0, ifd, file_hdr.sa_act_nr,
1026 file_actlst, endian_mismatch, arch_64,
1027 from_file, &file_magic);
1028 if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME,
1032 * An error was detected.
1033 * The timestamp hasn't been updated.
1038 while ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
1039 (tm_start.use && (datecmp(&rectime, &tm_start) < 0)) ||
1040 (tm_end.use && (datecmp(&rectime, &tm_end) >=0)));
1042 /* Save the first stats collected. Will be used to compute the average */
1043 copy_structures(act, id_seq, record_hdr, 2, 0);
1045 reset = TRUE; /* Set flag to reset last_uptime variable */
1047 /* Save current file position */
1048 if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) {
1054 * Read and write stats located between two possible Linux restarts.
1055 * Activities that should be displayed are saved in id_seq[] array.
1056 * Since we are reading from a file, we print all the stats for an
1057 * activity before displaying the next activity.
1058 * id_seq[] has been created in check_file_actlst(), retaining only
1059 * activities known by current sysstat version.
1061 for (i = 0; i < NR_ACT; i++) {
1066 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
1067 if (!IS_SELECTED(act[p]->options))
1070 if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) {
1071 handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, rows,
1072 act[p]->id, &reset, file_actlst,
1073 from_file, &file_magic, rec_hdr_tmp,
1074 endian_mismatch, arch_64);
1077 unsigned int optf, msk;
1079 optf = act[p]->opt_flags;
1081 for (msk = 1; msk < 0x100; msk <<= 1) {
1082 if ((act[p]->opt_flags & 0xff) & msk) {
1083 act[p]->opt_flags &= (0xffffff00 + msk);
1085 handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf,
1086 rows, act[p]->id, &reset, file_actlst,
1087 from_file, &file_magic, rec_hdr_tmp,
1088 endian_mismatch, arch_64);
1089 act[p]->opt_flags = optf;
1096 /* Go to next Linux restart, if possible */
1098 /* Read next record header */
1099 eosaf = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[curr],
1100 &file_hdr, arch_64, endian_mismatch);
1101 rtype = record_hdr[curr].record_type;
1103 if (!eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
1104 read_file_stat_bunch(act, curr, ifd, file_hdr.sa_act_nr,
1105 file_actlst, endian_mismatch, arch_64,
1106 from_file, &file_magic);
1108 else if (!eosaf && (rtype == R_COMMENT)) {
1109 /* This was a COMMENT record: print it */
1110 print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
1111 &tm_start, &tm_end, R_COMMENT, ifd,
1112 &rectime, NULL, from_file, 0,
1113 &file_magic, &file_hdr, act, &sar_fmt,
1114 endian_mismatch, arch_64);
1117 while (!eosaf && (rtype != R_RESTART));
1120 /* The last record we read was a RESTART one: Print it */
1121 if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) {
1122 print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
1123 &tm_start, &tm_end, R_RESTART, ifd,
1124 &rectime, NULL, from_file, 0,
1125 &file_magic, &file_hdr, act, &sar_fmt,
1126 endian_mismatch, arch_64);
1137 ***************************************************************************
1138 * Read statistics sent by sadc, the data collector.
1139 ***************************************************************************
1141 void read_stats(void)
1144 unsigned long lines;
1148 /* Don't buffer data if redirected to a pipe... */
1149 setbuf(stdout, NULL);
1151 /* Read stats header */
1154 if (!get_activity_nr(act, AO_SELECTED, COUNT_ACTIVITIES)) {
1155 /* Requested activities not available: Exit */
1156 print_collect_error();
1159 /* Determine if a stat line header has to be displayed */
1160 dis_hdr = check_line_hdr();
1162 lines = rows = get_win_height();
1164 /* Perform required allocations */
1165 allocate_structures(act);
1167 /* Print report header */
1168 print_report_hdr(flags, &rectime, &file_hdr);
1170 /* Read system statistics sent by the data collector */
1171 read_sadc_stat_bunch(0);
1174 /* Display stats since boot time and exit */
1175 write_stats_startup(0);
1178 /* Save the first stats collected. Will be used to compute the average */
1179 copy_structures(act, id_seq, record_hdr, 2, 0);
1181 /* Set a handler for SIGINT */
1182 memset(&int_act, 0, sizeof(int_act));
1183 int_act.sa_handler = int_handler;
1184 int_act.sa_flags = SA_RESTART;
1185 sigaction(SIGINT, &int_act, NULL);
1191 read_sadc_stat_bunch(curr);
1192 if (sigint_caught) {
1194 * SIGINT signal caught (it is sent by sadc).
1195 * => Display average stats.
1197 curr ^= 1; /* No data retrieved from last read */
1209 write_stats(curr, USE_SADC, &count, NO_TM_START, tm_end.use,
1210 NO_RESET, ALL_ACTIVITIES, TRUE);
1212 if (record_hdr[curr].record_type == R_LAST_STATS) {
1213 /* File rotation is happening: Re-read header data sent by sadc */
1215 allocate_structures(act);
1228 * Print statistics average.
1229 * At least one line of stats must have been displayed for this.
1230 * (There may be no lines at all if we press Ctrl/C immediately).
1234 write_stats_avg(curr, USE_SADC, ALL_ACTIVITIES);
1239 ***************************************************************************
1240 * Main entry to the sar program.
1241 ***************************************************************************
1243 int main(int argc, char **argv)
1245 int i, rc, opt = 1, args_idx = 1;
1248 char from_file[MAX_FILE_LEN], to_file[MAX_FILE_LEN];
1251 /* Compute page shift in kB */
1254 from_file[0] = to_file[0] = '\0';
1257 /* Init National Language Support */
1261 /* Init color strings */
1264 tm_start.use = tm_end.use = FALSE;
1266 /* Allocate and init activity bitmaps */
1267 allocate_bitmaps(act);
1271 /* Process options */
1272 while (opt < argc) {
1274 if (!strcmp(argv[opt], "--sadc")) {
1279 else if (!strncmp(argv[opt], "--iface=", 8)) {
1280 /* Parse devices entered on the command line */
1281 parse_sa_devices(argc, argv, &st_dev_list,
1282 &dlist_idx, &opt, A_NET_DEV, 8);
1285 else if (!strcmp(argv[opt], "--help")) {
1286 /* Display help message */
1287 display_help(argv[0]);
1290 else if (!strcmp(argv[opt], "--human")) {
1291 /* Display sizes in a human readable format */
1296 else if (!strncmp(argv[opt], "--dec=", 6) && (strlen(argv[opt]) == 7)) {
1297 /* Get number of decimal places */
1298 dplaces_nr = atoi(argv[opt] + 6);
1299 if ((dplaces_nr < 0) || (dplaces_nr > 2)) {
1305 else if (!strcmp(argv[opt], "-I")) {
1306 /* Parse -I option */
1307 if (parse_sar_I_opt(argv, &opt, act)) {
1312 else if (!strcmp(argv[opt], "-D")) {
1313 /* Option to tell sar to write to saYYYYMMDD data files */
1314 flags |= S_F_SA_YYYYMMDD;
1318 else if (!strcmp(argv[opt], "-P")) {
1319 /* Parse -P option */
1320 if (parse_sa_P_opt(argv, &opt, &flags, act)) {
1325 else if (!strcmp(argv[opt], "-o")) {
1327 /* Output file already specified */
1330 /* Save stats to a file */
1331 if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1332 (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1333 strncpy(to_file, argv[opt++], MAX_FILE_LEN);
1334 to_file[MAX_FILE_LEN - 1] = '\0';
1337 strcpy(to_file, "-");
1341 else if (!strcmp(argv[opt], "-f")) {
1342 if (from_file[0] || day_offset) {
1343 /* Input file already specified */
1346 /* Read stats from a file */
1347 if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1348 (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1349 strncpy(from_file, argv[opt++], MAX_FILE_LEN);
1350 from_file[MAX_FILE_LEN - 1] = '\0';
1351 /* Check if this is an alternate directory for sa files */
1352 check_alt_sa_dir(from_file, day_offset, -1);
1355 set_default_file(from_file, day_offset, -1);
1359 else if (!strcmp(argv[opt], "-s")) {
1360 /* Get time start */
1361 if (parse_timestamp(argv, &opt, &tm_start, DEF_TMSTART)) {
1366 else if (!strcmp(argv[opt], "-e")) {
1368 if (parse_timestamp(argv, &opt, &tm_end, DEF_TMEND)) {
1373 else if (!strcmp(argv[opt], "-i")) {
1374 if (!argv[++opt] || (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1377 interval = atol(argv[opt++]);
1381 flags |= S_F_INTERVAL_SET;
1384 else if (!strcmp(argv[opt], "-m")) {
1388 /* Parse option -m */
1389 if (parse_sar_m_opt(argv, &opt, act)) {
1394 else if (!strcmp(argv[opt], "-n")) {
1398 /* Parse option -n */
1399 if (parse_sar_n_opt(argv, &opt, act)) {
1404 else if ((strlen(argv[opt]) > 1) &&
1405 (strlen(argv[opt]) < 4) &&
1406 !strncmp(argv[opt], "-", 1) &&
1407 (strspn(argv[opt] + 1, DIGITS) == (strlen(argv[opt]) - 1))) {
1408 if (from_file[0] || day_offset) {
1409 /* Input file already specified */
1412 day_offset = atoi(argv[opt++] + 1);
1415 else if (!strncmp(argv[opt], "-", 1)) {
1416 /* Other options not previously tested */
1417 if ((rc = parse_sar_opt(argv, &opt, act, &flags, C_SAR)) != 0) {
1426 else if (interval < 0) {
1428 if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1431 interval = atol(argv[opt++]);
1438 /* Get count value */
1439 if ((strspn(argv[opt], DIGITS) != strlen(argv[opt])) ||
1444 /* Count parameter already set */
1447 count = atol(argv[opt++]);
1454 /* 'sar' is equivalent to 'sar -f' */
1456 ((interval < 0) && !from_file[0] && !to_file[0])) {
1457 set_default_file(from_file, day_offset, -1);
1460 if (tm_start.use && tm_end.use && (tm_end.tm_hour < tm_start.tm_hour)) {
1461 tm_end.tm_hour += 24;
1465 * Check option dependencies.
1467 /* You read from a file OR you write to it... */
1468 if (from_file[0] && to_file[0]) {
1469 fprintf(stderr, _("-f and -o options are mutually exclusive\n"));
1472 /* Use time start or option -i only when reading stats from a file */
1473 if ((tm_start.use || INTERVAL_SET(flags)) && !from_file[0]) {
1475 _("Not reading from a system activity file (use -f option)\n"));
1478 /* Don't print stats since boot time if -o or -f options are used */
1479 if (!interval && (from_file[0] || to_file[0])) {
1483 /* Cannot enter a day shift with -o option */
1484 if (to_file[0] && day_offset) {
1488 if (USE_PRETTY_OPTION(flags)) {
1489 dm_major = get_devmap_major();
1494 * count parameter not set: Display all the contents of the file
1495 * or generate a report continuously.
1500 /* Default is CPU activity... */
1501 select_default_activity(act);
1503 /* Reading stats from file: */
1509 /* Read stats from file */
1510 read_stats_from_file(from_file);
1512 /* Free stuctures and activity bitmaps */
1514 free_structures(act);
1522 /* Reading stats from sadc: */
1524 /* Create anonymous pipe */
1525 if (pipe(fd) == -1) {
1538 if (dup2(fd[1], STDOUT_FILENO) < 0) {
1545 * Prepare options for sadc.
1550 /* Interval value */
1554 else if (!interval) {
1557 * Display stats since system startup: Set <interval> to 1.
1558 * <count> arg will also be set to 1 below.
1560 salloc(args_idx++, ltemp);
1563 sprintf(ltemp, "%ld", interval);
1565 salloc(args_idx++, ltemp);
1569 sprintf(ltemp, "%ld", count + 1);
1570 salloc(args_idx++, ltemp);
1573 /* Flags to be passed to sadc */
1574 salloc(args_idx++, "-Z");
1576 /* Writing data to a file (option -o) */
1578 /* Set option -D if entered */
1579 if (USE_SA_YYYYMMDD(flags)) {
1580 salloc(args_idx++, "-D");
1582 /* Collect all possible activities (option -S XALL for sadc) */
1583 salloc(args_idx++, "-S");
1584 salloc(args_idx++, K_XALL);
1586 salloc(args_idx++, to_file);
1590 * If option -o hasn't been used, then tell sadc
1591 * to collect only activities that will be displayed.
1593 salloc(args_idx++, "-S");
1594 strcpy(ltemp, K_A_NULL);
1595 for (i = 0; i < NR_ACT; i++) {
1596 if (IS_SELECTED(act[i]->options)) {
1598 strcat(ltemp, act[i]->name);
1601 salloc(args_idx++, ltemp);
1604 /* Last arg is NULL */
1605 args[args_idx] = NULL;
1607 /* Call now the data collector */
1609 fprintf(stderr, "%s: 1.sadc: %s\n", __FUNCTION__, SADC_PATH);
1612 execv(SADC_PATH, args);
1614 fprintf(stderr, "%s: 2.sadc: %s\n", __FUNCTION__, SADC);
1618 * Note: Don't use execl/execlp since we don't have a fixed number of
1619 * args to give to sadc.
1621 fprintf(stderr, _("Cannot find the data collector (%s)\n"), SADC);
1626 default: /* Parent */
1627 if (dup2(fd[0], STDIN_FILENO) < 0) {
1633 /* Get now the statistics */
1639 /* Free structures and activity bitmaps */
1641 free_structures(act);