2 * sadf: system activity data formatter
3 * (C) 1999-2021 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 ***************************************************************************
34 # include <locale.h> /* For setlocale() */
37 # define _(string) gettext(string)
39 # define _(string) (string)
43 #include <pcp/pmapi.h>
44 #include <pcp/import.h>
48 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
49 char *sccsid(void) { return (SCCSID); }
54 void int_handler(int n) { return; }
57 long interval = -1, count = 0;
59 /* TRUE if data read from file don't match current machine's endianness */
60 int endian_mismatch = FALSE;
61 /* TRUE if file's data come from a 64 bit machine */
63 /* Number of decimal places */
65 /* Color palette number */
66 int palette = SVG_DEFAULT_COL_PALETTE;
69 unsigned int dm_major; /* Device-mapper major number */
70 unsigned int format = 0; /* Output format */
71 unsigned int f_position = 0; /* Output format position in array */
72 unsigned int canvas_height = 0; /* SVG canvas height value set with option -O */
73 unsigned int user_hz = 0; /* HZ value set with option -O */
76 struct file_header file_hdr;
80 * This array must always be entirely filled (even with trailing zeros).
82 unsigned int id_seq[NR_ACT];
84 /* Current record header */
85 struct record_header record_hdr[3];
87 /* Contain the date specified by -s and -e options */
88 struct tstamp tm_start, tm_end;
89 char *args[MAX_ARGV_NR];
91 extern struct activity *act[];
92 extern struct report_format *fmt[];
95 ***************************************************************************
96 * Print usage and exit.
99 * @progname Name of sysstat command.
100 ***************************************************************************
102 void usage(char *progname)
105 _("Usage: %s [ options ] [ <interval> [ <count> ] ] [ <datafile> | -[0-9]+ ]\n"),
108 fprintf(stderr, _("Options are:\n"
109 "[ -C ] [ -c | -d | -g | -j | -l | -p | -r | -x ] [ -H ] [ -h ] [ -T | -t | -U ] [ -V ]\n"
110 "[ -O <opts> [,...] ] [ -P { <cpu> [,...] | ALL } ]\n"
111 "[ --dev=<dev_list> ] [ --fs=<fs_list> ] [ --iface=<iface_list> ] [ --int=<int_list> ]\n"
112 "[ -s [ <hh:mm[:ss]> ] ] [ -e [ <hh:mm[:ss]> ] ]\n"
113 "[ -- <sar_options> ]\n"));
118 ***************************************************************************
120 ***************************************************************************
122 void init_structures(void)
126 for (i = 0; i < 3; i++) {
127 memset(&record_hdr[i], 0, RECORD_HEADER_SIZE);
132 ***************************************************************************
133 * Look for output format in array.
136 * @oformat Output format to look for.
139 * Position of output format in array.
140 ***************************************************************************
142 int get_format_position(unsigned int oformat)
146 for (i = 0; i < NR_FMT; i++) {
147 if (fmt[i]->id == oformat)
152 /* Should never happen */
159 ***************************************************************************
160 * Check that options entered on the command line are consistent with
161 * selected output format. If no output format has been explicitly entered,
162 * then select a default one.
163 ***************************************************************************
165 void check_format_options(void)
168 /* Select output format if none has been selected */
169 if (DISPLAY_HDR_ONLY(flags)) {
170 format = F_HEADER_OUTPUT;
173 format = F_PPC_OUTPUT;
177 /* Get format position in array */
178 f_position = get_format_position(format);
180 /* Check options consistency wrt output format */
181 if (!ACCEPT_HEADER_ONLY(fmt[f_position]->options)) {
182 /* Remove option -H */
183 flags &= ~S_F_HDR_ONLY;
185 if (!ACCEPT_HORIZONTALLY(fmt[f_position]->options)) {
186 /* Remove option -h */
187 flags &= ~S_F_HORIZONTALLY;
189 if (!ACCEPT_LOCAL_TIME(fmt[f_position]->options)) {
190 /* Remove option -T */
191 flags &= ~S_F_LOCAL_TIME;
193 if (!ACCEPT_SEC_EPOCH(fmt[f_position]->options)) {
194 /* Remove option -U */
195 flags &= ~S_F_SEC_EPOCH;
197 if (REJECT_TRUE_TIME(fmt[f_position]->options)) {
198 /* Remove option -t */
199 flags &= ~S_F_TRUE_TIME;
204 ***************************************************************************
205 * Read next sample statistics. If it's a special record (R_RESTART or
206 * R_COMMENT) then display it if requested. Also fill timestamps structures.
209 * @ifd File descriptor
210 * @action Flags indicating if special records should be displayed or
212 * @curr Index in array for current sample statistics.
213 * @file System activity data file name (name of file being read).
214 * @tab Number of tabulations to print.
215 * @file_magic System activity file magic header.
216 * @file_actlst List of (known or unknown) activities in file.
217 * @rectime Structure where timestamp (expressed in local time or in UTC
218 * depending on whether options -T/-t have been used or not) can
219 * be saved for current record.
220 * @oneof Set to UEOF_CONT if an unexpected end of file should not make
221 * sadf stop. Default behavior is to stop on unexpected EOF.
224 * @rtype Type of record read (R_RESTART, R_COMMENT, etc.)
225 * @rectime Structure where timestamp (expressed in local time or in UTC
226 * depending on options used) has been saved for current record.
227 * If current record was a special one (RESTART or COMMENT) and
228 * noted to be ignored, then the timestamp is saved only if
229 * explicitly told to do so with the SET_TIMESTAMPS action flag.
232 * 1 if EOF has been reached,
233 * 2 if an unexpected EOF has been reached, or an error occurred.
235 ***************************************************************************
237 int read_next_sample(int ifd, int action, int curr, char *file, int *rtype, int tab,
238 struct file_magic *file_magic, struct file_activity *file_actlst,
239 struct tm *rectime, int oneof)
242 char rec_hdr_tmp[MAX_RECORD_HEADER_SIZE];
244 /* Read current record */
245 if ((rc = read_record_hdr(ifd, rec_hdr_tmp, &record_hdr[curr], &file_hdr,
246 arch_64, endian_mismatch, oneof, sizeof(rec_hdr_tmp), flags,
247 fmt[f_position])) != 0)
251 *rtype = record_hdr[curr].record_type;
253 if (*rtype == R_COMMENT) {
254 if (action & IGNORE_COMMENT) {
255 /* Ignore COMMENT record */
256 if (lseek(ifd, MAX_COMMENT_LEN, SEEK_CUR) < MAX_COMMENT_LEN) {
257 if (oneof == UEOF_CONT)
263 /* Ignore unknown extra structures if present */
264 if (record_hdr[curr].extra_next && (skip_extra_struct(ifd, endian_mismatch, arch_64) < 0))
267 if (action & SET_TIMESTAMPS) {
268 if (sa_get_record_timestamp_struct(flags, &record_hdr[curr],
274 /* Display COMMENT record */
275 print_special_record(&record_hdr[curr], flags, &tm_start, &tm_end,
276 *rtype, ifd, rectime, file, tab,
277 file_magic, &file_hdr, act, fmt[f_position],
278 endian_mismatch, arch_64);
281 else if (*rtype == R_RESTART) {
282 if (action & IGNORE_RESTART) {
284 * Ignore RESTART record (don't display it)
285 * but anyway we have to read the CPU number that follows it
286 * (unless we don't want to do it now).
288 if (!(action & DONT_READ_CPU_NR)) {
289 file_hdr.sa_cpu_nr = read_nr_value(ifd, file, file_magic,
290 endian_mismatch, arch_64, TRUE);
292 /* Ignore unknown extra structures if present */
293 if (record_hdr[curr].extra_next && (skip_extra_struct(ifd, endian_mismatch, arch_64) < 0))
296 if (action & SET_TIMESTAMPS) {
297 if (sa_get_record_timestamp_struct(flags, &record_hdr[curr], rectime))
302 /* Display RESTART record */
303 print_special_record(&record_hdr[curr], flags, &tm_start, &tm_end,
304 *rtype, ifd, rectime, file, tab,
305 file_magic, &file_hdr, act, fmt[f_position],
306 endian_mismatch, arch_64);
311 * OK: Previous record was not a special one.
312 * So read now the extra fields.
314 if (read_file_stat_bunch(act, curr, ifd, file_hdr.sa_act_nr, file_actlst,
315 endian_mismatch, arch_64, file, file_magic, oneof) > 0)
317 if (sa_get_record_timestamp_struct(flags, &record_hdr[curr], rectime))
325 ***************************************************************************
326 * Display the field list (used eg. in database format).
329 * @act_id Activity to display, or ~0 for all.
330 ***************************************************************************
332 void list_fields(unsigned int act_id)
337 char hline[HEADER_LINE_LEN] = "";
339 printf("# hostname;interval;timestamp");
341 for (i = 0; i < NR_ACT; i++) {
343 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
346 if (IS_SELECTED(act[i]->options) && (act[i]->nr_ini > 0)) {
347 if (!HAS_MULTIPLE_OUTPUTS(act[i]->options)) {
348 printf(";%s", act[i]->hdr_line);
349 if ((act[i]->nr_ini > 1) && DISPLAY_HORIZONTALLY(flags)) {
355 strncpy(hline, act[i]->hdr_line, sizeof(hline) - 1);
356 hline[sizeof(hline) - 1] = '\0';
357 for (hl = strtok(hline, "|"); hl; hl = strtok(NULL, "|"), msk <<= 1) {
358 if ((hl != NULL) && ((act[i]->opt_flags & 0xff) & msk)) {
359 if (strchr(hl, '&')) {
360 j = strcspn(hl, "&");
361 if ((act[i]->opt_flags & 0xff00) & (msk << 8)) {
362 /* Display whole header line */
367 /* Display only the first part of the header line */
376 if ((act[i]->nr_ini > 1) && DISPLAY_HORIZONTALLY(flags)) {
388 ***************************************************************************
389 * Determine the time (expressed in seconds since the epoch) used as the
390 * origin on X axis for SVG graphs. If S_F_SVG_ONE_DAY is set, then origin
391 * will be the beginning of current day (00:00:00) else it will be the time
392 * of the first sample collected.
395 * Time origin on X axis (expressed in seconds since the epoch).
396 ***************************************************************************
398 time_t get_time_ref(void)
403 if (DISPLAY_ONE_DAY(flags)) {
404 localtime_r((time_t *) &(record_hdr[2].ust_time), <m);
406 /* Move back to midnight */
407 ltm.tm_sec = ltm.tm_min = ltm.tm_hour = 0;
414 return (time_t) record_hdr[2].ust_time;
418 ***************************************************************************
419 * Save or restore position in file.
422 * @ifd File descriptor of input file.
423 * @action DO_SAVE to save position or DO_RESTORE to restore it.
424 ***************************************************************************
426 void seek_file_position(int ifd, int action)
428 static off_t fpos = -1;
429 static unsigned int save_cpu_nr = 0;
431 if (action == DO_SAVE) {
432 /* Save current file position */
433 if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) {
437 save_cpu_nr = file_hdr.sa_cpu_nr;
439 else if (action == DO_RESTORE) {
441 if ((fpos < 0) || (lseek(ifd, fpos, SEEK_SET) < fpos)) {
445 file_hdr.sa_cpu_nr = save_cpu_nr;
450 ***************************************************************************
451 * Count number of different items in file. Save these numbers in fields
452 * @item_list_sz of structure activity, and create the corresponding list
453 * in field @item_list.
456 * @ifd File descriptor of input file.
457 * @file Name of file being read.
458 * @file_magic file_magic structure filled with file magic header data.
459 * @file_actlst List of (known or unknown) activities in file.
460 * @rectime Structure where timestamp (expressed in local time or
461 * in UTC depending on whether options -T/-t have been
462 * used or not) can be saved for current record.
465 * 0 if no records are concerned in file, and 1 otherwise.
466 ***************************************************************************
468 int count_file_items(int ifd, char *file, struct file_magic *file_magic,
469 struct file_activity *file_actlst, struct tm *rectime)
473 /* Save current file position */
474 seek_file_position(ifd, DO_SAVE);
476 /* Init maximum number of items for each activity */
477 for (i = 0; i < NR_ACT; i++) {
478 if (!HAS_LIST_ON_CMDLINE(act[i]->options)) {
479 act[i]->item_list_sz = 0;
483 /* Look for the first record that will be displayed */
485 eosaf = read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT | SET_TIMESTAMPS,
486 0, file, &rtype, 0, file_magic, file_actlst,
489 /* No record to display */
492 while ((tm_start.use && (datecmp(rectime, &tm_start, FALSE) < 0)) ||
493 (tm_end.use && (datecmp(rectime, &tm_end, FALSE) >= 0)));
496 * Read all the file and determine the maximum number
497 * of items for each activity.
500 for (i = 0; i < NR_ACT; i++) {
501 if (!HAS_LIST_ON_CMDLINE(act[i]->options)) {
502 if (act[i]->f_count_new) {
503 act[i]->item_list_sz += (*act[i]->f_count_new)(act[i], 0);
505 else if (act[i]->nr[0] > act[i]->item_list_sz) {
506 act[i]->item_list_sz = act[i]->nr[0];
512 eosaf = read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT | SET_TIMESTAMPS,
513 0, file, &rtype, 0, file_magic, file_actlst,
516 (tm_end.use && (datecmp(rectime, &tm_end, FALSE) >= 0)))
517 /* End of data file or end time exceeded */
520 while ((rtype == R_RESTART) || (rtype == R_COMMENT));
522 while (!eosaf && !(tm_end.use && (datecmp(rectime, &tm_end, FALSE) >= 0)));
525 seek_file_position(ifd, DO_RESTORE);
531 ***************************************************************************
532 * Compute the number of rows that will contain SVG views. Usually only one
533 * view is displayed on a row, unless the "packed" option has been entered.
534 * Each activity selected may have several views. Moreover some activities
535 * may have a number of items that varies within the file: In this case,
536 * the number of views will depend on the highest number of items saved in
540 * @ifd File descriptor of input file.
541 * @file Name of file being read.
542 * @file_magic file_magic structure filled with file magic header data.
543 * @file_actlst List of (known or unknown) activities in file.
544 * @rectime Structure where timestamp (expressed in local time or
545 * in UTC depending on whether options -T/-t have been
546 * used or not) can be saved for current record.
547 * @views_per_row Default number of views displayed on a single row.
550 * @views_per_row Maximum number of views that will be displayed on a
551 * single row (useful only if "packed" option entered).
552 * @nr_act_dispd Number of activities that will be displayed.
556 * Number of rows containing views, taking into account only activities
557 * to be displayed, and selected period of time (options -s/-e).
559 ***************************************************************************
561 int get_svg_graph_nr(int ifd, char *file, struct file_magic *file_magic,
562 struct file_activity *file_actlst, struct tm *rectime,
563 int *views_per_row, int *nr_act_dispd)
565 int i, n, p, tot_g_nr = 0;
569 /* Count items in file */
570 if (!count_file_items(ifd, file, file_magic, file_actlst, rectime))
571 /* No record to display => No graph */
574 for (i = 0; i < NR_ACT; i++) {
578 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
579 if (!IS_SELECTED(act[p]->options) || !act[p]->g_nr)
584 if (PACK_VIEWS(flags)) {
586 * One activity = one row with multiple views.
587 * Exception is A_MEMORY, for which one activity may be
588 * displayed in two rows if both memory *and* swap utilization
589 * have been selected.
591 if ((act[p]->id == A_MEMORY) &&
592 (DISPLAY_MEMORY(act[p]->opt_flags) && DISPLAY_SWAP(act[p]->opt_flags))) {
600 /* One activity = multiple rows with only one view */
603 if (ONE_GRAPH_PER_ITEM(act[p]->options)) {
604 n = n * act[p]->item_list_sz;
606 if (act[p]->g_nr > *views_per_row) {
607 *views_per_row = act[p]->g_nr;
613 if (*views_per_row > MAX_VIEWS_ON_A_ROW) {
614 *views_per_row = MAX_VIEWS_ON_A_ROW;
620 ***************************************************************************
621 * Display *one* sample of statistics for one or several activities,
622 * checking that all conditions are met before printing (time start, time
623 * end, interval). Current record should be a record of statistics (R_STATS),
624 * not a special one (R_RESTART or R_COMMENT).
627 * @curr Index in array for current sample statistics.
628 * @use_tm_start Set to TRUE if option -s has been used.
629 * @use_tm_end Set to TRUE if option -e has been used.
630 * @reset Set to TRUE if last_uptime should be reinitialized
631 * (used in next_slice() function).
632 * @parm Pointer on parameters depending on output format
633 * (eg.: number of tabulations to print).
634 * @rectime Structure where timestamp (expressed in local time
635 * or in UTC depending on whether options -T/-t have
636 * been used or not) has been saved for current record.
637 * @reset_cd TRUE if static cross_day variable should be reset.
638 * @act_id Activity to display (only for formats where
639 * activities are displayed one at a time) or
640 * ALL_ACTIVITIES for all.
643 * @cnt Set to 0 to indicate that no other lines of stats
644 * should be displayed.
647 * 1 if stats have been successfully displayed.
648 ***************************************************************************
650 int generic_write_stats(int curr, int use_tm_start, int use_tm_end, int reset,
651 long *cnt, void *parm, struct tm *rectime,
652 int reset_cd, unsigned int act_id)
655 unsigned long long dt, itv;
656 char cur_date[TIMESTAMP_LEN], cur_time[TIMESTAMP_LEN], *pre = NULL;
657 static int cross_day = FALSE;
662 * NB: Resetting cross_day is needed only if datafile
663 * may be rewinded (eg. in db or ppc output formats).
670 * For this first check, we use the time interval entered on
671 * the command line. This is equivalent to sar's option -i which
672 * selects records at seconds as close as possible to the number
673 * specified by the interval parameter.
675 if (!next_slice(record_hdr[2].uptime_cs, record_hdr[curr].uptime_cs,
677 /* Not close enough to desired interval */
680 /* Check if we are beginning a new day */
681 if (use_tm_start && record_hdr[!curr].ust_time &&
682 (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) &&
683 (record_hdr[curr].hour < record_hdr[!curr].hour)) {
688 if (use_tm_end && (datecmp(rectime, &tm_end, cross_day) > 0)) {
689 /* End time exceeded */
694 /* Get interval values in 1/100th of a second */
695 get_itv_value(&record_hdr[curr], &record_hdr[!curr], &itv);
698 /* Correct rounding error for dt */
699 if ((itv % 100) >= 50) {
703 /* Set date and time strings for current record */
704 set_record_timestamp_string(flags, &record_hdr[curr],
705 cur_date, cur_time, TIMESTAMP_LEN, rectime);
707 if (*fmt[f_position]->f_timestamp) {
708 pre = (char *) (*fmt[f_position]->f_timestamp)(parm, F_BEGIN, cur_date, cur_time, dt,
709 &record_hdr[curr], &file_hdr, flags);
712 /* Display statistics */
713 for (i = 0; i < NR_ACT; i++) {
715 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
718 if ((TEST_MARKUP(fmt[f_position]->options) && CLOSE_MARKUP(act[i]->options)) ||
719 (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) ||
720 (format == F_RAW_OUTPUT)) {
722 if (format == F_JSON_OUTPUT) {
724 int *tab = (int *) parm;
726 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
728 if (*fmt[f_position]->f_timestamp) {
729 (*fmt[f_position]->f_timestamp)(tab, F_MAIN, cur_date, cur_time,
730 dt, &record_hdr[curr],
734 (*act[i]->f_json_print)(act[i], curr, *tab, itv);
737 else if (format == F_XML_OUTPUT) {
739 int *tab = (int *) parm;
741 (*act[i]->f_xml_print)(act[i], curr, *tab, itv);
744 else if (format == F_SVG_OUTPUT) {
746 struct svg_parm *svg_p = (struct svg_parm *) parm;
748 svg_p->dt = (unsigned long) dt;
749 (*act[i]->f_svg_print)(act[i], curr, F_MAIN, svg_p, itv, &record_hdr[curr]);
752 else if (format == F_RAW_OUTPUT) {
754 if (DISPLAY_DEBUG_MODE(flags)) {
755 printf("# name; %s; nr_curr; %d; nr_alloc; %d; nr_ini; %d\n", act[i]->name,
756 act[i]->nr[curr], act[i]->nr_allocated, act[i]->nr_ini);
759 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
760 (*act[i]->f_raw_print)(act[i], pre, curr);
764 else if (format == F_PCP_OUTPUT) {
766 if (*act[i]->f_pcp_print) {
767 (*act[i]->f_pcp_print)(act[i], curr);
772 /* Other output formats: db, ppc */
773 (*act[i]->f_render)(act[i], (format == F_DB_OUTPUT), pre, curr, itv);
778 if (*fmt[f_position]->f_timestamp) {
779 (*fmt[f_position]->f_timestamp)(parm, F_END, cur_date, cur_time, dt,
780 &record_hdr[curr], &file_hdr, flags);
787 ***************************************************************************
788 * Read stats for current activity from file and print them.
789 * Display at most <count> lines of stats (and possibly comments inserted
790 * in file) located between two LINUX RESTART messages.
793 * @ifd File descriptor of input file.
794 * @curr Index in array for current sample statistics.
795 * @act_id Activity to display, or ~0 for all.
796 * @file_actlst List of (known or unknown) activities in file.
797 * @rectime Structure where timestamp (expressed in local time or in UTC
798 * depending on whether options -T/-t have been used or not) can
799 * be saved for current record.
800 * @file Name of file being read.
801 * @file_magic file_magic structure filled with file magic header data.
804 * @curr Index in array for next sample statistics.
805 * @cnt Number of lines of stats remaining to write.
806 * @eosaf Set to TRUE if EOF (end of file) has been reached.
807 * @reset Set to TRUE if last_uptime variable should be
808 * reinitialized (used in next_slice() function).
809 ***************************************************************************
811 void rw_curr_act_stats(int ifd, int *curr, long *cnt, int *eosaf,
812 unsigned int act_id, int *reset, struct file_activity *file_actlst,
813 struct tm *rectime, char *file,
814 struct file_magic *file_magic)
820 seek_file_position(ifd, DO_RESTORE);
822 if (DISPLAY_FIELD_LIST(fmt[f_position]->options)) {
823 /* Print field list */
828 * Restore the first stats collected.
829 * Used to compute the rate displayed on the first line.
831 copy_structures(act, id_seq, record_hdr, !*curr, 2);
837 /* Display <count> lines of stats */
838 *eosaf = read_next_sample(ifd, IGNORE_RESTART | DONT_READ_CPU_NR,
839 *curr, file, &rtype, 0, file_magic,
840 file_actlst, rectime, UEOF_STOP);
842 if (*eosaf || (rtype == R_RESTART))
845 if (rtype != R_COMMENT) {
846 next = generic_write_stats(*curr, tm_start.use, tm_end.use, *reset, cnt,
847 NULL, rectime, reset_cd, act_id);
852 * next is set to 1 when we were close enough to desired interval.
853 * In this case, the call to generic_write_stats() has actually
854 * displayed a line of stats.
870 ***************************************************************************
871 * Read stats for current activity from file and display its SVG graphs.
872 * At most <count> lines of stats are taken into account.
875 * @ifd File descriptor of input file.
876 * @curr Index in array for current sample statistics.
877 * @p Current activity position.
878 * @file_actlst List of (known or unknown) activities in file.
879 * @rectime Structure where timestamp (expressed in local time or in UTC
880 * depending on whether options -T/-t have been used or not) can
881 * be saved for current record.
882 * @file Name of file being read.
883 * @file_magic file_magic structure filled with file magic header data.
884 * @g_nr Number of graphs already displayed (for all activities).
886 * Total number of activities that will be displayed.
889 * @cnt Number of lines of stats remaining to write.
890 * @eosaf Set to TRUE if EOF (end of file) has been reached.
891 * @reset Set to TRUE if last_uptime variable should be
892 * reinitialized (used in next_slice() function).
893 * @g_nr Total number of views displayed (including current activity).
894 ***************************************************************************
896 void display_curr_act_graphs(int ifd, int *curr, long *cnt, int *eosaf,
897 int p, int *reset, struct file_activity *file_actlst,
898 struct tm *rectime, char *file, struct file_magic *file_magic,
899 int *g_nr, int nr_act_dispd)
901 struct svg_parm parm;
906 seek_file_position(ifd, DO_RESTORE);
909 * Restore the first stats collected.
910 * Originally used to compute the rate displayed on the first line.
911 * Here, this is to plot the first point and start the graph.
913 copy_structures(act, id_seq, record_hdr, !*curr, 2);
915 parm.graph_no = *g_nr;
916 parm.ust_time_ref = (unsigned long long) get_time_ref();
917 parm.ust_time_first = record_hdr[2].ust_time;
919 parm.file_hdr = &file_hdr;
920 parm.nr_act_dispd = nr_act_dispd;
925 /* Allocate graphs arrays */
926 (*act[p]->f_svg_print)(act[p], !*curr, F_BEGIN, &parm, 0, &record_hdr[!*curr]);
929 *eosaf = read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT | SET_TIMESTAMPS,
930 *curr, file, &rtype, 0, file_magic,
931 file_actlst, rectime, UEOF_CONT);
935 if (rtype == R_RESTART) {
938 /* Go to next statistics record, if possible */
940 *eosaf = read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT | SET_TIMESTAMPS,
941 *curr, file, &rtype, 0, file_magic,
942 file_actlst, rectime, UEOF_CONT);
944 while (!*eosaf && ((rtype == R_RESTART) || (rtype == R_COMMENT)));
948 else if (rtype != R_COMMENT) {
950 next = generic_write_stats(*curr, tm_start.use, tm_end.use, *reset, cnt,
951 &parm, rectime, reset_cd, act[p]->id);
955 * next is set to 1 when we were close enough to desired interval.
956 * In this case, the call to generic_write_stats() has actually
957 * displayed a line of stats.
959 parm.restart = FALSE;
960 parm.ust_time_end = record_hdr[*curr].ust_time;
969 while (!*eosaf && *cnt);
973 /* Determine X axis end value */
974 if (DISPLAY_ONE_DAY(flags) &&
975 (parm.ust_time_ref + (3600 * 24) > parm.ust_time_end)) {
976 parm.ust_time_end = parm.ust_time_ref + (3600 * 24);
979 /* Actually display graphs for current activity */
980 (*act[p]->f_svg_print)(act[p], *curr, F_END, &parm, 0, &record_hdr[!*curr]);
982 /* Update total number of graphs already displayed */
983 *g_nr = parm.graph_no;
987 ***************************************************************************
988 * Display file contents in selected format (logic #1).
989 * Logic #1: Grouped by record type. Sorted by timestamp.
990 * Formats: XML, JSON, PCP
992 * NB: all statistics data will be sorted by timestamp.
993 * Example: If stats for activities A and B at time t and t' have been collected,
994 * the output will be:
995 * stats for activity A at t
996 * stats for activity B at t
997 * stats for activity A at t'
998 * stats for activity B at t'
1001 * @ifd File descriptor of input file.
1002 * @file System activity data file name (name of file being read).
1003 * @file_actlst List of (known or unknown) activities in file.
1004 * @file_magic System activity file magic header.
1005 * @rectime Structure where timestamp (expressed in local time or in UTC
1006 * depending on whether options -T/-t have been used or not) can
1007 * be saved for current record.
1008 * @dparm PCP archive file name.
1009 ***************************************************************************
1011 void logic1_display_loop(int ifd, char *file, struct file_activity *file_actlst,
1012 struct file_magic *file_magic, struct tm *rectime, void *dparm)
1014 int curr, rtype, tab = 0;
1015 int eosaf, next, reset = FALSE;
1016 int ign_flag = IGNORE_COMMENT + IGNORE_RESTART;
1018 char *pcparchive = (char *) dparm;
1020 if (CREATE_ITEM_LIST(fmt[f_position]->options)) {
1021 /* Count items in file (e.g. for PCP output) */
1022 if (!count_file_items(ifd, file, file_magic, file_actlst, rectime))
1023 /* No record to display */
1026 /* Save current file position */
1027 seek_file_position(ifd, DO_SAVE);
1029 /* Print header (eg. XML file header) */
1030 if (*fmt[f_position]->f_header) {
1031 (*fmt[f_position]->f_header)(&tab, F_BEGIN, pcparchive, file_magic,
1032 &file_hdr, act, id_seq, file_actlst);
1035 if (ORDER_ALL_RECORDS(fmt[f_position]->options)) {
1036 ign_flag = IGNORE_NOTHING;
1038 /* RESTART and COMMENTS records will be immediately processed */
1039 if (*fmt[f_position]->f_restart) {
1040 (*fmt[f_position]->f_restart)(&tab, F_BEGIN, NULL, NULL, FALSE,
1043 if (DISPLAY_COMMENT(flags) && (*fmt[f_position]->f_comment)) {
1044 (*fmt[f_position]->f_comment)(&tab, F_BEGIN, NULL, NULL, 0, NULL,
1049 /* Process activities */
1050 if (*fmt[f_position]->f_statistics) {
1051 (*fmt[f_position]->f_statistics)(&tab, F_BEGIN, act, id_seq);
1056 * If this record is a special (RESTART or COMMENT) one,
1057 * process it then try to read the next record in file.
1060 eosaf = read_next_sample(ifd, ign_flag, 0, file,
1061 &rtype, tab, file_magic, file_actlst,
1062 rectime, UEOF_STOP);
1064 while (!eosaf && ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
1065 (tm_start.use && (datecmp(rectime, &tm_start, FALSE) < 0)) ||
1066 (tm_end.use && (datecmp(rectime, &tm_end, FALSE) >= 0))));
1075 /* Save the first stats collected. Used for example in next_slice() function */
1076 copy_structures(act, id_seq, record_hdr, 2, 0);
1079 eosaf = read_next_sample(ifd, ign_flag, curr, file,
1080 &rtype, tab, file_magic, file_actlst,
1081 rectime, UEOF_CONT);
1083 if (!eosaf && (rtype != R_COMMENT) && (rtype != R_RESTART)) {
1084 if (*fmt[f_position]->f_statistics) {
1085 (*fmt[f_position]->f_statistics)(&tab, F_MAIN, act, id_seq);
1088 /* next is set to 1 when we were close enough to desired interval */
1089 next = generic_write_stats(curr, tm_start.use, tm_end.use, reset,
1090 &cnt, &tab, rectime, FALSE, ALL_ACTIVITIES);
1101 while (cnt && !eosaf && (rtype != R_RESTART));
1104 /* Go to next Linux restart, if possible */
1106 eosaf = read_next_sample(ifd, ign_flag, curr, file,
1107 &rtype, tab, file_magic, file_actlst,
1108 rectime, UEOF_CONT);
1110 while (!eosaf && (rtype != R_RESTART));
1116 if (*fmt[f_position]->f_statistics) {
1117 (*fmt[f_position]->f_statistics)(&tab, F_END, act, id_seq);
1120 if (ign_flag == IGNORE_NOTHING) {
1122 * RESTART and COMMENT records have already been processed.
1123 * Display possible trailing data then terminate.
1125 if (*fmt[f_position]->f_restart) {
1126 (*fmt[f_position]->f_restart)(&tab, F_END, NULL, NULL,
1127 FALSE, &file_hdr, NULL);
1129 if (DISPLAY_COMMENT(flags) && (*fmt[f_position]->f_comment)) {
1130 (*fmt[f_position]->f_comment)(&tab, F_END, NULL, NULL, 0, NULL,
1137 seek_file_position(ifd, DO_RESTORE);
1139 /* Process now RESTART entries to display restart messages */
1140 if (*fmt[f_position]->f_restart) {
1141 (*fmt[f_position]->f_restart)(&tab, F_BEGIN, NULL, NULL, FALSE,
1146 eosaf = read_next_sample(ifd, IGNORE_COMMENT, 0,
1147 file, &rtype, tab, file_magic, file_actlst,
1148 rectime, UEOF_CONT);
1152 if (*fmt[f_position]->f_restart) {
1153 (*fmt[f_position]->f_restart)(&tab, F_END, NULL, NULL, FALSE, &file_hdr, NULL);
1157 seek_file_position(ifd, DO_RESTORE);
1159 /* Last, process COMMENT entries to display comments */
1160 if (DISPLAY_COMMENT(flags)) {
1161 if (*fmt[f_position]->f_comment) {
1162 (*fmt[f_position]->f_comment)(&tab, F_BEGIN, NULL, NULL, 0, NULL,
1166 eosaf = read_next_sample(ifd, IGNORE_RESTART, 0,
1167 file, &rtype, tab, file_magic, file_actlst,
1168 rectime, UEOF_CONT);
1172 if (*fmt[f_position]->f_comment) {
1173 (*fmt[f_position]->f_comment)(&tab, F_END, NULL, NULL, 0, NULL,
1179 /* Print header trailer */
1180 if (*fmt[f_position]->f_header) {
1181 (*fmt[f_position]->f_header)(&tab, F_END, pcparchive, file_magic,
1182 &file_hdr, act, id_seq, file_actlst);
1187 ***************************************************************************
1188 * Display file contents in selected format (logic #2).
1189 * Logic #2: Grouped by activity. Sorted by timestamp. Stop on RESTART
1191 * Formats: ppc, CSV, raw
1193 * NB: All statistics data for one activity will be displayed before
1194 * displaying stats for next activity. This is what sar does in its report.
1195 * Example: If stats for activities A and B at time t and t' have been collected,
1196 * the output will be:
1197 * stats for activity A at t
1198 * stats for activity A at t'
1199 * stats for activity B at t
1200 * stats for activity B at t'
1203 * @ifd File descriptor of input file.
1204 * @file Name of file being read.
1205 * @file_actlst List of (known or unknown) activities in file.
1206 * @file_magic file_magic structure filled with file magic header data.
1207 * @rectime Structure where timestamp (expressed in local time or in UTC
1208 * depending on whether options -T/-t have been used or not) can
1209 * be saved for current record.
1210 * @dparm Unused here.
1211 ***************************************************************************
1213 void logic2_display_loop(int ifd, char *file, struct file_activity *file_actlst,
1214 struct file_magic *file_magic, struct tm *rectime, void *dparm)
1217 int curr = 1, rtype;
1218 int eosaf = TRUE, reset = FALSE;
1221 /* Read system statistics from file */
1224 * If this record is a special (RESTART or COMMENT) one, print it and
1225 * (try to) get another one.
1228 if (read_next_sample(ifd, IGNORE_NOTHING, 0,
1229 file, &rtype, 0, file_magic, file_actlst,
1230 rectime, UEOF_STOP))
1231 /* End of sa data file */
1234 while ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
1235 (tm_start.use && (datecmp(rectime, &tm_start, FALSE) < 0)) ||
1236 (tm_end.use && (datecmp(rectime, &tm_end, FALSE) >= 0)));
1238 /* Save the first stats collected. Used for example in next_slice() function */
1239 copy_structures(act, id_seq, record_hdr, 2, 0);
1241 /* Set flag to reset last_uptime variable. Should be done after a LINUX RESTART record */
1244 /* Save current file position */
1245 seek_file_position(ifd, DO_SAVE);
1247 /* Read and write stats located between two possible Linux restarts */
1249 if (DISPLAY_HORIZONTALLY(flags)) {
1251 * If stats are displayed horizontally, then all activities
1252 * are printed on the same line.
1254 rw_curr_act_stats(ifd, &curr, &cnt, &eosaf,
1255 ALL_ACTIVITIES, &reset, file_actlst,
1256 rectime, file, file_magic);
1259 /* For each requested activity... */
1260 for (i = 0; i < NR_ACT; i++) {
1265 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
1266 if (!IS_SELECTED(act[p]->options))
1269 if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) {
1270 rw_curr_act_stats(ifd, &curr, &cnt, &eosaf,
1271 act[p]->id, &reset, file_actlst,
1272 rectime, file, file_magic);
1275 unsigned int optf, msk;
1277 optf = act[p]->opt_flags;
1279 for (msk = 1; msk < 0x100; msk <<= 1) {
1280 if ((act[p]->opt_flags & 0xff) & msk) {
1281 act[p]->opt_flags &= (0xffffff00 + msk);
1283 rw_curr_act_stats(ifd, &curr, &cnt, &eosaf,
1284 act[p]->id, &reset, file_actlst,
1287 act[p]->opt_flags = optf;
1295 /* Go to next Linux restart, if possible */
1297 eosaf = read_next_sample(ifd, IGNORE_RESTART | DONT_READ_CPU_NR,
1298 curr, file, &rtype, 0, file_magic,
1299 file_actlst, rectime, UEOF_STOP);
1301 while (!eosaf && (rtype != R_RESTART));
1305 * The last record we read was a RESTART one: Print it.
1306 * NB: Unlike COMMENTS records (which are displayed for each
1307 * activity), RESTART ones are only displayed once.
1309 if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) {
1310 print_special_record(&record_hdr[curr], flags, &tm_start, &tm_end,
1311 R_RESTART, ifd, rectime, file, 0,
1312 file_magic, &file_hdr, act, fmt[f_position],
1313 endian_mismatch, arch_64);
1320 ***************************************************************************
1321 * Display file contents in SVG format.
1324 * @ifd File descriptor of input file.
1325 * @file Name of file being read.
1326 * @file_actlst List of (known or unknown) activities in file.
1327 * @file_magic file_magic structure filled with file magic header data.
1328 * @rectime Structure where timestamp (expressed in local time or in UTC
1329 * depending on whether options -T/-t have been used or not) can
1330 * be saved for current record.
1331 * @dparm Unused here.
1332 ***************************************************************************
1334 void svg_display_loop(int ifd, char *file, struct file_activity *file_actlst,
1335 struct file_magic *file_magic, struct tm *rectime, void *dparm)
1337 struct svg_hdr_parm parm;
1339 int curr = 1, rtype, g_nr = 0, views_per_row = 1, nr_act_dispd;
1340 int eosaf = TRUE, reset = TRUE;
1344 /* Init custom colors palette */
1345 init_custom_color_palette();
1348 * Calculate the number of rows and the max number of views per row to display.
1349 * Result may be 0. In this case, "No data" will be displayed instead of the graphs.
1351 graph_nr = get_svg_graph_nr(ifd, file, file_magic,
1352 file_actlst, rectime, &views_per_row, &nr_act_dispd);
1354 if (SET_CANVAS_HEIGHT(flags)) {
1356 * Option "-O height=..." used: @graph_nr is NO LONGER a number
1357 * of graphs but the SVG canvas height set on the command line.
1359 graph_nr = canvas_height;
1363 parm.graph_nr = graph_nr;
1364 parm.views_per_row = PACK_VIEWS(flags) ? views_per_row : 1;
1365 parm.nr_act_dispd = nr_act_dispd;
1367 /* Print SVG header */
1368 if (*fmt[f_position]->f_header) {
1369 (*fmt[f_position]->f_header)(&parm, F_BEGIN + F_MAIN, file, file_magic,
1370 &file_hdr, act, id_seq, file_actlst);
1374 * If this record is a special (RESTART or COMMENT) one, ignore it and
1375 * (try to) get another one.
1378 if (read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT, 0,
1379 file, &rtype, 0, file_magic, file_actlst,
1380 rectime, UEOF_CONT))
1382 /* End of sa data file: No views displayed */
1387 while ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
1388 (tm_start.use && (datecmp(rectime, &tm_start, FALSE) < 0)) ||
1389 (tm_end.use && (datecmp(rectime, &tm_end, FALSE) >= 0)));
1391 /* Save the first stats collected. Used for example in next_slice() function */
1392 copy_structures(act, id_seq, record_hdr, 2, 0);
1394 /* Save current file position */
1395 seek_file_position(ifd, DO_SAVE);
1397 /* For each requested activity, display graphs */
1398 for (i = 0; i < NR_ACT; i++) {
1403 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
1404 if (!IS_SELECTED(act[p]->options) || !act[p]->g_nr)
1407 if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) {
1408 display_curr_act_graphs(ifd, &curr, &cnt, &eosaf,
1409 p, &reset, file_actlst,
1411 file_magic, &g_nr, nr_act_dispd);
1414 unsigned int optf, msk;
1416 optf = act[p]->opt_flags;
1418 for (msk = 1; msk < 0x100; msk <<= 1) {
1419 if ((act[p]->opt_flags & 0xff) & msk) {
1420 act[p]->opt_flags &= (0xffffff00 + msk);
1421 display_curr_act_graphs(ifd, &curr, &cnt, &eosaf,
1422 p, &reset, file_actlst,
1424 file_magic, &g_nr, nr_act_dispd);
1425 act[p]->opt_flags = optf;
1431 /* Real number of graphs that have been displayed */
1432 parm.graph_nr = g_nr;
1435 /* Print SVG trailer */
1436 if (*fmt[f_position]->f_header) {
1437 (*fmt[f_position]->f_header)(&parm, F_END, file, file_magic,
1438 &file_hdr, act, id_seq, file_actlst);
1443 ***************************************************************************
1444 * Check system activity datafile contents before displaying stats.
1445 * Display file header if option -H has been entered, else call function
1446 * corresponding to selected output format.
1449 * @dfile System activity data file name.
1450 * @pcparchive PCP archive file name.
1451 ***************************************************************************
1453 void read_stats_from_file(char dfile[], char pcparchive[])
1455 struct file_magic file_magic;
1456 struct file_activity *file_actlst = NULL;
1460 /* Prepare file for reading and read its headers */
1461 check_file_actlst(&ifd, dfile, act, flags, &file_magic, &file_hdr,
1462 &file_actlst, id_seq, &endian_mismatch, &arch_64);
1464 if (DISPLAY_HDR_ONLY(flags)) {
1465 if (*fmt[f_position]->f_header) {
1466 if (format == F_PCP_OUTPUT) {
1469 /* Display only data file header then exit */
1470 (*fmt[f_position]->f_header)(&tab, F_BEGIN + F_END, dfile, &file_magic,
1471 &file_hdr, act, id_seq, file_actlst);
1476 /* Perform required allocations */
1477 allocate_structures(act);
1479 if (SET_LC_NUMERIC_C(fmt[f_position]->options)) {
1480 /* Use a decimal point */
1481 setlocale(LC_NUMERIC, "C");
1484 /* Call function corresponding to selected output format */
1485 if (*fmt[f_position]->f_display) {
1486 (*fmt[f_position]->f_display)(ifd, dfile, file_actlst, &file_magic,
1487 &rectime, pcparchive);
1493 free_structures(act);
1497 ***************************************************************************
1498 * Main entry to the sadf program
1499 ***************************************************************************
1501 int main(int argc, char **argv)
1503 int opt = 1, sar_options = 0;
1506 char dfile[MAX_FILE_LEN], pcparchive[MAX_FILE_LEN];
1509 /* Compute page shift in kB */
1512 dfile[0] = pcparchive[0] = '\0';
1515 /* Init National Language Support */
1519 tm_start.use = tm_end.use = FALSE;
1521 /* Allocate and init activity bitmaps */
1522 allocate_bitmaps(act);
1524 /* Init some structures */
1527 /* Process options */
1528 while (opt < argc) {
1530 if (!strcmp(argv[opt], "-P")) {
1531 if (parse_sa_P_opt(argv, &opt, &flags, act)) {
1536 else if (!strncmp(argv[opt], "--dev=", 6)) {
1537 /* Parse devices entered on the command line */
1538 p = get_activity_position(act, A_DISK, EXIT_IF_NOT_FOUND);
1539 parse_sa_devices(argv[opt], act[p], MAX_DEV_LEN, &opt, 6, NO_RANGE);
1542 else if (!strncmp(argv[opt], "--fs=", 5)) {
1543 /* Parse devices entered on the command line */
1544 p = get_activity_position(act, A_FS, EXIT_IF_NOT_FOUND);
1545 parse_sa_devices(argv[opt], act[p], MAX_FS_LEN, &opt, 5, NO_RANGE);
1548 else if (!strncmp(argv[opt], "--iface=", 8)) {
1549 /* Parse devices entered on the command line */
1550 p = get_activity_position(act, A_NET_DEV, EXIT_IF_NOT_FOUND);
1551 parse_sa_devices(argv[opt], act[p], MAX_IFACE_LEN, &opt, 8, NO_RANGE);
1552 q = get_activity_position(act, A_NET_EDEV, EXIT_IF_NOT_FOUND);
1553 act[q]->item_list = act[p]->item_list;
1554 act[q]->item_list_sz = act[p]->item_list_sz;
1555 act[q]->options |= AO_LIST_ON_CMDLINE;
1558 else if (!strncmp(argv[opt], "--int=", 6)) {
1559 /* Parse interrupts names entered on the command line */
1560 p = get_activity_position(act, A_IRQ, EXIT_IF_NOT_FOUND);
1561 parse_sa_devices(argv[opt], act[p], MAX_SA_IRQ_LEN, &opt, 6, NR_IRQS);
1564 else if (!strcmp(argv[opt], "-s")) {
1565 /* Get time start */
1566 if (parse_timestamp(argv, &opt, &tm_start, DEF_TMSTART)) {
1571 else if (!strcmp(argv[opt], "-e")) {
1573 if (parse_timestamp(argv, &opt, &tm_end, DEF_TMEND)) {
1579 else if (!strncmp(argv[opt], "--getenv", 8)) {
1585 else if (!strcmp(argv[opt], "-O")) {
1586 /* Parse output options */
1587 if (!argv[++opt] || sar_options) {
1590 for (t = strtok(argv[opt], ","); t; t = strtok(NULL, ",")) {
1591 if (!strcmp(t, K_SKIP_EMPTY)) {
1592 flags |= S_F_SVG_SKIP;
1594 else if (!strcmp(t, K_AUTOSCALE)) {
1595 flags |= S_F_SVG_AUTOSCALE;
1597 else if (!strcmp(t, K_ONEDAY)) {
1598 flags |= S_F_SVG_ONE_DAY;
1600 else if (!strcmp(t, K_SHOWIDLE)) {
1601 flags |= S_F_SVG_SHOW_IDLE;
1603 else if (!strcmp(t, K_SHOWINFO)) {
1604 flags |= S_F_SVG_SHOW_INFO;
1606 else if (!strcmp(t, K_DEBUG)) {
1607 flags |= S_F_DEBUG_MODE;
1609 else if (!strncmp(t, K_HEIGHT, strlen(K_HEIGHT))) {
1610 v = t + strlen(K_HEIGHT);
1611 if (!strlen(v) || (strspn(v, DIGITS) != strlen(v))) {
1614 canvas_height = atoi(v);
1615 flags |= S_F_SVG_HEIGHT;
1617 else if (!strcmp(t, K_PACKED)) {
1618 flags |= S_F_SVG_PACKED;
1620 else if (!strcmp(t, K_SHOWTOC)) {
1621 flags |= S_F_SVG_SHOW_TOC;
1623 else if (!strcmp(t, K_CUSTOMCOL)) {
1624 palette = SVG_CUSTOM_COL_PALETTE;
1626 else if (!strcmp(t, K_BWCOL)) {
1627 palette = SVG_BW_COL_PALETTE;
1629 else if (!strncmp(t, K_PCPARCHIVE, strlen(K_PCPARCHIVE))) {
1630 v = t + strlen(K_PCPARCHIVE);
1631 strncpy(pcparchive, v, sizeof(pcparchive));
1632 pcparchive[sizeof(pcparchive) - 1] = '\0';
1634 else if (!strncmp(t, K_HZ, strlen(K_HZ))) {
1635 v = t + strlen(K_HZ);
1636 if (!strlen(v) || (strspn(v, DIGITS) != strlen(v))) {
1648 else if ((strlen(argv[opt]) > 1) &&
1649 (strlen(argv[opt]) < 4) &&
1650 !strncmp(argv[opt], "-", 1) &&
1651 (strspn(argv[opt] + 1, DIGITS) == (strlen(argv[opt]) - 1))) {
1652 if (dfile[0] || day_offset) {
1653 /* File already specified */
1656 day_offset = atoi(argv[opt++] + 1);
1659 else if (!strcmp(argv[opt], "--")) {
1664 else if (!strcmp(argv[opt], "-m")) {
1665 if (!argv[++opt] || !sar_options) {
1668 /* Parse sar's option -m */
1669 if (parse_sar_m_opt(argv, &opt, act)) {
1674 else if (!strcmp(argv[opt], "-n")) {
1675 if (!argv[++opt] || !sar_options) {
1678 /* Parse sar's option -n */
1679 if (parse_sar_n_opt(argv, &opt, act)) {
1684 else if (!strcmp(argv[opt], "-q")) {
1688 else if (!argv[++opt]) {
1689 SELECT_ACTIVITY(A_QUEUE);
1691 /* Parse option -q */
1692 else if (parse_sar_q_opt(argv, &opt, act)) {
1693 SELECT_ACTIVITY(A_QUEUE);
1697 else if (!strncmp(argv[opt], "-", 1)) {
1698 /* Other options not previously tested */
1700 if ((rc = parse_sar_opt(argv, &opt, act, &flags, C_SADF)) != 0) {
1709 for (i = 1; *(argv[opt] + i); i++) {
1711 switch (*(argv[opt] + i)) {
1714 flags |= S_F_COMMENT;
1721 format = F_CONV_OUTPUT;
1728 format = F_DB_OUTPUT;
1735 format = F_SVG_OUTPUT;
1739 flags |= S_F_HORIZONTALLY;
1743 flags |= S_F_HDR_ONLY;
1750 format = F_JSON_OUTPUT;
1757 format = F_PCP_OUTPUT;
1764 format = F_PPC_OUTPUT;
1771 format = F_RAW_OUTPUT;
1775 flags |= S_F_LOCAL_TIME;
1779 flags |= S_F_TRUE_TIME;
1783 flags |= S_F_SEC_EPOCH;
1790 format = F_XML_OUTPUT;
1805 /* Get data file name */
1806 else if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1807 if (dfile[0] || day_offset) {
1808 /* File already specified */
1811 /* Write data to file */
1812 strncpy(dfile, argv[opt++], sizeof(dfile));
1813 dfile[sizeof(dfile) - 1] = '\0';
1814 /* Check if this is an alternate directory for sa files */
1815 check_alt_sa_dir(dfile, 0, -1);
1818 else if (interval < 0) {
1820 if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1823 interval = atol(argv[opt++]);
1830 /* Get count value */
1831 if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1835 /* Count parameter already set */
1838 count = atol(argv[opt++]);
1843 count = -1; /* To generate a report continuously */
1848 if (USE_OPTION_A(flags)) {
1849 /* Set -P ALL -I ALL if needed */
1850 set_bitmaps(act, &flags);
1853 /* sadf reads current daily data file by default */
1855 set_default_file(dfile, day_offset, -1);
1858 /* PCP mode: If no archive file specified then use the name of the daily data file */
1859 if (!pcparchive[0] && (format == F_PCP_OUTPUT)) {
1860 strcpy(pcparchive, dfile);
1864 if (format == F_PCP_OUTPUT) {
1865 fprintf(stderr, _("PCP support not compiled in\n"));
1870 if (tm_start.use && tm_end.use && (tm_end.tm_hour < tm_start.tm_hour)) {
1871 tm_end.tm_hour += 24;
1874 if (DISPLAY_PRETTY(flags)) {
1875 dm_major = get_devmap_major();
1878 /* Options -T, -t and -U are mutually exclusive */
1879 if ((PRINT_LOCAL_TIME(flags) + PRINT_TRUE_TIME(flags) +
1880 PRINT_SEC_EPOCH(flags)) > 1) {
1885 * Display all the contents of the daily data file if the count parameter
1886 * was not set on the command line.
1892 /* Default is CPU activity */
1893 select_default_activity(act);
1895 /* Check options consistency with selected output format. Default is PPC display */
1896 check_format_options();
1902 if (format == F_CONV_OUTPUT) {
1903 /* Convert file to current format */
1904 convert_file(dfile, act);
1907 /* Read stats from file */
1908 read_stats_from_file(dfile, pcparchive);