2 * sar: report system activity
3 * (C) 1999-2015 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 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
19 ***************************************************************************
40 #define _(string) gettext(string)
42 #define _(string) (string)
45 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
46 char *sccsid(void) { return (SCCSID); }
48 /* Interval and count parameters */
49 long interval = -1, count = 0;
51 /* TRUE if a header line must be printed */
54 unsigned int flags = 0;
55 unsigned int dm_major; /* Device-mapper major number */
57 char timestamp[2][TIMESTAMP_LEN];
59 unsigned long avg_count = 0;
62 struct file_header file_hdr;
64 /* Current record header */
65 struct record_header record_hdr[3];
69 * This array must always be entirely filled (even with trailing zeros).
71 unsigned int id_seq[NR_ACT];
75 /* Contain the date specified by -s and -e options */
76 struct tstamp tm_start, tm_end;
78 char *args[MAX_ARGV_NR];
80 extern struct activity *act[];
82 struct sigaction int_act;
83 int sigint_caught = 0;
86 ***************************************************************************
87 * Print usage title message.
90 * @progname Name of sysstat command
91 ***************************************************************************
93 void print_usage_title(FILE *fp, char *progname)
95 fprintf(fp, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
100 ***************************************************************************
101 * Print usage and exit.
104 * @progname Name of sysstat command
105 ***************************************************************************
107 void usage(char *progname)
109 print_usage_title(stderr, progname);
110 fprintf(stderr, _("Options are:\n"
111 "[ -A ] [ -B ] [ -b ] [ -C ] [ -D ] [ -d ] [ -F [ MOUNTS ] ] [ -H ] [ -h ]\n"
112 "[ -p ] [ -q ] [ -R ] [ -r [ ALL ] ] [ -S ] [ -t ] [ -u [ ALL ] ] [ -V ]\n"
113 "[ -v ] [ -W ] [ -w ] [ -y ] [ --sadc ]\n"
114 "[ -I { <int> [,...] | SUM | ALL | XALL } ] [ -P { <cpu> [,...] | ALL } ]\n"
115 "[ -m { <keyword> [,...] | ALL } ] [ -n { <keyword> [,...] | ALL } ]\n"
116 "[ -j { ID | LABEL | PATH | UUID | ... } ]\n"
117 "[ -f [ <filename> ] | -o [ <filename> ] | -[0-9]+ ]\n"
118 "[ -i <interval> ] [ -s [ <hh:mm[:ss]> ] ] [ -e [ <hh:mm[:ss]> ] ]\n"));
123 ***************************************************************************
124 * Display a short help message and exit.
127 * @progname Name of sysstat command
128 ***************************************************************************
130 void display_help(char *progname)
132 print_usage_title(stdout, progname);
133 printf(_("Main options and reports:\n"));
134 printf(_("\t-B\tPaging statistics\n"));
135 printf(_("\t-b\tI/O and transfer rate statistics\n"));
136 printf(_("\t-d\tBlock devices statistics\n"));
137 printf(_("\t-F [ MOUNTS ]\n"));
138 printf(_("\t\tFilesystems statistics\n"));
139 printf(_("\t-H\tHugepages utilization statistics\n"));
140 printf(_("\t-I { <int> | SUM | ALL | XALL }\n"
141 "\t\tInterrupts statistics\n"));
142 printf(_("\t-m { <keyword> [,...] | ALL }\n"
143 "\t\tPower management statistics\n"
144 "\t\tKeywords are:\n"
145 "\t\tCPU\tCPU instantaneous clock frequency\n"
146 "\t\tFAN\tFans speed\n"
147 "\t\tFREQ\tCPU average clock frequency\n"
148 "\t\tIN\tVoltage inputs\n"
149 "\t\tTEMP\tDevices temperature\n"
150 "\t\tUSB\tUSB devices plugged into the system\n"));
151 printf(_("\t-n { <keyword> [,...] | ALL }\n"
152 "\t\tNetwork statistics\n"
153 "\t\tKeywords are:\n"
154 "\t\tDEV\tNetwork interfaces\n"
155 "\t\tEDEV\tNetwork interfaces (errors)\n"
156 "\t\tNFS\tNFS client\n"
157 "\t\tNFSD\tNFS server\n"
158 "\t\tSOCK\tSockets\t(v4)\n"
159 "\t\tIP\tIP traffic\t(v4)\n"
160 "\t\tEIP\tIP traffic\t(v4) (errors)\n"
161 "\t\tICMP\tICMP traffic\t(v4)\n"
162 "\t\tEICMP\tICMP traffic\t(v4) (errors)\n"
163 "\t\tTCP\tTCP traffic\t(v4)\n"
164 "\t\tETCP\tTCP traffic\t(v4) (errors)\n"
165 "\t\tUDP\tUDP traffic\t(v4)\n"
166 "\t\tSOCK6\tSockets\t(v6)\n"
167 "\t\tIP6\tIP traffic\t(v6)\n"
168 "\t\tEIP6\tIP traffic\t(v6) (errors)\n"
169 "\t\tICMP6\tICMP traffic\t(v6)\n"
170 "\t\tEICMP6\tICMP traffic\t(v6) (errors)\n"
171 "\t\tUDP6\tUDP traffic\t(v6)\n"
172 "\t\tFC\tFibre channel HBAs\n"));
173 printf(_("\t-q\tQueue length and load average statistics\n"));
174 printf(_("\t-R\tMemory statistics\n"));
175 printf(_("\t-r [ ALL ]\n"
176 "\t\tMemory utilization statistics\n"));
177 printf(_("\t-S\tSwap space utilization statistics\n"));
178 printf(_("\t-u [ ALL ]\n"
179 "\t\tCPU utilization statistics\n"));
180 printf(_("\t-v\tKernel tables statistics\n"));
181 printf(_("\t-W\tSwapping statistics\n"));
182 printf(_("\t-w\tTask creation and system switching statistics\n"));
183 printf(_("\t-y\tTTY devices statistics\n"));
188 ***************************************************************************
189 * Give a hint to the user about where is located the data collector.
190 ***************************************************************************
192 void which_sadc(void)
196 if (stat(SADC_PATH, &buf) < 0) {
197 printf(_("Data collector will be sought in PATH\n"));
200 printf(_("Data collector found: %s\n"), SADC_PATH);
207 ***************************************************************************
208 * SIGINT signal handler.
211 * @sig Signal number.
212 ***************************************************************************
214 void int_handler(int sig)
217 printf("\n"); /* Skip "^C" displayed on screen */
222 ***************************************************************************
223 * Init some structures.
224 ***************************************************************************
226 void init_structures(void)
230 for (i = 0; i < 3; i++)
231 memset(&record_hdr[i], 0, RECORD_HEADER_SIZE);
235 ***************************************************************************
236 * Allocate memory for sadc args.
239 * @i Argument number.
240 * @ltemp Argument value.
241 ***************************************************************************
243 void salloc(int i, char *ltemp)
245 if ((args[i] = (char *) malloc(strlen(ltemp) + 1)) == NULL) {
249 strcpy(args[i], ltemp);
253 ***************************************************************************
254 * Display an error message. Happens when the data collector doesn't send
256 ***************************************************************************
258 void print_read_error(void)
260 fprintf(stderr, _("End of data collecting unexpected\n"));
265 ***************************************************************************
266 * Check that every selected activity actually belongs to the sequence list.
267 * If not, then the activity should be unselected since it will not be sent
268 * by sadc. An activity can be not sent if its number of items is null.
271 * @act_nr Size of sequence list.
272 ***************************************************************************
274 void reverse_check_act(unsigned int act_nr)
278 for (i = 0; i < NR_ACT; i++) {
280 if (IS_SELECTED(act[i]->options)) {
282 for (j = 0; j < act_nr; j++) {
283 if (id_seq[j] == act[i]->id)
287 act[i]->options &= ~AO_SELECTED;
293 ***************************************************************************
294 * Fill the (struct tm) rectime structure with current record's time,
295 * based on current record's time data saved in file.
296 * The resulting timestamp is expressed in the locale of the file creator
297 * or in the user's own locale depending on whether option -t has been used
301 * @curr Index in array for current sample statistics.
304 * 1 if an error was detected, or 0 otherwise.
305 ***************************************************************************
307 int sar_get_record_timestamp_struct(int curr)
311 /* Check if option -t was specified on the command line */
312 if (PRINT_TRUE_TIME(flags)) {
314 rectime.tm_hour = record_hdr[curr].hour;
315 rectime.tm_min = record_hdr[curr].minute;
316 rectime.tm_sec = record_hdr[curr].second;
319 if ((ltm = localtime((const time_t *) &record_hdr[curr].ust_time)) == NULL)
321 * An error was detected.
322 * The rectime structure has NOT been updated.
333 ***************************************************************************
334 * Determine if a stat header line has to be displayed.
337 * TRUE if a header line has to be displayed.
338 ***************************************************************************
340 int check_line_hdr(void)
344 /* Get number of options entered on the command line */
345 if (get_activity_nr(act, AO_SELECTED, COUNT_OUTPUTS) > 1)
348 for (i = 0; i < NR_ACT; i++) {
349 if (IS_SELECTED(act[i]->options)) {
350 /* Special processing for activities using a bitmap */
351 if (act[i]->bitmap) {
352 if (count_bits(act[i]->bitmap->b_array,
353 BITMAP_SIZE(act[i]->bitmap->b_size)) > 1) {
357 else if (act[i]->nr > 1) {
360 /* Stop now since we have only one selected activity */
369 ***************************************************************************
370 * Set current record's timestamp string.
373 * @curr Index in array for current sample statistics.
374 * @len Maximum length of timestamp string.
377 * @cur_time Timestamp string.
380 * 1 if an error was detected, or 0 otherwise.
381 ***************************************************************************
383 int set_record_timestamp_string(int curr, char *cur_time, int len)
385 /* Fill timestamp structure */
386 if (sar_get_record_timestamp_struct(curr))
390 /* Set cur_time date value */
391 strftime(cur_time, len, "%X", &rectime);
397 ***************************************************************************
398 * Print statistics average.
401 * @curr Index in array for current sample statistics.
402 * @read_from_file Set to TRUE if stats are read from a system activity
404 * @act_id Activity that can be displayed, or ~0 for all.
405 * Remember that when reading stats from a file, only
406 * one activity can be displayed at a time.
407 ***************************************************************************
409 void write_stats_avg(int curr, int read_from_file, unsigned int act_id)
412 unsigned long long itv, g_itv;
413 static __nr_t cpu_nr = -1;
416 cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr;
418 /* Interval value in jiffies */
419 g_itv = get_interval(record_hdr[2].uptime, record_hdr[curr].uptime);
422 itv = get_interval(record_hdr[2].uptime0, record_hdr[curr].uptime0);
426 strncpy(timestamp[curr], _("Average:"), TIMESTAMP_LEN);
427 timestamp[curr][TIMESTAMP_LEN - 1] = '\0';
428 strcpy(timestamp[!curr], timestamp[curr]);
431 TEST_STDOUT(STDOUT_FILENO);
433 for (i = 0; i < NR_ACT; i++) {
435 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
438 if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
439 /* Display current average activity statistics */
440 (*act[i]->f_print_avg)(act[i], 2, curr,
441 NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv);
445 if (read_from_file) {
447 * Reset number of lines printed only if we read stats
448 * from a system activity file.
455 ***************************************************************************
456 * Print system statistics.
459 * @curr Index in array for current sample statistics.
460 * @read_from_file Set to TRUE if stats are read from a system activity
462 * @use_tm_start Set to TRUE if option -s has been used.
463 * @use_tm_end Set to TRUE if option -e has been used.
464 * @reset Set to TRUE if last_uptime variable should be
465 * reinitialized (used in next_slice() function).
466 * @act_id Activity that can be displayed or ~0 for all.
467 * Remember that when reading stats from a file, only
468 * one activity can be displayed at a time.
469 * @reset_cd TRUE if static cross_day variable should be reset
473 * @cnt Number of remaining lines to display.
476 * 1 if stats have been successfully displayed, and 0 otherwise.
477 ***************************************************************************
479 int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start,
480 int use_tm_end, int reset, unsigned int act_id, int reset_cd)
483 unsigned long long itv, g_itv;
484 static int cross_day = 0;
485 static __nr_t cpu_nr = -1;
488 cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr;
492 * cross_day is a static variable that is set to 1 when the first
493 * record of stats from a new day is read from a unique data file
494 * (in the case where the file contains data from two consecutive
495 * days). When set to 1, every following records timestamp will
496 * have its hour value increased by 24.
497 * Yet when a new activity (being read from the file) is going to
498 * be displayed, we start reading the file from the beginning
499 * again, and so cross_day should be reset in this case.
505 if (read_from_file) {
506 if (!next_slice(record_hdr[2].uptime0, record_hdr[curr].uptime0,
508 /* Not close enough to desired interval */
512 /* Set previous timestamp */
513 if (set_record_timestamp_string(!curr, timestamp[!curr], 16))
515 /* Set current timestamp */
516 if (set_record_timestamp_string(curr, timestamp[curr], 16))
519 /* Check if we are beginning a new day */
520 if (use_tm_start && record_hdr[!curr].ust_time &&
521 (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) &&
522 (record_hdr[curr].hour < record_hdr[!curr].hour)) {
528 * This is necessary if we want to properly handle something like:
529 * sar -s time_start -e time_end with
530 * time_start(day D) > time_end(day D+1)
532 rectime.tm_hour +=24;
536 if (use_tm_start && (datecmp(&rectime, &tm_start) < 0))
537 /* it's too soon... */
540 /* Get interval values */
541 get_itv_value(&record_hdr[curr], &record_hdr[!curr],
542 cpu_nr, &itv, &g_itv);
545 if (use_tm_end && (datecmp(&rectime, &tm_end) > 0)) {
546 /* It's too late... */
554 TEST_STDOUT(STDOUT_FILENO);
556 for (i = 0; i < NR_ACT; i++) {
558 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
561 if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
562 /* Display current activity statistics */
563 (*act[i]->f_print)(act[i], !curr, curr,
564 NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv);
572 ***************************************************************************
573 * Display stats since system startup.
576 * @curr Index in array for current sample statistics.
577 ***************************************************************************
579 void write_stats_startup(int curr)
583 /* Set to 0 previous structures corresponding to boot time */
584 memset(&record_hdr[!curr], 0, RECORD_HEADER_SIZE);
585 record_hdr[!curr].record_type = R_STATS;
586 record_hdr[!curr].hour = record_hdr[curr].hour;
587 record_hdr[!curr].minute = record_hdr[curr].minute;
588 record_hdr[!curr].second = record_hdr[curr].second;
589 record_hdr[!curr].ust_time = record_hdr[curr].ust_time;
591 for (i = 0; i < NR_ACT; i++) {
592 if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
593 memset(act[i]->buf[!curr], 0,
594 (size_t) act[i]->msize * (size_t) act[i]->nr * (size_t) act[i]->nr2);
598 flags |= S_F_SINCE_BOOT;
601 write_stats(curr, USE_SADC, &count, NO_TM_START, NO_TM_END, NO_RESET,
602 ALL_ACTIVITIES, TRUE);
608 ***************************************************************************
609 * Read data sent by the data collector.
612 * @size Number of bytes of data to read.
615 * @buffer Buffer where data will be saved.
618 * 1 if end of file has been reached, 0 otherwise.
619 ***************************************************************************
621 int sa_read(void *buffer, int size)
627 if ((n = read(STDIN_FILENO, buffer, size)) < 0) {
636 buffer = (char *) buffer + n;
643 ***************************************************************************
644 * Print a Linux restart message (contents of a RESTART record) or a
645 * comment (contents of a COMMENT record).
648 * @curr Index in array for current sample statistics.
649 * @use_tm_start Set to TRUE if option -s has been used.
650 * @use_tm_end Set to TRUE if option -e has been used.
651 * @rtype Record type to display.
652 * @ifd Input file descriptor.
653 * @file Name of file being read.
654 * @file_magic file_magic structure filled with file magic header
658 * 1 if the record has been successfully displayed, and 0 otherwise.
659 ***************************************************************************
661 int sar_print_special(int curr, int use_tm_start, int use_tm_end, int rtype,
662 int ifd, char *file, struct file_magic *file_magic)
666 unsigned int new_cpu_nr;
668 if (set_record_timestamp_string(curr, cur_time, 26))
671 /* The record must be in the interval specified by -s/-e options */
672 if ((use_tm_start && (datecmp(&rectime, &tm_start) < 0)) ||
673 (use_tm_end && (datecmp(&rectime, &tm_end) > 0))) {
677 if (rtype == R_RESTART) {
678 /* Don't forget to read the volatile activities structures */
679 new_cpu_nr = read_vol_act_structures(ifd, act, file, file_magic,
680 file_hdr.sa_vol_act_nr);
683 printf("\n%-11s LINUX RESTART\t(%d CPU)\n",
684 cur_time, new_cpu_nr > 1 ? new_cpu_nr - 1 : 1);
688 else if (rtype == R_COMMENT) {
689 char file_comment[MAX_COMMENT_LEN];
691 /* Don't forget to read comment record even if it won't be displayed... */
692 sa_fread(ifd, file_comment, MAX_COMMENT_LEN, HARD_SIZE);
693 file_comment[MAX_COMMENT_LEN - 1] = '\0';
695 if (dp && DISPLAY_COMMENT(flags)) {
696 printf("%-11s COM %s\n", cur_time, file_comment);
705 ***************************************************************************
706 * Read the various statistics sent by the data collector (sadc).
709 * @curr Index in array for current sample statistics.
710 ***************************************************************************
712 void read_sadc_stat_bunch(int curr)
716 /* Read record header (type is always R_STATS since it is read from sadc) */
717 if (sa_read(&record_hdr[curr], RECORD_HEADER_SIZE)) {
721 for (i = 0; i < NR_ACT; i++) {
725 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
727 if (sa_read(act[p]->buf[curr], act[p]->fsize * act[p]->nr * act[p]->nr2)) {
734 ***************************************************************************
735 * Read stats for current activity from file and display them.
738 * @ifd Input file descriptor.
739 * @fpos Position in file where reading must start.
740 * @curr Index in array for current sample statistics.
741 * @rows Number of rows of screen.
742 * @act_id Activity to display.
743 * @file_actlst List of activities in file.
744 * @file Name of file being read.
745 * @file_magic file_magic structure filled with file magic header data.
748 * @curr Index in array for next sample statistics.
749 * @cnt Number of remaining lines of stats to write.
750 * @eosaf Set to TRUE if EOF (end of file) has been reached.
751 * @reset Set to TRUE if last_uptime variable should be
752 * reinitialized (used in next_slice() function).
753 ***************************************************************************
755 void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf,
756 int rows, unsigned int act_id, int *reset,
757 struct file_activity *file_actlst, char *file,
758 struct file_magic *file_magic)
761 unsigned long lines = 0;
763 int davg = 0, next, inc;
765 if (lseek(ifd, fpos, SEEK_SET) < fpos) {
771 * Restore the first stats collected.
772 * Used to compute the rate displayed on the first line.
774 copy_structures(act, id_seq, record_hdr, !*curr, 2);
778 /* Assess number of lines printed */
779 p = get_activity_position(act, act_id, EXIT_IF_NOT_FOUND);
780 if (act[p]->bitmap) {
781 inc = count_bits(act[p]->bitmap->b_array,
782 BITMAP_SIZE(act[p]->bitmap->b_size));
791 /* Display count lines of stats */
792 *eosaf = sa_fread(ifd, &record_hdr[*curr],
793 RECORD_HEADER_SIZE, SOFT_SIZE);
794 rtype = record_hdr[*curr].record_type;
796 if (!*eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
797 /* Read the extra fields since it's not a special record */
798 read_file_stat_bunch(act, *curr, ifd, file_hdr.sa_act_nr, file_actlst);
801 if ((lines >= rows) || !lines) {
808 if (!*eosaf && (rtype != R_RESTART)) {
810 if (rtype == R_COMMENT) {
811 /* Display comment */
812 next = sar_print_special(*curr, tm_start.use, tm_end.use,
813 R_COMMENT, ifd, file, file_magic);
815 /* A line of comment was actually displayed */
821 /* next is set to 1 when we were close enough to desired interval */
822 next = write_stats(*curr, USE_SA_FILE, cnt, tm_start.use, tm_end.use,
823 *reset, act_id, reset_cd);
825 if (next && (*cnt > 0)) {
836 while (*cnt && !*eosaf && (rtype != R_RESTART));
839 write_stats_avg(!*curr, USE_SA_FILE, act_id);
846 ***************************************************************************
847 * Read header data sent by sadc.
848 ***************************************************************************
850 void read_header_data(void)
852 struct file_magic file_magic;
853 struct file_activity file_act;
857 /* Read magic header */
858 rc = sa_read(&file_magic, FILE_MAGIC_SIZE);
860 sprintf(version, "%d.%d.%d.%d",
861 file_magic.sysstat_version,
862 file_magic.sysstat_patchlevel,
863 file_magic.sysstat_sublevel,
864 file_magic.sysstat_extraversion);
865 if (!file_magic.sysstat_extraversion) {
866 version[strlen(version) - 2] = '\0';
869 if (rc || (file_magic.sysstat_magic != SYSSTAT_MAGIC) ||
870 (file_magic.format_magic != FORMAT_MAGIC) ||
871 strcmp(version, VERSION)) {
873 /* sar and sadc commands are not consistent */
874 fprintf(stderr, _("Invalid data format\n"));
876 if (!rc && (file_magic.sysstat_magic == SYSSTAT_MAGIC)) {
878 _("Using a wrong data collector from a different sysstat version\n"));
885 * No need to take into account file_magic.header_size. We are sure that
886 * sadc and sar are from the same version (we have checked FORMAT_MAGIC
887 * but also VERSION above) and thus the size of file_header is FILE_HEADER_SIZE.
889 if (sa_read(&file_hdr, FILE_HEADER_SIZE)) {
893 /* Read activity list */
894 for (i = 0; i < file_hdr.sa_act_nr; i++) {
896 if (sa_read(&file_act, FILE_ACTIVITY_SIZE)) {
900 p = get_activity_position(act, file_act.id, RESUME_IF_NOT_FOUND);
902 if ((p < 0) || (act[p]->fsize != file_act.size)
905 || (act[p]->magic != file_act.magic)) {
906 /* Remember that we are reading data from sadc and not from a file... */
907 fprintf(stderr, _("Inconsistent input data\n"));
911 id_seq[i] = file_act.id; /* We necessarily have "i < NR_ACT" */
912 act[p]->nr = file_act.nr;
913 act[p]->nr2 = file_act.nr2;
920 /* Check that all selected activties are actually sent by sadc */
921 reverse_check_act(file_hdr.sa_act_nr);
925 ***************************************************************************
926 * Read statistics from a system activity data file.
929 * @from_file Input file name.
930 ***************************************************************************
932 void read_stats_from_file(char from_file[])
934 struct file_magic file_magic;
935 struct file_activity *file_actlst = NULL;
938 int rows, eosaf = TRUE, reset = FALSE;
942 /* Get window size */
943 rows = get_win_height();
945 /* Read file headers and activity list */
946 check_file_actlst(&ifd, from_file, act, &file_magic, &file_hdr,
947 &file_actlst, id_seq, FALSE);
949 /* Perform required allocations */
950 allocate_structures(act);
952 /* Print report header */
953 print_report_hdr(flags, &rectime, &file_hdr,
954 act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr);
956 /* Read system statistics from file */
959 * If this record is a special (RESTART or COMMENT) one, print it and
960 * (try to) get another one.
963 if (sa_fread(ifd, &record_hdr[0], RECORD_HEADER_SIZE, SOFT_SIZE))
964 /* End of sa data file */
967 rtype = record_hdr[0].record_type;
968 if ((rtype == R_RESTART) || (rtype == R_COMMENT)) {
969 sar_print_special(0, tm_start.use, tm_end.use, rtype,
970 ifd, from_file, &file_magic);
974 * OK: Previous record was not a special one.
975 * So read now the extra fields.
977 read_file_stat_bunch(act, 0, ifd, file_hdr.sa_act_nr,
979 if (sar_get_record_timestamp_struct(0))
981 * An error was detected.
982 * The timestamp hasn't been updated.
987 while ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
988 (tm_start.use && (datecmp(&rectime, &tm_start) < 0)) ||
989 (tm_end.use && (datecmp(&rectime, &tm_end) >=0)));
991 /* Save the first stats collected. Will be used to compute the average */
992 copy_structures(act, id_seq, record_hdr, 2, 0);
994 reset = TRUE; /* Set flag to reset last_uptime variable */
996 /* Save current file position */
997 if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) {
1003 * Read and write stats located between two possible Linux restarts.
1004 * Activities that should be displayed are saved in id_seq[] array.
1006 for (i = 0; i < NR_ACT; i++) {
1011 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
1012 if (!IS_SELECTED(act[p]->options))
1015 if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) {
1016 handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, rows,
1017 act[p]->id, &reset, file_actlst,
1018 from_file, &file_magic);
1021 unsigned int optf, msk;
1023 optf = act[p]->opt_flags;
1025 for (msk = 1; msk < 0x100; msk <<= 1) {
1026 if ((act[p]->opt_flags & 0xff) & msk) {
1027 act[p]->opt_flags &= (0xffffff00 + msk);
1029 handle_curr_act_stats(ifd, fpos, &curr, &cnt,
1030 &eosaf, rows, act[p]->id,
1031 &reset, file_actlst,
1032 from_file, &file_magic);
1033 act[p]->opt_flags = optf;
1040 /* Go to next Linux restart, if possible */
1042 eosaf = sa_fread(ifd, &record_hdr[curr], RECORD_HEADER_SIZE,
1044 rtype = record_hdr[curr].record_type;
1045 if (!eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
1046 read_file_stat_bunch(act, curr, ifd, file_hdr.sa_act_nr,
1049 else if (!eosaf && (rtype == R_COMMENT)) {
1050 /* This was a COMMENT record: print it */
1051 sar_print_special(curr, tm_start.use, tm_end.use, R_COMMENT,
1052 ifd, from_file, &file_magic);
1055 while (!eosaf && (rtype != R_RESTART));
1058 /* The last record we read was a RESTART one: Print it */
1059 if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) {
1060 sar_print_special(curr, tm_start.use, tm_end.use, R_RESTART,
1061 ifd, from_file, &file_magic);
1072 ***************************************************************************
1073 * Read statistics sent by sadc, the data collector.
1074 ***************************************************************************
1076 void read_stats(void)
1079 unsigned long lines;
1083 /* Don't buffer data if redirected to a pipe... */
1084 setbuf(stdout, NULL);
1086 /* Read stats header */
1089 if (!get_activity_nr(act, AO_SELECTED, COUNT_ACTIVITIES)) {
1090 fprintf(stderr, _("Requested activities not available\n"));
1094 /* Determine if a stat line header has to be displayed */
1095 dis_hdr = check_line_hdr();
1097 lines = rows = get_win_height();
1099 /* Perform required allocations */
1100 allocate_structures(act);
1102 /* Print report header */
1103 print_report_hdr(flags, &rectime, &file_hdr,
1104 act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr);
1106 /* Read system statistics sent by the data collector */
1107 read_sadc_stat_bunch(0);
1110 /* Display stats since boot time and exit */
1111 write_stats_startup(0);
1114 /* Save the first stats collected. Will be used to compute the average */
1115 copy_structures(act, id_seq, record_hdr, 2, 0);
1117 /* Set a handler for SIGINT */
1118 memset(&int_act, 0, sizeof(int_act));
1119 int_act.sa_handler = int_handler;
1120 int_act.sa_flags = SA_RESTART;
1121 sigaction(SIGINT, &int_act, NULL);
1127 read_sadc_stat_bunch(curr);
1137 write_stats(curr, USE_SADC, &count, NO_TM_START, tm_end.use,
1138 NO_RESET, ALL_ACTIVITIES, TRUE);
1140 if (record_hdr[curr].record_type == R_LAST_STATS) {
1141 /* File rotation is happening: Re-read header data sent by sadc */
1143 allocate_structures(act);
1150 if (sigint_caught) {
1151 /* SIGINT signal caught => Display average stats */
1161 /* Print statistics average */
1163 write_stats_avg(curr, USE_SADC, ALL_ACTIVITIES);
1167 ***************************************************************************
1168 * Main entry to the sar program.
1169 ***************************************************************************
1171 int main(int argc, char **argv)
1173 int i, rc, opt = 1, args_idx = 2;
1176 char from_file[MAX_FILE_LEN], to_file[MAX_FILE_LEN];
1182 /* Compute page shift in kB */
1185 from_file[0] = to_file[0] = '\0';
1188 /* Init National Language Support */
1192 tm_start.use = tm_end.use = FALSE;
1194 /* Allocate and init activity bitmaps */
1195 allocate_bitmaps(act);
1199 /* Process options */
1200 while (opt < argc) {
1202 if (!strcmp(argv[opt], "--sadc")) {
1207 else if (!strcmp(argv[opt], "-I")) {
1209 /* Parse -I option */
1210 if (parse_sar_I_opt(argv, &opt, act)) {
1219 else if (!strcmp(argv[opt], "-D")) {
1220 /* Option to tell sar to write to saYYYYMMDD data files */
1221 flags |= S_F_SA_YYYYMMDD;
1225 else if (!strcmp(argv[opt], "-P")) {
1226 /* Parse -P option */
1227 if (parse_sa_P_opt(argv, &opt, &flags, act)) {
1232 else if (!strcmp(argv[opt], "-o")) {
1234 /* Output file already specified */
1237 /* Save stats to a file */
1238 if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1239 (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1240 strncpy(to_file, argv[opt++], MAX_FILE_LEN);
1241 to_file[MAX_FILE_LEN - 1] = '\0';
1244 strcpy(to_file, "-");
1248 else if (!strcmp(argv[opt], "-f")) {
1249 if (from_file[0] || day_offset) {
1250 /* Input file already specified */
1253 /* Read stats from a file */
1254 if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1255 (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1256 strncpy(from_file, argv[opt++], MAX_FILE_LEN);
1257 from_file[MAX_FILE_LEN - 1] = '\0';
1258 /* Check if this is an alternate directory for sa files */
1259 check_alt_sa_dir(from_file, day_offset, -1);
1262 set_default_file(from_file, day_offset, -1);
1266 else if (!strcmp(argv[opt], "-s")) {
1267 /* Get time start */
1268 if (parse_timestamp(argv, &opt, &tm_start, DEF_TMSTART)) {
1273 else if (!strcmp(argv[opt], "-e")) {
1275 if (parse_timestamp(argv, &opt, &tm_end, DEF_TMEND)) {
1280 else if (!strcmp(argv[opt], "-h")) {
1281 /* Display help message */
1282 display_help(argv[0]);
1285 else if (!strcmp(argv[opt], "-i")) {
1286 if (!argv[++opt] || (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1289 interval = atol(argv[opt++]);
1293 flags |= S_F_INTERVAL_SET;
1296 else if (!strcmp(argv[opt], "-m")) {
1298 /* Parse option -m */
1299 if (parse_sar_m_opt(argv, &opt, act)) {
1308 else if (!strcmp(argv[opt], "-n")) {
1310 /* Parse option -n */
1311 if (parse_sar_n_opt(argv, &opt, act)) {
1320 else if ((strlen(argv[opt]) > 1) &&
1321 (strlen(argv[opt]) < 4) &&
1322 !strncmp(argv[opt], "-", 1) &&
1323 (strspn(argv[opt] + 1, DIGITS) == (strlen(argv[opt]) - 1))) {
1324 if (from_file[0] || day_offset) {
1325 /* Input file already specified */
1328 day_offset = atoi(argv[opt++] + 1);
1331 else if (!strncmp(argv[opt], "-", 1)) {
1332 /* Other options not previously tested */
1333 if ((rc = parse_sar_opt(argv, &opt, act, &flags, C_SAR)) != 0) {
1342 else if (interval < 0) {
1344 if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1347 interval = atol(argv[opt++]);
1354 /* Get count value */
1355 if ((strspn(argv[opt], DIGITS) != strlen(argv[opt])) ||
1360 /* Count parameter already set */
1363 count = atol(argv[opt++]);
1370 /* 'sar' is equivalent to 'sar -f' */
1372 ((interval < 0) && !from_file[0] && !to_file[0])) {
1373 set_default_file(from_file, day_offset, -1);
1376 if (tm_start.use && tm_end.use && (tm_end.tm_hour < tm_start.tm_hour)) {
1377 tm_end.tm_hour += 24;
1381 * Check option dependencies.
1383 /* You read from a file OR you write to it... */
1384 if (from_file[0] && to_file[0]) {
1385 fprintf(stderr, _("-f and -o options are mutually exclusive\n"));
1388 /* Use time start or option -i only when reading stats from a file */
1389 if ((tm_start.use || INTERVAL_SET(flags)) && !from_file[0]) {
1391 _("Not reading from a system activity file (use -f option)\n"));
1394 /* Don't print stats since boot time if -o or -f options are used */
1395 if (!interval && (from_file[0] || to_file[0])) {
1399 /* Cannot enter a day shift with -o option */
1400 if (to_file[0] && day_offset) {
1404 if (USE_PRETTY_OPTION(flags)) {
1405 dm_major = get_devmap_major();
1410 * count parameter not set: Display all the contents of the file
1411 * or generate a report continuously.
1416 /* Default is CPU activity... */
1417 select_default_activity(act);
1419 /* Reading stats from file: */
1425 /* Read stats from file */
1426 read_stats_from_file(from_file);
1428 /* Free stuctures and activity bitmaps */
1430 free_structures(act);
1435 /* Reading stats from sadc: */
1437 /* Create anonymous pipe */
1438 if (pipe(fd) == -1) {
1451 if (dup2(fd[1], STDOUT_FILENO) < 0) {
1458 * Prepare options for sadc.
1463 /* Interval value */
1467 else if (!interval) {
1471 sprintf(ltemp, "%ld", interval);
1477 sprintf(ltemp, "%ld", count + 1);
1478 salloc(args_idx++, ltemp);
1481 /* Flags to be passed to sadc */
1482 salloc(args_idx++, "-z");
1484 /* Writing data to a file (option -o) */
1486 /* Set option -D if entered */
1487 if (USE_SA_YYYYMMDD(flags)) {
1488 salloc(args_idx++, "-D");
1490 /* Collect all possible activities (option -S XALL for sadc) */
1491 salloc(args_idx++, "-S");
1492 salloc(args_idx++, K_XALL);
1494 salloc(args_idx++, to_file);
1498 * If option -o hasn't been used, then tell sadc
1499 * to collect only activities that will be displayed.
1503 for (i = 0; i < NR_ACT; i++) {
1504 if (IS_SELECTED(act[i]->options)) {
1505 act_id |= act[i]->group;
1510 snprintf(ltemp, 19, "%d", act_id);
1512 salloc(args_idx++, "-S");
1513 salloc(args_idx++, ltemp);
1517 /* Last arg is NULL */
1518 args[args_idx] = NULL;
1520 /* Call now the data collector */
1521 execv(SADC_PATH, args);
1524 * Note: Don't use execl/execlp since we don't have a fixed number of
1525 * args to give to sadc.
1527 fprintf(stderr, _("Cannot find the data collector (%s)\n"), SADC);
1532 default: /* Parent */
1533 if (dup2(fd[0], STDIN_FILENO) < 0) {
1539 /* Get now the statistics */
1545 /* Free structures and activity bitmaps */
1547 free_structures(act);