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 printf(_("\t-q\tQueue length and load average statistics\n"));
173 printf(_("\t-R\tMemory statistics\n"));
174 printf(_("\t-r [ ALL ]\n"
175 "\t\tMemory utilization statistics\n"));
176 printf(_("\t-S\tSwap space utilization statistics\n"));
177 printf(_("\t-u [ ALL ]\n"
178 "\t\tCPU utilization statistics\n"));
179 printf(_("\t-v\tKernel tables statistics\n"));
180 printf(_("\t-W\tSwapping statistics\n"));
181 printf(_("\t-w\tTask creation and system switching statistics\n"));
182 printf(_("\t-y\tTTY devices statistics\n"));
187 ***************************************************************************
188 * Give a hint to the user about where is located the data collector.
189 ***************************************************************************
191 void which_sadc(void)
195 if (stat(SADC_PATH, &buf) < 0) {
196 printf(_("Data collector will be sought in PATH\n"));
199 printf(_("Data collector found: %s\n"), SADC_PATH);
206 ***************************************************************************
207 * SIGINT signal handler.
210 * @sig Signal number.
211 ***************************************************************************
213 void int_handler(int sig)
216 printf("\n"); /* Skip "^C" displayed on screen */
221 ***************************************************************************
222 * Init some structures.
223 ***************************************************************************
225 void init_structures(void)
229 for (i = 0; i < 3; i++)
230 memset(&record_hdr[i], 0, RECORD_HEADER_SIZE);
234 ***************************************************************************
235 * Allocate memory for sadc args.
238 * @i Argument number.
239 * @ltemp Argument value.
240 ***************************************************************************
242 void salloc(int i, char *ltemp)
244 if ((args[i] = (char *) malloc(strlen(ltemp) + 1)) == NULL) {
248 strcpy(args[i], ltemp);
252 ***************************************************************************
253 * Display an error message. Happens when the data collector doesn't send
255 ***************************************************************************
257 void print_read_error(void)
259 fprintf(stderr, _("End of data collecting unexpected\n"));
264 ***************************************************************************
265 * Check that every selected activity actually belongs to the sequence list.
266 * If not, then the activity should be unselected since it will not be sent
267 * by sadc. An activity can be not sent if its number of items is null.
270 * @act_nr Size of sequence list.
271 ***************************************************************************
273 void reverse_check_act(unsigned int act_nr)
277 for (i = 0; i < NR_ACT; i++) {
279 if (IS_SELECTED(act[i]->options)) {
281 for (j = 0; j < act_nr; j++) {
282 if (id_seq[j] == act[i]->id)
286 act[i]->options &= ~AO_SELECTED;
292 ***************************************************************************
293 * Fill the (struct tm) rectime structure with current record's time,
294 * based on current record's time data saved in file.
295 * The resulting timestamp is expressed in the locale of the file creator
296 * or in the user's own locale depending on whether option -t has been used
300 * @curr Index in array for current sample statistics.
303 * 1 if an error was detected, or 0 otherwise.
304 ***************************************************************************
306 int sar_get_record_timestamp_struct(int curr)
310 /* Check if option -t was specified on the command line */
311 if (PRINT_TRUE_TIME(flags)) {
313 rectime.tm_hour = record_hdr[curr].hour;
314 rectime.tm_min = record_hdr[curr].minute;
315 rectime.tm_sec = record_hdr[curr].second;
318 if ((ltm = localtime((const time_t *) &record_hdr[curr].ust_time)) == NULL)
320 * An error was detected.
321 * The rectime structure has NOT been updated.
332 ***************************************************************************
333 * Determine if a stat header line has to be displayed.
336 * TRUE if a header line has to be displayed.
337 ***************************************************************************
339 int check_line_hdr(void)
343 /* Get number of options entered on the command line */
344 if (get_activity_nr(act, AO_SELECTED, COUNT_OUTPUTS) > 1)
347 for (i = 0; i < NR_ACT; i++) {
348 if (IS_SELECTED(act[i]->options)) {
349 /* Special processing for activities using a bitmap */
350 if (act[i]->bitmap) {
351 if (count_bits(act[i]->bitmap->b_array,
352 BITMAP_SIZE(act[i]->bitmap->b_size)) > 1) {
356 else if (act[i]->nr > 1) {
359 /* Stop now since we have only one selected activity */
368 ***************************************************************************
369 * Set current record's timestamp string.
372 * @curr Index in array for current sample statistics.
373 * @len Maximum length of timestamp string.
376 * @cur_time Timestamp string.
379 * 1 if an error was detected, or 0 otherwise.
380 ***************************************************************************
382 int set_record_timestamp_string(int curr, char *cur_time, int len)
384 /* Fill timestamp structure */
385 if (sar_get_record_timestamp_struct(curr))
389 /* Set cur_time date value */
390 strftime(cur_time, len, "%X", &rectime);
396 ***************************************************************************
397 * Print statistics average.
400 * @curr Index in array for current sample statistics.
401 * @read_from_file Set to TRUE if stats are read from a system activity
403 * @act_id Activity that can be displayed, or ~0 for all.
404 * Remember that when reading stats from a file, only
405 * one activity can be displayed at a time.
406 ***************************************************************************
408 void write_stats_avg(int curr, int read_from_file, unsigned int act_id)
411 unsigned long long itv, g_itv;
412 static __nr_t cpu_nr = -1;
415 cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr;
417 /* Interval value in jiffies */
418 g_itv = get_interval(record_hdr[2].uptime, record_hdr[curr].uptime);
421 itv = get_interval(record_hdr[2].uptime0, record_hdr[curr].uptime0);
425 strncpy(timestamp[curr], _("Average:"), TIMESTAMP_LEN);
426 timestamp[curr][TIMESTAMP_LEN - 1] = '\0';
427 strcpy(timestamp[!curr], timestamp[curr]);
430 TEST_STDOUT(STDOUT_FILENO);
432 for (i = 0; i < NR_ACT; i++) {
434 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
437 if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
438 /* Display current average activity statistics */
439 (*act[i]->f_print_avg)(act[i], 2, curr,
440 NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv);
444 if (read_from_file) {
446 * Reset number of lines printed only if we read stats
447 * from a system activity file.
454 ***************************************************************************
455 * Print system statistics.
458 * @curr Index in array for current sample statistics.
459 * @read_from_file Set to TRUE if stats are read from a system activity
461 * @use_tm_start Set to TRUE if option -s has been used.
462 * @use_tm_end Set to TRUE if option -e has been used.
463 * @reset Set to TRUE if last_uptime variable should be
464 * reinitialized (used in next_slice() function).
465 * @act_id Activity that can be displayed or ~0 for all.
466 * Remember that when reading stats from a file, only
467 * one activity can be displayed at a time.
468 * @reset_cd TRUE if static cross_day variable should be reset
472 * @cnt Number of remaining lines to display.
475 * 1 if stats have been successfully displayed, and 0 otherwise.
476 ***************************************************************************
478 int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start,
479 int use_tm_end, int reset, unsigned int act_id, int reset_cd)
482 unsigned long long itv, g_itv;
483 static int cross_day = 0;
484 static __nr_t cpu_nr = -1;
487 cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr;
491 * cross_day is a static variable that is set to 1 when the first
492 * record of stats from a new day is read from a unique data file
493 * (in the case where the file contains data from two consecutive
494 * days). When set to 1, every following records timestamp will
495 * have its hour value increased by 24.
496 * Yet when a new activity (being read from the file) is going to
497 * be displayed, we start reading the file from the beginning
498 * again, and so cross_day should be reset in this case.
504 if (read_from_file) {
505 if (!next_slice(record_hdr[2].uptime0, record_hdr[curr].uptime0,
507 /* Not close enough to desired interval */
511 /* Set previous timestamp */
512 if (set_record_timestamp_string(!curr, timestamp[!curr], 16))
514 /* Set current timestamp */
515 if (set_record_timestamp_string(curr, timestamp[curr], 16))
518 /* Check if we are beginning a new day */
519 if (use_tm_start && record_hdr[!curr].ust_time &&
520 (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) &&
521 (record_hdr[curr].hour < record_hdr[!curr].hour)) {
527 * This is necessary if we want to properly handle something like:
528 * sar -s time_start -e time_end with
529 * time_start(day D) > time_end(day D+1)
531 rectime.tm_hour +=24;
535 if (use_tm_start && (datecmp(&rectime, &tm_start) < 0))
536 /* it's too soon... */
539 /* Get interval values */
540 get_itv_value(&record_hdr[curr], &record_hdr[!curr],
541 cpu_nr, &itv, &g_itv);
544 if (use_tm_end && (datecmp(&rectime, &tm_end) > 0)) {
545 /* It's too late... */
553 TEST_STDOUT(STDOUT_FILENO);
555 for (i = 0; i < NR_ACT; i++) {
557 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
560 if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
561 /* Display current activity statistics */
562 (*act[i]->f_print)(act[i], !curr, curr,
563 NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv);
571 ***************************************************************************
572 * Display stats since system startup.
575 * @curr Index in array for current sample statistics.
576 ***************************************************************************
578 void write_stats_startup(int curr)
582 /* Set to 0 previous structures corresponding to boot time */
583 memset(&record_hdr[!curr], 0, RECORD_HEADER_SIZE);
584 record_hdr[!curr].record_type = R_STATS;
585 record_hdr[!curr].hour = record_hdr[curr].hour;
586 record_hdr[!curr].minute = record_hdr[curr].minute;
587 record_hdr[!curr].second = record_hdr[curr].second;
588 record_hdr[!curr].ust_time = record_hdr[curr].ust_time;
590 for (i = 0; i < NR_ACT; i++) {
591 if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
592 memset(act[i]->buf[!curr], 0,
593 (size_t) act[i]->msize * (size_t) act[i]->nr * (size_t) act[i]->nr2);
597 flags |= S_F_SINCE_BOOT;
600 write_stats(curr, USE_SADC, &count, NO_TM_START, NO_TM_END, NO_RESET,
601 ALL_ACTIVITIES, TRUE);
607 ***************************************************************************
608 * Read data sent by the data collector.
611 * @size Number of bytes of data to read.
614 * @buffer Buffer where data will be saved.
617 * 1 if end of file has been reached, 0 otherwise.
618 ***************************************************************************
620 int sa_read(void *buffer, int size)
626 if ((n = read(STDIN_FILENO, buffer, size)) < 0) {
635 buffer = (char *) buffer + n;
642 ***************************************************************************
643 * Print a Linux restart message (contents of a RESTART record) or a
644 * comment (contents of a COMMENT record).
647 * @curr Index in array for current sample statistics.
648 * @use_tm_start Set to TRUE if option -s has been used.
649 * @use_tm_end Set to TRUE if option -e has been used.
650 * @rtype Record type to display.
651 * @ifd Input file descriptor.
652 * @file Name of file being read.
653 * @file_magic file_magic structure filled with file magic header
657 * 1 if the record has been successfully displayed, and 0 otherwise.
658 ***************************************************************************
660 int sar_print_special(int curr, int use_tm_start, int use_tm_end, int rtype,
661 int ifd, char *file, struct file_magic *file_magic)
665 unsigned int new_cpu_nr;
667 if (set_record_timestamp_string(curr, cur_time, 26))
670 /* The record must be in the interval specified by -s/-e options */
671 if ((use_tm_start && (datecmp(&rectime, &tm_start) < 0)) ||
672 (use_tm_end && (datecmp(&rectime, &tm_end) > 0))) {
676 if (rtype == R_RESTART) {
677 /* Don't forget to read the volatile activities structures */
678 new_cpu_nr = read_vol_act_structures(ifd, act, file, file_magic,
679 file_hdr.sa_vol_act_nr);
682 printf("\n%-11s LINUX RESTART\t(%d CPU)\n",
683 cur_time, new_cpu_nr > 1 ? new_cpu_nr - 1 : 1);
687 else if (rtype == R_COMMENT) {
688 char file_comment[MAX_COMMENT_LEN];
690 /* Don't forget to read comment record even if it won't be displayed... */
691 sa_fread(ifd, file_comment, MAX_COMMENT_LEN, HARD_SIZE);
692 file_comment[MAX_COMMENT_LEN - 1] = '\0';
694 if (dp && DISPLAY_COMMENT(flags)) {
695 printf("%-11s COM %s\n", cur_time, file_comment);
704 ***************************************************************************
705 * Read the various statistics sent by the data collector (sadc).
708 * @curr Index in array for current sample statistics.
709 ***************************************************************************
711 void read_sadc_stat_bunch(int curr)
715 /* Read record header (type is always R_STATS since it is read from sadc) */
716 if (sa_read(&record_hdr[curr], RECORD_HEADER_SIZE)) {
720 for (i = 0; i < NR_ACT; i++) {
724 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
726 if (sa_read(act[p]->buf[curr], act[p]->fsize * act[p]->nr * act[p]->nr2)) {
733 ***************************************************************************
734 * Read stats for current activity from file and display them.
737 * @ifd Input file descriptor.
738 * @fpos Position in file where reading must start.
739 * @curr Index in array for current sample statistics.
740 * @rows Number of rows of screen.
741 * @act_id Activity to display.
742 * @file_actlst List of activities in file.
743 * @file Name of file being read.
744 * @file_magic file_magic structure filled with file magic header data.
747 * @curr Index in array for next sample statistics.
748 * @cnt Number of remaining lines of stats to write.
749 * @eosaf Set to TRUE if EOF (end of file) has been reached.
750 * @reset Set to TRUE if last_uptime variable should be
751 * reinitialized (used in next_slice() function).
752 ***************************************************************************
754 void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf,
755 int rows, unsigned int act_id, int *reset,
756 struct file_activity *file_actlst, char *file,
757 struct file_magic *file_magic)
760 unsigned long lines = 0;
762 int davg = 0, next, inc;
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 */
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));
790 /* Display count lines of stats */
791 *eosaf = sa_fread(ifd, &record_hdr[*curr],
792 RECORD_HEADER_SIZE, SOFT_SIZE);
793 rtype = record_hdr[*curr].record_type;
795 if (!*eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
796 /* Read the extra fields since it's not a special record */
797 read_file_stat_bunch(act, *curr, ifd, file_hdr.sa_act_nr, file_actlst);
800 if ((lines >= rows) || !lines) {
807 if (!*eosaf && (rtype != R_RESTART)) {
809 if (rtype == R_COMMENT) {
810 /* Display comment */
811 next = sar_print_special(*curr, tm_start.use, tm_end.use,
812 R_COMMENT, ifd, file, file_magic);
814 /* A line of comment was actually displayed */
820 /* next is set to 1 when we were close enough to desired interval */
821 next = write_stats(*curr, USE_SA_FILE, cnt, tm_start.use, tm_end.use,
822 *reset, act_id, reset_cd);
824 if (next && (*cnt > 0)) {
835 while (*cnt && !*eosaf && (rtype != R_RESTART));
838 write_stats_avg(!*curr, USE_SA_FILE, act_id);
845 ***************************************************************************
846 * Read header data sent by sadc.
847 ***************************************************************************
849 void read_header_data(void)
851 struct file_magic file_magic;
852 struct file_activity file_act;
856 /* Read magic header */
857 rc = sa_read(&file_magic, FILE_MAGIC_SIZE);
859 sprintf(version, "%d.%d.%d.%d",
860 file_magic.sysstat_version,
861 file_magic.sysstat_patchlevel,
862 file_magic.sysstat_sublevel,
863 file_magic.sysstat_extraversion);
864 if (!file_magic.sysstat_extraversion) {
865 version[strlen(version) - 2] = '\0';
868 if (rc || (file_magic.sysstat_magic != SYSSTAT_MAGIC) ||
869 (file_magic.format_magic != FORMAT_MAGIC) ||
870 strcmp(version, VERSION)) {
872 /* sar and sadc commands are not consistent */
873 fprintf(stderr, _("Invalid data format\n"));
875 if (!rc && (file_magic.sysstat_magic == SYSSTAT_MAGIC)) {
877 _("Using a wrong data collector from a different sysstat version\n"));
884 * No need to take into account file_magic.header_size. We are sure that
885 * sadc and sar are from the same version (we have checked FORMAT_MAGIC
886 * but also VERSION above) and thus the size of file_header is FILE_HEADER_SIZE.
888 if (sa_read(&file_hdr, FILE_HEADER_SIZE)) {
892 /* Read activity list */
893 for (i = 0; i < file_hdr.sa_act_nr; i++) {
895 if (sa_read(&file_act, FILE_ACTIVITY_SIZE)) {
899 p = get_activity_position(act, file_act.id, RESUME_IF_NOT_FOUND);
901 if ((p < 0) || (act[p]->fsize != file_act.size)
904 || (act[p]->magic != file_act.magic)) {
905 /* Remember that we are reading data from sadc and not from a file... */
906 fprintf(stderr, _("Inconsistent input data\n"));
910 id_seq[i] = file_act.id; /* We necessarily have "i < NR_ACT" */
911 act[p]->nr = file_act.nr;
912 act[p]->nr2 = file_act.nr2;
919 /* Check that all selected activties are actually sent by sadc */
920 reverse_check_act(file_hdr.sa_act_nr);
924 ***************************************************************************
925 * Read statistics from a system activity data file.
928 * @from_file Input file name.
929 ***************************************************************************
931 void read_stats_from_file(char from_file[])
933 struct file_magic file_magic;
934 struct file_activity *file_actlst = NULL;
937 int rows, eosaf = TRUE, reset = FALSE;
941 /* Get window size */
942 rows = get_win_height();
944 /* Read file headers and activity list */
945 check_file_actlst(&ifd, from_file, act, &file_magic, &file_hdr,
946 &file_actlst, id_seq, FALSE);
948 /* Perform required allocations */
949 allocate_structures(act);
951 /* Print report header */
952 print_report_hdr(flags, &rectime, &file_hdr,
953 act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr);
955 /* Read system statistics from file */
958 * If this record is a special (RESTART or COMMENT) one, print it and
959 * (try to) get another one.
962 if (sa_fread(ifd, &record_hdr[0], RECORD_HEADER_SIZE, SOFT_SIZE))
963 /* End of sa data file */
966 rtype = record_hdr[0].record_type;
967 if ((rtype == R_RESTART) || (rtype == R_COMMENT)) {
968 sar_print_special(0, tm_start.use, tm_end.use, rtype,
969 ifd, from_file, &file_magic);
973 * OK: Previous record was not a special one.
974 * So read now the extra fields.
976 read_file_stat_bunch(act, 0, ifd, file_hdr.sa_act_nr,
978 if (sar_get_record_timestamp_struct(0))
980 * An error was detected.
981 * The timestamp hasn't been updated.
986 while ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
987 (tm_start.use && (datecmp(&rectime, &tm_start) < 0)) ||
988 (tm_end.use && (datecmp(&rectime, &tm_end) >=0)));
990 /* Save the first stats collected. Will be used to compute the average */
991 copy_structures(act, id_seq, record_hdr, 2, 0);
993 reset = TRUE; /* Set flag to reset last_uptime variable */
995 /* Save current file position */
996 if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) {
1002 * Read and write stats located between two possible Linux restarts.
1003 * Activities that should be displayed are saved in id_seq[] array.
1005 for (i = 0; i < NR_ACT; i++) {
1010 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
1011 if (!IS_SELECTED(act[p]->options))
1014 if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) {
1015 handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, rows,
1016 act[p]->id, &reset, file_actlst,
1017 from_file, &file_magic);
1020 unsigned int optf, msk;
1022 optf = act[p]->opt_flags;
1024 for (msk = 1; msk < 0x100; msk <<= 1) {
1025 if ((act[p]->opt_flags & 0xff) & msk) {
1026 act[p]->opt_flags &= (0xffffff00 + msk);
1028 handle_curr_act_stats(ifd, fpos, &curr, &cnt,
1029 &eosaf, rows, act[p]->id,
1030 &reset, file_actlst,
1031 from_file, &file_magic);
1032 act[p]->opt_flags = optf;
1039 /* Go to next Linux restart, if possible */
1041 eosaf = sa_fread(ifd, &record_hdr[curr], RECORD_HEADER_SIZE,
1043 rtype = record_hdr[curr].record_type;
1044 if (!eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
1045 read_file_stat_bunch(act, curr, ifd, file_hdr.sa_act_nr,
1048 else if (!eosaf && (rtype == R_COMMENT)) {
1049 /* This was a COMMENT record: print it */
1050 sar_print_special(curr, tm_start.use, tm_end.use, R_COMMENT,
1051 ifd, from_file, &file_magic);
1054 while (!eosaf && (rtype != R_RESTART));
1057 /* The last record we read was a RESTART one: Print it */
1058 if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) {
1059 sar_print_special(curr, tm_start.use, tm_end.use, R_RESTART,
1060 ifd, from_file, &file_magic);
1071 ***************************************************************************
1072 * Read statistics sent by sadc, the data collector.
1073 ***************************************************************************
1075 void read_stats(void)
1078 unsigned long lines;
1082 /* Don't buffer data if redirected to a pipe... */
1083 setbuf(stdout, NULL);
1085 /* Read stats header */
1088 if (!get_activity_nr(act, AO_SELECTED, COUNT_ACTIVITIES)) {
1089 fprintf(stderr, _("Requested activities not available\n"));
1093 /* Determine if a stat line header has to be displayed */
1094 dis_hdr = check_line_hdr();
1096 lines = rows = get_win_height();
1098 /* Perform required allocations */
1099 allocate_structures(act);
1101 /* Print report header */
1102 print_report_hdr(flags, &rectime, &file_hdr,
1103 act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr);
1105 /* Read system statistics sent by the data collector */
1106 read_sadc_stat_bunch(0);
1109 /* Display stats since boot time and exit */
1110 write_stats_startup(0);
1113 /* Save the first stats collected. Will be used to compute the average */
1114 copy_structures(act, id_seq, record_hdr, 2, 0);
1116 /* Set a handler for SIGINT */
1117 memset(&int_act, 0, sizeof(int_act));
1118 int_act.sa_handler = int_handler;
1119 int_act.sa_flags = SA_RESTART;
1120 sigaction(SIGINT, &int_act, NULL);
1126 read_sadc_stat_bunch(curr);
1136 write_stats(curr, USE_SADC, &count, NO_TM_START, tm_end.use,
1137 NO_RESET, ALL_ACTIVITIES, TRUE);
1139 if (record_hdr[curr].record_type == R_LAST_STATS) {
1140 /* File rotation is happening: Re-read header data sent by sadc */
1142 allocate_structures(act);
1149 if (sigint_caught) {
1150 /* SIGINT signal caught => Display average stats */
1160 /* Print statistics average */
1162 write_stats_avg(curr, USE_SADC, ALL_ACTIVITIES);
1166 ***************************************************************************
1167 * Main entry to the sar program.
1168 ***************************************************************************
1170 int main(int argc, char **argv)
1172 int i, rc, opt = 1, args_idx = 2;
1175 char from_file[MAX_FILE_LEN], to_file[MAX_FILE_LEN];
1181 /* Compute page shift in kB */
1184 from_file[0] = to_file[0] = '\0';
1187 /* Init National Language Support */
1191 tm_start.use = tm_end.use = FALSE;
1193 /* Allocate and init activity bitmaps */
1194 allocate_bitmaps(act);
1198 /* Process options */
1199 while (opt < argc) {
1201 if (!strcmp(argv[opt], "--sadc")) {
1206 else if (!strcmp(argv[opt], "-I")) {
1208 /* Parse -I option */
1209 if (parse_sar_I_opt(argv, &opt, act)) {
1218 else if (!strcmp(argv[opt], "-D")) {
1219 /* Option to tell sar to write to saYYYYMMDD data files */
1220 flags |= S_F_SA_YYYYMMDD;
1224 else if (!strcmp(argv[opt], "-P")) {
1225 /* Parse -P option */
1226 if (parse_sa_P_opt(argv, &opt, &flags, act)) {
1231 else if (!strcmp(argv[opt], "-o")) {
1233 /* Output file already specified */
1236 /* Save stats to a file */
1237 if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1238 (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1239 strncpy(to_file, argv[opt++], MAX_FILE_LEN);
1240 to_file[MAX_FILE_LEN - 1] = '\0';
1243 strcpy(to_file, "-");
1247 else if (!strcmp(argv[opt], "-f")) {
1248 if (from_file[0] || day_offset) {
1249 /* Input file already specified */
1252 /* Read stats from a file */
1253 if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1254 (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1255 strncpy(from_file, argv[opt++], MAX_FILE_LEN);
1256 from_file[MAX_FILE_LEN - 1] = '\0';
1257 /* Check if this is an alternate directory for sa files */
1258 check_alt_sa_dir(from_file, day_offset, -1);
1261 set_default_file(from_file, day_offset, -1);
1265 else if (!strcmp(argv[opt], "-s")) {
1266 /* Get time start */
1267 if (parse_timestamp(argv, &opt, &tm_start, DEF_TMSTART)) {
1272 else if (!strcmp(argv[opt], "-e")) {
1274 if (parse_timestamp(argv, &opt, &tm_end, DEF_TMEND)) {
1279 else if (!strcmp(argv[opt], "-h")) {
1280 /* Display help message */
1281 display_help(argv[0]);
1284 else if (!strcmp(argv[opt], "-i")) {
1285 if (!argv[++opt] || (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1288 interval = atol(argv[opt++]);
1292 flags |= S_F_INTERVAL_SET;
1295 else if (!strcmp(argv[opt], "-m")) {
1297 /* Parse option -m */
1298 if (parse_sar_m_opt(argv, &opt, act)) {
1307 else if (!strcmp(argv[opt], "-n")) {
1309 /* Parse option -n */
1310 if (parse_sar_n_opt(argv, &opt, act)) {
1319 else if ((strlen(argv[opt]) > 1) &&
1320 (strlen(argv[opt]) < 4) &&
1321 !strncmp(argv[opt], "-", 1) &&
1322 (strspn(argv[opt] + 1, DIGITS) == (strlen(argv[opt]) - 1))) {
1323 if (from_file[0] || day_offset) {
1324 /* Input file already specified */
1327 day_offset = atoi(argv[opt++] + 1);
1330 else if (!strncmp(argv[opt], "-", 1)) {
1331 /* Other options not previously tested */
1332 if ((rc = parse_sar_opt(argv, &opt, act, &flags, C_SAR)) != 0) {
1341 else if (interval < 0) {
1343 if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1346 interval = atol(argv[opt++]);
1353 /* Get count value */
1354 if ((strspn(argv[opt], DIGITS) != strlen(argv[opt])) ||
1359 /* Count parameter already set */
1362 count = atol(argv[opt++]);
1369 /* 'sar' is equivalent to 'sar -f' */
1371 ((interval < 0) && !from_file[0] && !to_file[0])) {
1372 set_default_file(from_file, day_offset, -1);
1375 if (tm_start.use && tm_end.use && (tm_end.tm_hour < tm_start.tm_hour)) {
1376 tm_end.tm_hour += 24;
1380 * Check option dependencies.
1382 /* You read from a file OR you write to it... */
1383 if (from_file[0] && to_file[0]) {
1384 fprintf(stderr, _("-f and -o options are mutually exclusive\n"));
1387 /* Use time start or option -i only when reading stats from a file */
1388 if ((tm_start.use || INTERVAL_SET(flags)) && !from_file[0]) {
1390 _("Not reading from a system activity file (use -f option)\n"));
1393 /* Don't print stats since boot time if -o or -f options are used */
1394 if (!interval && (from_file[0] || to_file[0])) {
1398 /* Cannot enter a day shift with -o option */
1399 if (to_file[0] && day_offset) {
1403 if (USE_PRETTY_OPTION(flags)) {
1404 dm_major = get_devmap_major();
1409 * count parameter not set: Display all the contents of the file
1410 * or generate a report continuously.
1415 /* Default is CPU activity... */
1416 select_default_activity(act);
1418 /* Reading stats from file: */
1424 /* Read stats from file */
1425 read_stats_from_file(from_file);
1427 /* Free stuctures and activity bitmaps */
1429 free_structures(act);
1434 /* Reading stats from sadc: */
1436 /* Create anonymous pipe */
1437 if (pipe(fd) == -1) {
1450 if (dup2(fd[1], STDOUT_FILENO) < 0) {
1457 * Prepare options for sadc.
1462 /* Interval value */
1466 else if (!interval) {
1470 sprintf(ltemp, "%ld", interval);
1476 sprintf(ltemp, "%ld", count + 1);
1477 salloc(args_idx++, ltemp);
1480 /* Flags to be passed to sadc */
1481 salloc(args_idx++, "-z");
1483 /* Writing data to a file (option -o) */
1485 /* Set option -D if entered */
1486 if (USE_SA_YYYYMMDD(flags)) {
1487 salloc(args_idx++, "-D");
1489 /* Collect all possible activities (option -S XALL for sadc) */
1490 salloc(args_idx++, "-S");
1491 salloc(args_idx++, K_XALL);
1493 salloc(args_idx++, to_file);
1497 * If option -o hasn't been used, then tell sadc
1498 * to collect only activities that will be displayed.
1502 for (i = 0; i < NR_ACT; i++) {
1503 if (IS_SELECTED(act[i]->options)) {
1504 act_id |= act[i]->group;
1509 snprintf(ltemp, 19, "%d", act_id);
1511 salloc(args_idx++, "-S");
1512 salloc(args_idx++, ltemp);
1516 /* Last arg is NULL */
1517 args[args_idx] = NULL;
1519 /* Call now the data collector */
1520 execv(SADC_PATH, args);
1523 * Note: Don't use execl/execlp since we don't have a fixed number of
1524 * args to give to sadc.
1526 fprintf(stderr, _("Cannot find the data collector (%s)\n"), SADC);
1531 default: /* Parent */
1532 if (dup2(fd[0], STDIN_FILENO) < 0) {
1538 /* Get now the statistics */
1544 /* Free structures and activity bitmaps */
1546 free_structures(act);