2 * sar: report system activity
3 * (C) 1999-2016 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 ***************************************************************************
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[];
81 extern struct report_format sar_fmt;
83 struct sigaction int_act;
84 int sigint_caught = 0;
87 ***************************************************************************
88 * Print usage title message.
91 * @progname Name of sysstat command
92 ***************************************************************************
94 void print_usage_title(FILE *fp, char *progname)
96 fprintf(fp, _("Usage: %s [ options ] [ <interval> [ <count> ] ]\n"),
101 ***************************************************************************
102 * Print usage and exit.
105 * @progname Name of sysstat command
106 ***************************************************************************
108 void usage(char *progname)
110 print_usage_title(stderr, progname);
111 fprintf(stderr, _("Options are:\n"
112 "[ -A ] [ -B ] [ -b ] [ -C ] [ -D ] [ -d ] [ -F [ MOUNT ] ] [ -H ] [ -h ]\n"
113 "[ -p ] [ -q ] [ -R ] [ -r [ ALL ] ] [ -S ] [ -t ] [ -u [ ALL ] ] [ -V ]\n"
114 "[ -v ] [ -W ] [ -w ] [ -y ] [ --sadc ]\n"
115 "[ -I { <int> [,...] | SUM | ALL | XALL } ] [ -P { <cpu> [,...] | ALL } ]\n"
116 "[ -m { <keyword> [,...] | ALL } ] [ -n { <keyword> [,...] | ALL } ]\n"
117 "[ -j { ID | LABEL | PATH | UUID | ... } ]\n"
118 "[ -f [ <filename> ] | -o [ <filename> ] | -[0-9]+ ]\n"
119 "[ -i <interval> ] [ -s [ <hh:mm[:ss]> ] ] [ -e [ <hh:mm[:ss]> ] ]\n"));
124 ***************************************************************************
125 * Display a short help message and exit.
128 * @progname Name of sysstat command
129 ***************************************************************************
131 void display_help(char *progname)
133 print_usage_title(stdout, progname);
134 printf(_("Main options and reports:\n"));
135 printf(_("\t-B\tPaging statistics\n"));
136 printf(_("\t-b\tI/O and transfer rate statistics\n"));
137 printf(_("\t-d\tBlock devices statistics\n"));
138 printf(_("\t-F [ MOUNT ]\n"));
139 printf(_("\t\tFilesystems statistics\n"));
140 printf(_("\t-H\tHugepages utilization statistics\n"));
141 printf(_("\t-I { <int> | SUM | ALL | XALL }\n"
142 "\t\tInterrupts statistics\n"));
143 printf(_("\t-m { <keyword> [,...] | ALL }\n"
144 "\t\tPower management statistics\n"
145 "\t\tKeywords are:\n"
146 "\t\tCPU\tCPU instantaneous clock frequency\n"
147 "\t\tFAN\tFans speed\n"
148 "\t\tFREQ\tCPU average clock frequency\n"
149 "\t\tIN\tVoltage inputs\n"
150 "\t\tTEMP\tDevices temperature\n"
151 "\t\tUSB\tUSB devices plugged into the system\n"));
152 printf(_("\t-n { <keyword> [,...] | ALL }\n"
153 "\t\tNetwork statistics\n"
154 "\t\tKeywords are:\n"
155 "\t\tDEV\tNetwork interfaces\n"
156 "\t\tEDEV\tNetwork interfaces (errors)\n"
157 "\t\tNFS\tNFS client\n"
158 "\t\tNFSD\tNFS server\n"
159 "\t\tSOCK\tSockets\t(v4)\n"
160 "\t\tIP\tIP traffic\t(v4)\n"
161 "\t\tEIP\tIP traffic\t(v4) (errors)\n"
162 "\t\tICMP\tICMP traffic\t(v4)\n"
163 "\t\tEICMP\tICMP traffic\t(v4) (errors)\n"
164 "\t\tTCP\tTCP traffic\t(v4)\n"
165 "\t\tETCP\tTCP traffic\t(v4) (errors)\n"
166 "\t\tUDP\tUDP traffic\t(v4)\n"
167 "\t\tSOCK6\tSockets\t(v6)\n"
168 "\t\tIP6\tIP traffic\t(v6)\n"
169 "\t\tEIP6\tIP traffic\t(v6) (errors)\n"
170 "\t\tICMP6\tICMP traffic\t(v6)\n"
171 "\t\tEICMP6\tICMP traffic\t(v6) (errors)\n"
172 "\t\tUDP6\tUDP traffic\t(v6)\n"
173 "\t\tFC\tFibre channel HBAs\n"));
174 printf(_("\t-q\tQueue length and load average statistics\n"));
175 printf(_("\t-R\tMemory statistics\n"));
176 printf(_("\t-r [ ALL ]\n"
177 "\t\tMemory utilization statistics\n"));
178 printf(_("\t-S\tSwap space utilization statistics\n"));
179 printf(_("\t-u [ ALL ]\n"
180 "\t\tCPU utilization statistics\n"));
181 printf(_("\t-v\tKernel tables statistics\n"));
182 printf(_("\t-W\tSwapping statistics\n"));
183 printf(_("\t-w\tTask creation and system switching statistics\n"));
184 printf(_("\t-y\tTTY devices statistics\n"));
189 ***************************************************************************
190 * Give a hint to the user about where is located the data collector.
191 ***************************************************************************
193 void which_sadc(void)
197 if (stat(SADC_PATH, &buf) < 0) {
198 printf(_("Data collector will be sought in PATH\n"));
201 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 zero.
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 * Determine if a stat header line has to be displayed.
297 * TRUE if a header line has to be displayed.
298 ***************************************************************************
300 int check_line_hdr(void)
304 /* Get number of options entered on the command line */
305 if (get_activity_nr(act, AO_SELECTED, COUNT_OUTPUTS) > 1)
308 for (i = 0; i < NR_ACT; i++) {
309 if (IS_SELECTED(act[i]->options)) {
310 /* Special processing for activities using a bitmap */
311 if (act[i]->bitmap) {
312 if (count_bits(act[i]->bitmap->b_array,
313 BITMAP_SIZE(act[i]->bitmap->b_size)) > 1) {
317 else if (act[i]->nr > 1) {
320 /* Stop now since we have only one selected activity */
329 ***************************************************************************
330 * Print statistics average.
333 * @curr Index in array for current sample statistics.
334 * @read_from_file Set to TRUE if stats are read from a system activity
336 * @act_id Activity that can be displayed, or ~0 for all.
337 * Remember that when reading stats from a file, only
338 * one activity can be displayed at a time.
339 ***************************************************************************
341 void write_stats_avg(int curr, int read_from_file, unsigned int act_id)
344 unsigned long long itv, g_itv;
345 static __nr_t cpu_nr = -1;
348 cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr;
350 /* Interval value in jiffies */
351 g_itv = get_interval(record_hdr[2].uptime, record_hdr[curr].uptime);
354 itv = get_interval(record_hdr[2].uptime0, record_hdr[curr].uptime0);
358 strncpy(timestamp[curr], _("Average:"), TIMESTAMP_LEN);
359 timestamp[curr][TIMESTAMP_LEN - 1] = '\0';
360 strcpy(timestamp[!curr], timestamp[curr]);
363 TEST_STDOUT(STDOUT_FILENO);
365 for (i = 0; i < NR_ACT; i++) {
367 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
370 if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
371 /* Display current average activity statistics */
372 (*act[i]->f_print_avg)(act[i], 2, curr,
373 NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv);
377 if (read_from_file) {
379 * Reset number of lines printed only if we read stats
380 * from a system activity file.
387 ***************************************************************************
388 * Print system statistics.
391 * @curr Index in array for current sample statistics.
392 * @read_from_file Set to TRUE if stats are read from a system activity
394 * @use_tm_start Set to TRUE if option -s has been used.
395 * @use_tm_end Set to TRUE if option -e has been used.
396 * @reset Set to TRUE if last_uptime variable should be
397 * reinitialized (used in next_slice() function).
398 * @act_id Activity that can be displayed or ~0 for all.
399 * Remember that when reading stats from a file, only
400 * one activity can be displayed at a time.
401 * @reset_cd TRUE if static cross_day variable should be reset
405 * @cnt Number of remaining lines to display.
408 * 1 if stats have been successfully displayed, and 0 otherwise.
409 ***************************************************************************
411 int write_stats(int curr, int read_from_file, long *cnt, int use_tm_start,
412 int use_tm_end, int reset, unsigned int act_id, int reset_cd)
415 unsigned long long itv, g_itv;
416 static int cross_day = 0;
417 static __nr_t cpu_nr = -1;
420 cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr;
424 * cross_day is a static variable that is set to 1 when the first
425 * record of stats from a new day is read from a unique data file
426 * (in the case where the file contains data from two consecutive
427 * days). When set to 1, every following records timestamp will
428 * have its hour value increased by 24.
429 * Yet when a new activity (being read from the file) is going to
430 * be displayed, we start reading the file from the beginning
431 * again, and so cross_day should be reset in this case.
437 if (read_from_file) {
438 if (!next_slice(record_hdr[2].uptime0, record_hdr[curr].uptime0,
440 /* Not close enough to desired interval */
444 if (!is_iso_time_fmt())
445 flags |= S_F_PREFD_TIME_OUTPUT;
447 /* Get then set previous timestamp */
448 if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[!curr],
451 set_record_timestamp_string(flags, &record_hdr[!curr],
452 NULL, timestamp[!curr], TIMESTAMP_LEN, &rectime);
454 /* Get then set current timestamp */
455 if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME, &record_hdr[curr],
458 set_record_timestamp_string(flags, &record_hdr[curr],
459 NULL, timestamp[curr], TIMESTAMP_LEN, &rectime);
461 /* Check if we are beginning a new day */
462 if (use_tm_start && record_hdr[!curr].ust_time &&
463 (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) &&
464 (record_hdr[curr].hour < record_hdr[!curr].hour)) {
470 * This is necessary if we want to properly handle something like:
471 * sar -s time_start -e time_end with
472 * time_start(day D) > time_end(day D+1)
474 rectime.tm_hour +=24;
478 if (use_tm_start && (datecmp(&rectime, &tm_start) < 0))
479 /* it's too soon... */
482 /* Get interval values */
483 get_itv_value(&record_hdr[curr], &record_hdr[!curr],
484 cpu_nr, &itv, &g_itv);
487 if (use_tm_end && (datecmp(&rectime, &tm_end) > 0)) {
488 /* It's too late... */
496 TEST_STDOUT(STDOUT_FILENO);
498 for (i = 0; i < NR_ACT; i++) {
500 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
503 if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
504 /* Display current activity statistics */
505 (*act[i]->f_print)(act[i], !curr, curr,
506 NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv);
514 ***************************************************************************
515 * Display stats since system startup.
518 * @curr Index in array for current sample statistics.
519 ***************************************************************************
521 void write_stats_startup(int curr)
525 /* Set to 0 previous structures corresponding to boot time */
526 memset(&record_hdr[!curr], 0, RECORD_HEADER_SIZE);
527 record_hdr[!curr].record_type = R_STATS;
528 record_hdr[!curr].hour = record_hdr[curr].hour;
529 record_hdr[!curr].minute = record_hdr[curr].minute;
530 record_hdr[!curr].second = record_hdr[curr].second;
531 record_hdr[!curr].ust_time = record_hdr[curr].ust_time;
533 for (i = 0; i < NR_ACT; i++) {
534 if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
535 memset(act[i]->buf[!curr], 0,
536 (size_t) act[i]->msize * (size_t) act[i]->nr * (size_t) act[i]->nr2);
540 flags |= S_F_SINCE_BOOT;
543 write_stats(curr, USE_SADC, &count, NO_TM_START, NO_TM_END, NO_RESET,
544 ALL_ACTIVITIES, TRUE);
550 ***************************************************************************
551 * Read data sent by the data collector.
554 * @size Number of bytes of data to read.
557 * @buffer Buffer where data will be saved.
560 * 1 if end of file has been reached, 0 otherwise.
561 ***************************************************************************
563 int sa_read(void *buffer, int size)
569 if ((n = read(STDIN_FILENO, buffer, size)) < 0) {
578 buffer = (char *) buffer + n;
585 ***************************************************************************
586 * Display a restart message (contents of a R_RESTART record).
589 * @tab Number of tabulations (unused here).
590 * @action Action expected from current function (unused here).
591 * @cur_date Date string of current restart message (unused here).
592 * @cur_time Time string of current restart message.
593 * @utc True if @cur_time is expressed in UTC (unused here).
594 * @file_hdr System activity file standard header (unused here).
595 * @cpu_nr CPU count associated with restart mark.
596 ***************************************************************************
598 __print_funct_t print_sar_restart(int *tab, int action, char *cur_date, char *cur_time, int utc,
599 struct file_header *file_hdr, unsigned int cpu_nr)
603 printf("\n%-11s", cur_time);
604 sprintf(restart, " LINUX RESTART\t(%d CPU)\n",
605 cpu_nr > 1 ? cpu_nr - 1 : 1);
606 cprintf_s(IS_RESTART, "%s", restart);
611 ***************************************************************************
612 * Display a comment (contents of R_COMMENT record).
615 * @tab Number of tabulations (unused here).
616 * @action Action expected from current function (unused here).
617 * @cur_date Date string of current comment (unused here).
618 * @cur_time Time string of current comment.
619 * @utc True if @cur_time is expressed in UTC (unused here).
620 * @comment Comment to display.
621 * @file_hdr System activity file standard header (unused here).
622 ***************************************************************************
624 __print_funct_t print_sar_comment(int *tab, int action, char *cur_date, char *cur_time, int utc,
625 char *comment, struct file_header *file_hdr)
627 printf("%-11s", cur_time);
628 cprintf_s(IS_COMMENT, " COM %s\n", comment);
632 ***************************************************************************
633 * Read the various statistics sent by the data collector (sadc).
636 * @curr Index in array for current sample statistics.
637 ***************************************************************************
639 void read_sadc_stat_bunch(int curr)
643 /* Read record header (type is always R_STATS since it is read from sadc) */
644 if (sa_read(&record_hdr[curr], RECORD_HEADER_SIZE)) {
646 * SIGINT (sent by sadc) is likely to be received
647 * while we are stuck in sa_read().
648 * If this happens then no data have to be read.
656 for (i = 0; i < NR_ACT; i++) {
660 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
662 if (sa_read(act[p]->buf[curr], act[p]->fsize * act[p]->nr * act[p]->nr2)) {
669 ***************************************************************************
670 * Read stats for current activity from file and display them.
673 * @ifd Input file descriptor.
674 * @fpos Position in file where reading must start.
675 * @curr Index in array for current sample statistics.
676 * @rows Number of rows of screen.
677 * @act_id Activity to display.
678 * @file_actlst List of activities in file.
679 * @file Name of file being read.
680 * @file_magic file_magic structure filled with file magic header data.
683 * @curr Index in array for next sample statistics.
684 * @cnt Number of remaining lines of stats to write.
685 * @eosaf Set to TRUE if EOF (end of file) has been reached.
686 * @reset Set to TRUE if last_uptime variable should be
687 * reinitialized (used in next_slice() function).
688 ***************************************************************************
690 void handle_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf,
691 int rows, unsigned int act_id, int *reset,
692 struct file_activity *file_actlst, char *file,
693 struct file_magic *file_magic)
696 unsigned long lines = 0;
698 int davg = 0, next, inc;
700 if (lseek(ifd, fpos, SEEK_SET) < fpos) {
706 * Restore the first stats collected.
707 * Used to compute the rate displayed on the first line.
709 copy_structures(act, id_seq, record_hdr, !*curr, 2);
713 /* Assess number of lines printed */
714 p = get_activity_position(act, act_id, EXIT_IF_NOT_FOUND);
715 if (act[p]->bitmap) {
716 inc = count_bits(act[p]->bitmap->b_array,
717 BITMAP_SIZE(act[p]->bitmap->b_size));
726 /* Display count lines of stats */
727 *eosaf = sa_fread(ifd, &record_hdr[*curr],
728 RECORD_HEADER_SIZE, SOFT_SIZE);
729 rtype = record_hdr[*curr].record_type;
731 if (!*eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
732 /* Read the extra fields since it's not a special record */
733 read_file_stat_bunch(act, *curr, ifd, file_hdr.sa_act_nr, file_actlst);
736 if ((lines >= rows) || !lines) {
743 if (!*eosaf && (rtype != R_RESTART)) {
745 if (rtype == R_COMMENT) {
746 /* Display comment */
747 next = print_special_record(&record_hdr[*curr], flags + S_F_LOCAL_TIME,
748 &tm_start, &tm_end, R_COMMENT, ifd,
749 &rectime, NULL, file, 0,
750 file_magic, &file_hdr, act, &sar_fmt);
752 /* A line of comment was actually displayed */
758 /* next is set to 1 when we were close enough to desired interval */
759 next = write_stats(*curr, USE_SA_FILE, cnt, tm_start.use, tm_end.use,
760 *reset, act_id, reset_cd);
762 if (next && (*cnt > 0)) {
773 while (*cnt && !*eosaf && (rtype != R_RESTART));
776 write_stats_avg(!*curr, USE_SA_FILE, act_id);
783 ***************************************************************************
784 * Read header data sent by sadc.
785 ***************************************************************************
787 void read_header_data(void)
789 struct file_magic file_magic;
790 struct file_activity file_act;
794 /* Read magic header */
795 rc = sa_read(&file_magic, FILE_MAGIC_SIZE);
797 sprintf(version, "%d.%d.%d.%d",
798 file_magic.sysstat_version,
799 file_magic.sysstat_patchlevel,
800 file_magic.sysstat_sublevel,
801 file_magic.sysstat_extraversion);
802 if (!file_magic.sysstat_extraversion) {
803 version[strlen(version) - 2] = '\0';
806 if (rc || (file_magic.sysstat_magic != SYSSTAT_MAGIC) ||
807 (file_magic.format_magic != FORMAT_MAGIC) ||
808 strcmp(version, VERSION)) {
810 /* sar and sadc commands are not consistent */
811 if (!rc && (file_magic.sysstat_magic == SYSSTAT_MAGIC)) {
813 _("Using a wrong data collector from a different sysstat version\n"));
821 * No need to take into account file_magic.header_size. We are sure that
822 * sadc and sar are from the same version (we have checked FORMAT_MAGIC
823 * but also VERSION above) and thus the size of file_header is FILE_HEADER_SIZE.
825 if (sa_read(&file_hdr, FILE_HEADER_SIZE)) {
829 if (file_hdr.sa_act_nr > NR_ACT)
832 /* Read activity list */
833 for (i = 0; i < file_hdr.sa_act_nr; i++) {
835 if (sa_read(&file_act, FILE_ACTIVITY_SIZE)) {
839 p = get_activity_position(act, file_act.id, RESUME_IF_NOT_FOUND);
841 if ((p < 0) || (act[p]->fsize != file_act.size)
842 || (file_act.nr <= 0)
843 || (file_act.nr2 <= 0)
844 || (act[p]->magic != file_act.magic))
845 /* Remember that we are reading data from sadc and not from a file... */
848 id_seq[i] = file_act.id; /* We necessarily have "i < NR_ACT" */
849 act[p]->nr = file_act.nr;
850 act[p]->nr2 = file_act.nr2;
857 /* Check that all selected activties are actually sent by sadc */
858 reverse_check_act(file_hdr.sa_act_nr);
864 /* Strange data sent by sadc...! */
865 fprintf(stderr, _("Inconsistent input data\n"));
871 ***************************************************************************
872 * Read statistics from a system activity data file.
875 * @from_file Input file name.
876 ***************************************************************************
878 void read_stats_from_file(char from_file[])
880 struct file_magic file_magic;
881 struct file_activity *file_actlst = NULL;
884 int rows, eosaf = TRUE, reset = FALSE;
888 /* Get window size */
889 rows = get_win_height();
891 /* Read file headers and activity list */
892 check_file_actlst(&ifd, from_file, act, &file_magic, &file_hdr,
893 &file_actlst, id_seq, FALSE);
895 /* Perform required allocations */
896 allocate_structures(act);
898 /* Print report header */
899 print_report_hdr(flags, &rectime, &file_hdr,
900 act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr);
902 /* Read system statistics from file */
905 * If this record is a special (RESTART or COMMENT) one, print it and
906 * (try to) get another one.
909 if (sa_fread(ifd, &record_hdr[0], RECORD_HEADER_SIZE, SOFT_SIZE))
910 /* End of sa data file */
913 rtype = record_hdr[0].record_type;
914 if ((rtype == R_RESTART) || (rtype == R_COMMENT)) {
915 print_special_record(&record_hdr[0], flags + S_F_LOCAL_TIME,
916 &tm_start, &tm_end, rtype, ifd,
917 &rectime, NULL, from_file, 0, &file_magic,
918 &file_hdr, act, &sar_fmt);
922 * OK: Previous record was not a special one.
923 * So read now the extra fields.
925 read_file_stat_bunch(act, 0, ifd, file_hdr.sa_act_nr,
927 if (sa_get_record_timestamp_struct(flags + S_F_LOCAL_TIME,
931 * An error was detected.
932 * The timestamp hasn't been updated.
937 while ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
938 (tm_start.use && (datecmp(&rectime, &tm_start) < 0)) ||
939 (tm_end.use && (datecmp(&rectime, &tm_end) >=0)));
941 /* Save the first stats collected. Will be used to compute the average */
942 copy_structures(act, id_seq, record_hdr, 2, 0);
944 reset = TRUE; /* Set flag to reset last_uptime variable */
946 /* Save current file position */
947 if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) {
953 * Read and write stats located between two possible Linux restarts.
954 * Activities that should be displayed are saved in id_seq[] array.
956 for (i = 0; i < NR_ACT; i++) {
961 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
962 if (!IS_SELECTED(act[p]->options))
965 if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) {
966 handle_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf, rows,
967 act[p]->id, &reset, file_actlst,
968 from_file, &file_magic);
971 unsigned int optf, msk;
973 optf = act[p]->opt_flags;
975 for (msk = 1; msk < 0x100; msk <<= 1) {
976 if ((act[p]->opt_flags & 0xff) & msk) {
977 act[p]->opt_flags &= (0xffffff00 + msk);
979 handle_curr_act_stats(ifd, fpos, &curr, &cnt,
980 &eosaf, rows, act[p]->id,
982 from_file, &file_magic);
983 act[p]->opt_flags = optf;
990 /* Go to next Linux restart, if possible */
992 eosaf = sa_fread(ifd, &record_hdr[curr], RECORD_HEADER_SIZE,
994 rtype = record_hdr[curr].record_type;
995 if (!eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
996 read_file_stat_bunch(act, curr, ifd, file_hdr.sa_act_nr,
999 else if (!eosaf && (rtype == R_COMMENT)) {
1000 /* This was a COMMENT record: print it */
1001 print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
1002 &tm_start, &tm_end, R_COMMENT, ifd,
1003 &rectime, NULL, from_file, 0,
1004 &file_magic, &file_hdr, act, &sar_fmt);
1007 while (!eosaf && (rtype != R_RESTART));
1010 /* The last record we read was a RESTART one: Print it */
1011 if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) {
1012 print_special_record(&record_hdr[curr], flags + S_F_LOCAL_TIME,
1013 &tm_start, &tm_end, R_RESTART, ifd,
1014 &rectime, NULL, from_file, 0,
1015 &file_magic, &file_hdr, act, &sar_fmt);
1026 ***************************************************************************
1027 * Read statistics sent by sadc, the data collector.
1028 ***************************************************************************
1030 void read_stats(void)
1033 unsigned long lines;
1037 /* Don't buffer data if redirected to a pipe... */
1038 setbuf(stdout, NULL);
1040 /* Read stats header */
1043 if (!get_activity_nr(act, AO_SELECTED, COUNT_ACTIVITIES)) {
1044 fprintf(stderr, _("Requested activities not available\n"));
1048 /* Determine if a stat line header has to be displayed */
1049 dis_hdr = check_line_hdr();
1051 lines = rows = get_win_height();
1053 /* Perform required allocations */
1054 allocate_structures(act);
1056 /* Print report header */
1057 print_report_hdr(flags, &rectime, &file_hdr,
1058 act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr);
1060 /* Read system statistics sent by the data collector */
1061 read_sadc_stat_bunch(0);
1064 /* Display stats since boot time and exit */
1065 write_stats_startup(0);
1068 /* Save the first stats collected. Will be used to compute the average */
1069 copy_structures(act, id_seq, record_hdr, 2, 0);
1071 /* Set a handler for SIGINT */
1072 memset(&int_act, 0, sizeof(int_act));
1073 int_act.sa_handler = int_handler;
1074 int_act.sa_flags = SA_RESTART;
1075 sigaction(SIGINT, &int_act, NULL);
1081 read_sadc_stat_bunch(curr);
1082 if (sigint_caught) {
1084 * SIGINT signal caught (it is sent by sadc).
1085 * => Display average stats.
1087 curr ^= 1; /* No data retrieved from last read */
1099 write_stats(curr, USE_SADC, &count, NO_TM_START, tm_end.use,
1100 NO_RESET, ALL_ACTIVITIES, TRUE);
1102 if (record_hdr[curr].record_type == R_LAST_STATS) {
1103 /* File rotation is happening: Re-read header data sent by sadc */
1105 allocate_structures(act);
1118 * Print statistics average.
1119 * At least one line of stats must have been displayed for this.
1120 * (There may be no lines at all if we press Ctrl/C immediately).
1124 write_stats_avg(curr, USE_SADC, ALL_ACTIVITIES);
1129 ***************************************************************************
1130 * Main entry to the sar program.
1131 ***************************************************************************
1133 int main(int argc, char **argv)
1135 int i, rc, opt = 1, args_idx = 2;
1138 char from_file[MAX_FILE_LEN], to_file[MAX_FILE_LEN];
1144 /* Compute page shift in kB */
1147 from_file[0] = to_file[0] = '\0';
1150 /* Init National Language Support */
1154 /* Init color strings */
1157 tm_start.use = tm_end.use = FALSE;
1159 /* Allocate and init activity bitmaps */
1160 allocate_bitmaps(act);
1164 /* Process options */
1165 while (opt < argc) {
1167 if (!strcmp(argv[opt], "--sadc")) {
1172 else if (!strcmp(argv[opt], "-I")) {
1174 /* Parse -I option */
1175 if (parse_sar_I_opt(argv, &opt, act)) {
1184 else if (!strcmp(argv[opt], "-D")) {
1185 /* Option to tell sar to write to saYYYYMMDD data files */
1186 flags |= S_F_SA_YYYYMMDD;
1190 else if (!strcmp(argv[opt], "-P")) {
1191 /* Parse -P option */
1192 if (parse_sa_P_opt(argv, &opt, &flags, act)) {
1197 else if (!strcmp(argv[opt], "-o")) {
1199 /* Output file already specified */
1202 /* Save stats to a file */
1203 if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1204 (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1205 strncpy(to_file, argv[opt++], MAX_FILE_LEN);
1206 to_file[MAX_FILE_LEN - 1] = '\0';
1209 strcpy(to_file, "-");
1213 else if (!strcmp(argv[opt], "-f")) {
1214 if (from_file[0] || day_offset) {
1215 /* Input file already specified */
1218 /* Read stats from a file */
1219 if ((argv[++opt]) && strncmp(argv[opt], "-", 1) &&
1220 (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1221 strncpy(from_file, argv[opt++], MAX_FILE_LEN);
1222 from_file[MAX_FILE_LEN - 1] = '\0';
1223 /* Check if this is an alternate directory for sa files */
1224 check_alt_sa_dir(from_file, day_offset, -1);
1227 set_default_file(from_file, day_offset, -1);
1231 else if (!strcmp(argv[opt], "-s")) {
1232 /* Get time start */
1233 if (parse_timestamp(argv, &opt, &tm_start, DEF_TMSTART)) {
1238 else if (!strcmp(argv[opt], "-e")) {
1240 if (parse_timestamp(argv, &opt, &tm_end, DEF_TMEND)) {
1245 else if (!strcmp(argv[opt], "-h")) {
1246 /* Display help message */
1247 display_help(argv[0]);
1250 else if (!strcmp(argv[opt], "-i")) {
1251 if (!argv[++opt] || (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
1254 interval = atol(argv[opt++]);
1258 flags |= S_F_INTERVAL_SET;
1261 else if (!strcmp(argv[opt], "-m")) {
1263 /* Parse option -m */
1264 if (parse_sar_m_opt(argv, &opt, act)) {
1273 else if (!strcmp(argv[opt], "-n")) {
1275 /* Parse option -n */
1276 if (parse_sar_n_opt(argv, &opt, act)) {
1285 else if ((strlen(argv[opt]) > 1) &&
1286 (strlen(argv[opt]) < 4) &&
1287 !strncmp(argv[opt], "-", 1) &&
1288 (strspn(argv[opt] + 1, DIGITS) == (strlen(argv[opt]) - 1))) {
1289 if (from_file[0] || day_offset) {
1290 /* Input file already specified */
1293 day_offset = atoi(argv[opt++] + 1);
1296 else if (!strncmp(argv[opt], "-", 1)) {
1297 /* Other options not previously tested */
1298 if ((rc = parse_sar_opt(argv, &opt, act, &flags, C_SAR)) != 0) {
1307 else if (interval < 0) {
1309 if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1312 interval = atol(argv[opt++]);
1319 /* Get count value */
1320 if ((strspn(argv[opt], DIGITS) != strlen(argv[opt])) ||
1325 /* Count parameter already set */
1328 count = atol(argv[opt++]);
1335 /* 'sar' is equivalent to 'sar -f' */
1337 ((interval < 0) && !from_file[0] && !to_file[0])) {
1338 set_default_file(from_file, day_offset, -1);
1341 if (tm_start.use && tm_end.use && (tm_end.tm_hour < tm_start.tm_hour)) {
1342 tm_end.tm_hour += 24;
1346 * Check option dependencies.
1348 /* You read from a file OR you write to it... */
1349 if (from_file[0] && to_file[0]) {
1350 fprintf(stderr, _("-f and -o options are mutually exclusive\n"));
1353 /* Use time start or option -i only when reading stats from a file */
1354 if ((tm_start.use || INTERVAL_SET(flags)) && !from_file[0]) {
1356 _("Not reading from a system activity file (use -f option)\n"));
1359 /* Don't print stats since boot time if -o or -f options are used */
1360 if (!interval && (from_file[0] || to_file[0])) {
1364 /* Cannot enter a day shift with -o option */
1365 if (to_file[0] && day_offset) {
1369 if (USE_PRETTY_OPTION(flags)) {
1370 dm_major = get_devmap_major();
1375 * count parameter not set: Display all the contents of the file
1376 * or generate a report continuously.
1381 /* Default is CPU activity... */
1382 select_default_activity(act);
1384 /* Reading stats from file: */
1390 /* Read stats from file */
1391 read_stats_from_file(from_file);
1393 /* Free stuctures and activity bitmaps */
1395 free_structures(act);
1400 /* Reading stats from sadc: */
1402 /* Create anonymous pipe */
1403 if (pipe(fd) == -1) {
1416 if (dup2(fd[1], STDOUT_FILENO) < 0) {
1423 * Prepare options for sadc.
1428 /* Interval value */
1432 else if (!interval) {
1436 sprintf(ltemp, "%ld", interval);
1442 sprintf(ltemp, "%ld", count + 1);
1443 salloc(args_idx++, ltemp);
1446 /* Flags to be passed to sadc */
1447 salloc(args_idx++, "-z");
1449 /* Writing data to a file (option -o) */
1451 /* Set option -D if entered */
1452 if (USE_SA_YYYYMMDD(flags)) {
1453 salloc(args_idx++, "-D");
1455 /* Collect all possible activities (option -S XALL for sadc) */
1456 salloc(args_idx++, "-S");
1457 salloc(args_idx++, K_XALL);
1459 salloc(args_idx++, to_file);
1463 * If option -o hasn't been used, then tell sadc
1464 * to collect only activities that will be displayed.
1468 for (i = 0; i < NR_ACT; i++) {
1469 if (IS_SELECTED(act[i]->options)) {
1470 act_id |= act[i]->group;
1475 snprintf(ltemp, 19, "%d", act_id);
1477 salloc(args_idx++, "-S");
1478 salloc(args_idx++, ltemp);
1482 /* Last arg is NULL */
1483 args[args_idx] = NULL;
1485 /* Call now the data collector */
1486 execv(SADC_PATH, args);
1489 * Note: Don't use execl/execlp since we don't have a fixed number of
1490 * args to give to sadc.
1492 fprintf(stderr, _("Cannot find the data collector (%s)\n"), SADC);
1497 default: /* Parent */
1498 if (dup2(fd[0], STDIN_FILENO) < 0) {
1504 /* Get now the statistics */
1510 /* Free structures and activity bitmaps */
1512 free_structures(act);