]> granicus.if.org Git - sysstat/blob - sadf.c
A_IRQ: Update sadf RAW output report
[sysstat] / sadf.c
1 /*
2  * sadf: system activity data formatter
3  * (C) 1999-2021 by Sebastien GODARD (sysstat <at> orange.fr)
4  *
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.                                              *
10  *                                                                         *
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 *
14  * for more details.                                                       *
15  *                                                                         *
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  ***************************************************************************
20  */
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <stdint.h>
26 #include <stdarg.h>
27 #include <unistd.h>
28 #include <time.h>
29 #include <errno.h>
30
31 #include "version.h"
32 #include "sadf.h"
33
34 # include <locale.h>    /* For setlocale() */
35 #ifdef USE_NLS
36 # include <libintl.h>
37 # define _(string) gettext(string)
38 #else
39 # define _(string) (string)
40 #endif
41
42 #ifdef HAVE_PCP
43 #include <pcp/pmapi.h>
44 #include <pcp/import.h>
45 #endif
46
47 #ifdef USE_SCCSID
48 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
49 char *sccsid(void) { return (SCCSID); }
50 #endif
51
52 #ifdef TEST
53 extern int __env;
54 void int_handler(int n) { return; }
55 #endif
56
57 long interval = -1, count = 0;
58
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 */
62 int arch_64 = FALSE;
63 /* Number of decimal places */
64 int dplaces_nr = -1;
65 /* Color palette number */
66 int palette = SVG_DEFAULT_COL_PALETTE;
67
68 uint64_t flags = 0;
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 */
74
75 /* File header */
76 struct file_header file_hdr;
77
78 /*
79  * Activity sequence.
80  * This array must always be entirely filled (even with trailing zeros).
81  */
82 unsigned int id_seq[NR_ACT];
83
84 /* Current record header */
85 struct record_header record_hdr[3];
86
87 /* Contain the date specified by -s and -e options */
88 struct tstamp tm_start, tm_end;
89 char *args[MAX_ARGV_NR];
90
91 extern struct activity *act[];
92 extern struct report_format *fmt[];
93
94 /*
95  ***************************************************************************
96  * Print usage and exit.
97  *
98  * IN:
99  * @progname    Name of sysstat command.
100  ***************************************************************************
101  */
102 void usage(char *progname)
103 {
104         fprintf(stderr,
105                 _("Usage: %s [ options ] [ <interval> [ <count> ] ] [ <datafile> | -[0-9]+ ]\n"),
106                 progname);
107
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"));
114         exit(1);
115 }
116
117 /*
118  ***************************************************************************
119  * Init structures.
120  ***************************************************************************
121  */
122 void init_structures(void)
123 {
124         int i;
125
126         for (i = 0; i < 3; i++) {
127                 memset(&record_hdr[i], 0, RECORD_HEADER_SIZE);
128         }
129 }
130
131 /*
132  ***************************************************************************
133  * Look for output format in array.
134  *
135  * IN:
136  * @oformat     Output format to look for.
137  *
138  * RETURNS:
139  * Position of output format in array.
140  ***************************************************************************
141  */
142 int get_format_position(unsigned int oformat)
143 {
144         int i;
145
146         for (i = 0; i < NR_FMT; i++) {
147                 if (fmt[i]->id == oformat)
148                         break;
149         }
150
151         if (i == NR_FMT)
152                 /* Should never happen */
153                 return 0;
154
155         return i;
156 }
157
158 /*
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  ***************************************************************************
164  */
165 void check_format_options(void)
166 {
167         if (!format) {
168                 /* Select output format if none has been selected */
169                 if (DISPLAY_HDR_ONLY(flags)) {
170                         format = F_HEADER_OUTPUT;
171                 }
172                 else {
173                         format = F_PPC_OUTPUT;
174                 }
175         }
176
177         /* Get format position in array */
178         f_position = get_format_position(format);
179
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;
184         }
185         if (!ACCEPT_HORIZONTALLY(fmt[f_position]->options)) {
186                 /* Remove option -h */
187                 flags &= ~S_F_HORIZONTALLY;
188         }
189         if (!ACCEPT_LOCAL_TIME(fmt[f_position]->options)) {
190                 /* Remove option -T */
191                 flags &= ~S_F_LOCAL_TIME;
192         }
193         if (!ACCEPT_SEC_EPOCH(fmt[f_position]->options)) {
194                 /* Remove option -U */
195                 flags &= ~S_F_SEC_EPOCH;
196         }
197         if (REJECT_TRUE_TIME(fmt[f_position]->options)) {
198                 /* Remove option -t */
199                 flags &= ~S_F_TRUE_TIME;
200         }
201 }
202
203 /*
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.
207  *
208  * IN:
209  * @ifd         File descriptor
210  * @action      Flags indicating if special records should be displayed or
211  *              not.
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.
222  *
223  * OUT:
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.
230  *
231  * RETURNS:
232  * 1 if EOF has been reached,
233  * 2 if an unexpected EOF has been reached, or an error occurred.
234  * 0 otherwise.
235  ***************************************************************************
236  */
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)
240 {
241         int rc;
242         char rec_hdr_tmp[MAX_RECORD_HEADER_SIZE];
243
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)
248                 /* End of sa file */
249                 return rc;
250
251         *rtype = record_hdr[curr].record_type;
252
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)
258                                         return 2;
259                                 close(ifd);
260                                 exit(2);
261                         }
262
263                         /* Ignore unknown extra structures if present */
264                         if (record_hdr[curr].extra_next && (skip_extra_struct(ifd, endian_mismatch, arch_64) < 0))
265                                 return 2;
266
267                         if (action & SET_TIMESTAMPS) {
268                                 if (sa_get_record_timestamp_struct(flags, &record_hdr[curr],
269                                                                    rectime))
270                                         return 2;
271                         }
272                 }
273                 else {
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);
279                 }
280         }
281         else if (*rtype == R_RESTART) {
282                 if (action & IGNORE_RESTART) {
283                         /*
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).
287                          */
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);
291
292                                 /* Ignore unknown extra structures if present */
293                                 if (record_hdr[curr].extra_next && (skip_extra_struct(ifd, endian_mismatch, arch_64) < 0))
294                                         return 2;
295                         }
296                         if (action & SET_TIMESTAMPS) {
297                                 if (sa_get_record_timestamp_struct(flags, &record_hdr[curr], rectime))
298                                         return 2;
299                         }
300                 }
301                 else {
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);
307                 }
308         }
309         else {
310                 /*
311                  * OK: Previous record was not a special one.
312                  * So read now the extra fields.
313                  */
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)
316                         return 2;
317                 if (sa_get_record_timestamp_struct(flags, &record_hdr[curr], rectime))
318                         return 2;
319         }
320
321         return 0;
322 }
323
324 /*
325  ***************************************************************************
326  * Display the field list (used eg. in database format).
327  *
328  * IN:
329  * @act_id      Activity to display, or ~0 for all.
330  ***************************************************************************
331  */
332 void list_fields(unsigned int act_id)
333 {
334         int i, j;
335         unsigned int msk;
336         char *hl;
337         char hline[HEADER_LINE_LEN] = "";
338
339         printf("# hostname;interval;timestamp");
340
341         for (i = 0; i < NR_ACT; i++) {
342
343                 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
344                         continue;
345
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)) {
350                                         printf("[...]");
351                                 }
352                         }
353                         else {
354                                 msk = 1;
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 */
363                                                                 *(hl + j) = ';';
364                                                                 printf(";%s", hl);
365                                                         }
366                                                         else {
367                                                                 /* Display only the first part of the header line */
368                                                                 *(hl + j) = '\0';
369                                                                 printf(";%s", hl);
370                                                         }
371                                                         *(hl + j) = '&';
372                                                 }
373                                                 else {
374                                                         printf(";%s", hl);
375                                                 }
376                                                 if ((act[i]->nr_ini > 1) && DISPLAY_HORIZONTALLY(flags)) {
377                                                         printf("[...]");
378                                                 }
379                                         }
380                                 }
381                         }
382                 }
383         }
384         printf("\n");
385 }
386
387 /*
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.
393  *
394  * RETURNS:
395  * Time origin on X axis (expressed in seconds since the epoch).
396  ***************************************************************************
397  */
398 time_t get_time_ref(void)
399 {
400         struct tm ltm;
401         time_t t;
402
403         if (DISPLAY_ONE_DAY(flags)) {
404                 localtime_r((time_t *) &(record_hdr[2].ust_time), &ltm);
405
406                 /* Move back to midnight */
407                 ltm.tm_sec = ltm.tm_min = ltm.tm_hour = 0;
408
409                 t = mktime(&ltm);
410                 if (t != -1)
411                         return t;
412         }
413
414         return (time_t) record_hdr[2].ust_time;
415 }
416
417 /*
418  ***************************************************************************
419  * Save or restore position in file.
420  *
421  * IN:
422  * @ifd         File descriptor of input file.
423  * @action      DO_SAVE to save position or DO_RESTORE to restore it.
424  ***************************************************************************
425  */
426 void seek_file_position(int ifd, int action)
427 {
428         static off_t fpos = -1;
429         static unsigned int save_cpu_nr = 0;
430
431         if (action == DO_SAVE) {
432                 /* Save current file position */
433                 if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) {
434                         perror("lseek");
435                         exit(2);
436                 }
437                 save_cpu_nr = file_hdr.sa_cpu_nr;
438         }
439         else if (action == DO_RESTORE) {
440                 /* Rewind file */
441                 if ((fpos < 0) || (lseek(ifd, fpos, SEEK_SET) < fpos)) {
442                         perror("lseek");
443                         exit(2);
444                 }
445                 file_hdr.sa_cpu_nr = save_cpu_nr;
446         }
447 }
448
449 /*
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.
454  *
455  * IN:
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.
463  *
464  * RETURNS:
465  * 0 if no records are concerned in file, and 1 otherwise.
466  ***************************************************************************
467  */
468 int count_file_items(int ifd, char *file, struct file_magic *file_magic,
469                       struct file_activity *file_actlst, struct tm *rectime)
470 {
471         int i, eosaf, rtype;
472
473         /* Save current file position */
474         seek_file_position(ifd, DO_SAVE);
475
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;
480                 }
481         }
482
483         /* Look for the first record that will be displayed */
484         do {
485                 eosaf = read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT | SET_TIMESTAMPS,
486                                          0, file, &rtype, 0, file_magic, file_actlst,
487                                          rectime, UEOF_CONT);
488                 if (eosaf)
489                         /* No record to display */
490                         return 0;
491         }
492         while ((tm_start.use && (datecmp(rectime, &tm_start, FALSE) < 0)) ||
493                (tm_end.use && (datecmp(rectime, &tm_end, FALSE) >= 0)));
494
495         /*
496          * Read all the file and determine the maximum number
497          * of items for each activity.
498          */
499         do {
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);
504                                 }
505                                 else if (act[i]->nr[0] > act[i]->item_list_sz) {
506                                         act[i]->item_list_sz = act[i]->nr[0];
507                                 }
508                         }
509                 }
510
511                 do {
512                         eosaf = read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT | SET_TIMESTAMPS,
513                                                  0, file, &rtype, 0, file_magic, file_actlst,
514                                                  rectime, UEOF_CONT);
515                         if (eosaf ||
516                             (tm_end.use && (datecmp(rectime, &tm_end, FALSE) >= 0)))
517                                 /* End of data file or end time exceeded */
518                                 break;
519                 }
520                 while ((rtype == R_RESTART) || (rtype == R_COMMENT));
521         }
522         while (!eosaf && !(tm_end.use && (datecmp(rectime, &tm_end, FALSE) >= 0)));
523
524         /* Rewind file */
525         seek_file_position(ifd, DO_RESTORE);
526
527         return 1;
528 }
529
530 /*
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
537  * the file.
538  *
539  * 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.
548  *
549  * OUT:
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.
553  *                      May be 0.
554  *
555  * RETURNS:
556  * Number of rows containing views, taking into account only activities
557  * to be displayed, and selected period of time (options -s/-e).
558  * Result may be 0.
559  ***************************************************************************
560  */
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)
564 {
565         int i, n, p, tot_g_nr = 0;
566
567         *nr_act_dispd = 0;
568
569         /* Count items in file */
570         if (!count_file_items(ifd, file, file_magic, file_actlst, rectime))
571                 /* No record to display => No graph */
572                 return 0;
573
574         for (i = 0; i < NR_ACT; i++) {
575                 if (!id_seq[i])
576                         continue;
577
578                 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
579                 if (!IS_SELECTED(act[p]->options) || !act[p]->g_nr)
580                         continue;
581
582                 (*nr_act_dispd)++;
583
584                 if (PACK_VIEWS(flags)) {
585                         /*
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.
590                          */
591                         if ((act[p]->id == A_MEMORY) &&
592                             (DISPLAY_MEMORY(act[p]->opt_flags) && DISPLAY_SWAP(act[p]->opt_flags))) {
593                                 n = 2;
594                         }
595                         else {
596                                 n = 1;
597                         }
598                 }
599                 else {
600                         /* One activity = multiple rows with only one view */
601                         n = act[p]->g_nr;
602                 }
603                 if (ONE_GRAPH_PER_ITEM(act[p]->options)) {
604                          n = n * act[p]->item_list_sz;
605                 }
606                 if (act[p]->g_nr > *views_per_row) {
607                         *views_per_row = act[p]->g_nr;
608                 }
609
610                 tot_g_nr += n;
611         }
612
613         if (*views_per_row > MAX_VIEWS_ON_A_ROW) {
614                 *views_per_row = MAX_VIEWS_ON_A_ROW;
615         }
616
617         return tot_g_nr;
618 }
619 /*
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).
625  *
626  * IN:
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.
641  *
642  * OUT:
643  * @cnt                 Set to 0 to indicate that no other lines of stats
644  *                      should be displayed.
645  *
646  * RETURNS:
647  * 1 if stats have been successfully displayed.
648  ***************************************************************************
649  */
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)
653 {
654         int i;
655         unsigned long long dt, itv;
656         char cur_date[TIMESTAMP_LEN], cur_time[TIMESTAMP_LEN], *pre = NULL;
657         static int cross_day = FALSE;
658
659         if (reset_cd) {
660                 /*
661                  * See note in sar.c.
662                  * NB: Resetting cross_day is needed only if datafile
663                  * may be rewinded (eg. in db or ppc output formats).
664                  */
665                 cross_day = 0;
666         }
667
668         /*
669          * Check time (1).
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.
674          */
675         if (!next_slice(record_hdr[2].uptime_cs, record_hdr[curr].uptime_cs,
676                         reset, interval))
677                 /* Not close enough to desired interval */
678                 return 0;
679
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)) {
684                 cross_day = TRUE;
685         }
686
687         /* Check time (2) */
688         if (use_tm_end && (datecmp(rectime, &tm_end, cross_day) > 0)) {
689                 /* End time exceeded */
690                 *cnt = 0;
691                 return 0;
692         }
693
694         /* Get interval values in 1/100th of a second */
695         get_itv_value(&record_hdr[curr], &record_hdr[!curr], &itv);
696
697         dt = itv / 100;
698         /* Correct rounding error for dt */
699         if ((itv % 100) >= 50) {
700                 dt++;
701         }
702
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);
706
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);
710         }
711
712         /* Display statistics */
713         for (i = 0; i < NR_ACT; i++) {
714
715                 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
716                         continue;
717
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)) {
721
722                         if (format == F_JSON_OUTPUT) {
723                                 /* JSON output */
724                                 int *tab = (int *) parm;
725
726                                 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
727
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],
731                                                                                 &file_hdr, flags);
732                                         }
733                                 }
734                                 (*act[i]->f_json_print)(act[i], curr, *tab, itv);
735                         }
736
737                         else if (format == F_XML_OUTPUT) {
738                                 /* XML output */
739                                 int *tab = (int *) parm;
740
741                                 (*act[i]->f_xml_print)(act[i], curr, *tab, itv);
742                         }
743
744                         else if (format == F_SVG_OUTPUT) {
745                                 /* SVG output */
746                                 struct svg_parm *svg_p = (struct svg_parm *) parm;
747
748                                 svg_p->dt = (unsigned long) dt;
749                                 (*act[i]->f_svg_print)(act[i], curr, F_MAIN, svg_p, itv, &record_hdr[curr]);
750                         }
751
752                         else if (format == F_RAW_OUTPUT) {
753                                 /* 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);
757                                 }
758
759                                 if (IS_SELECTED(act[i]->options) && (act[i]->nr[curr] > 0)) {
760                                         (*act[i]->f_raw_print)(act[i], pre, curr);
761                                 }
762                         }
763
764                         else if (format == F_PCP_OUTPUT) {
765                                 /* PCP archive */
766                                 if (*act[i]->f_pcp_print) {
767                                         (*act[i]->f_pcp_print)(act[i], curr);
768                                 }
769                         }
770
771                         else {
772                                 /* Other output formats: db, ppc */
773                                 (*act[i]->f_render)(act[i], (format == F_DB_OUTPUT), pre, curr, itv);
774                         }
775                 }
776         }
777
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);
781         }
782
783         return 1;
784 }
785
786 /*
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.
791  *
792  * IN:
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.
802  *
803  * OUT:
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  ***************************************************************************
810  */
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)
815 {
816         int rtype;
817         int next, reset_cd;
818
819         /* Rewind file */
820         seek_file_position(ifd, DO_RESTORE);
821
822         if (DISPLAY_FIELD_LIST(fmt[f_position]->options)) {
823                 /* Print field list */
824                 list_fields(act_id);
825         }
826
827         /*
828          * Restore the first stats collected.
829          * Used to compute the rate displayed on the first line.
830          */
831         copy_structures(act, id_seq, record_hdr, !*curr, 2);
832
833         *cnt  = count;
834         reset_cd = 1;
835
836         do {
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);
841
842                 if (*eosaf || (rtype == R_RESTART))
843                         break;
844
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);
848                         reset_cd = 0;
849
850                         if (next) {
851                                 /*
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.
855                                  */
856                                 *curr ^= 1;
857                                 if (*cnt > 0) {
858                                         (*cnt)--;
859                                 }
860                         }
861                         *reset = FALSE;
862                 }
863         }
864         while (*cnt);
865
866         *reset = TRUE;
867 }
868
869 /*
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.
873  *
874  * IN:
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).
885  * @nr_act_dispd
886  *              Total number of activities that will be displayed.
887  *
888  * OUT:
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  ***************************************************************************
895  */
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)
900 {
901         struct svg_parm parm;
902         int rtype;
903         int next, reset_cd;
904
905         /* Rewind file */
906         seek_file_position(ifd, DO_RESTORE);
907
908         /*
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.
912          */
913         copy_structures(act, id_seq, record_hdr, !*curr, 2);
914
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;
918         parm.restart = TRUE;
919         parm.file_hdr = &file_hdr;
920         parm.nr_act_dispd = nr_act_dispd;
921
922         *cnt  = count;
923         reset_cd = 1;
924
925         /* Allocate graphs arrays */
926         (*act[p]->f_svg_print)(act[p], !*curr, F_BEGIN, &parm, 0, &record_hdr[!*curr]);
927
928         do {
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);
932                 if (*eosaf)
933                         break;
934
935                 if (rtype == R_RESTART) {
936                         parm.restart = TRUE;
937                         *reset = TRUE;
938                         /* Go to next statistics record, if possible */
939                         do {
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);
943                         }
944                         while (!*eosaf && ((rtype == R_RESTART) || (rtype == R_COMMENT)));
945
946                         *curr ^= 1;
947                 }
948                 else if (rtype != R_COMMENT) {
949
950                         next = generic_write_stats(*curr, tm_start.use, tm_end.use, *reset, cnt,
951                                                    &parm, rectime, reset_cd, act[p]->id);
952                         reset_cd = 0;
953                         if (next) {
954                                 /*
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.
958                                  */
959                                 parm.restart = FALSE;
960                                 parm.ust_time_end = record_hdr[*curr].ust_time;
961                                 *curr ^= 1;
962                                 if (*cnt > 0) {
963                                         (*cnt)--;
964                                 }
965                         }
966                         *reset = FALSE;
967                 }
968         }
969         while (!*eosaf && *cnt);
970
971         *reset = TRUE;
972
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);
977         }
978
979         /* Actually display graphs for current activity */
980         (*act[p]->f_svg_print)(act[p], *curr, F_END, &parm, 0, &record_hdr[!*curr]);
981
982         /* Update total number of graphs already displayed */
983         *g_nr = parm.graph_no;
984 }
985
986 /*
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
991  *
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'
999  *
1000  * IN:
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  ***************************************************************************
1010  */
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)
1013 {
1014         int curr, rtype, tab = 0;
1015         int eosaf, next, reset = FALSE;
1016         int ign_flag = IGNORE_COMMENT + IGNORE_RESTART;
1017         long cnt = 1;
1018         char *pcparchive = (char *) dparm;
1019
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 */
1024                         return;
1025         }
1026         /* Save current file position */
1027         seek_file_position(ifd, DO_SAVE);
1028
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);
1033         }
1034
1035         if (ORDER_ALL_RECORDS(fmt[f_position]->options)) {
1036                 ign_flag = IGNORE_NOTHING;
1037
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,
1041                                                       &file_hdr, NULL);
1042                 }
1043                 if (DISPLAY_COMMENT(flags) && (*fmt[f_position]->f_comment)) {
1044                         (*fmt[f_position]->f_comment)(&tab, F_BEGIN, NULL, NULL, 0, NULL,
1045                                                       &file_hdr, NULL);
1046                 }
1047         }
1048
1049         /* Process activities */
1050         if (*fmt[f_position]->f_statistics) {
1051                 (*fmt[f_position]->f_statistics)(&tab, F_BEGIN, act, id_seq);
1052         }
1053
1054         do {
1055                 /*
1056                  * If this record is a special (RESTART or COMMENT) one,
1057                  * process it then try to read the next record in file.
1058                  */
1059                 do {
1060                         eosaf = read_next_sample(ifd, ign_flag, 0, file,
1061                                                  &rtype, tab, file_magic, file_actlst,
1062                                                  rectime, UEOF_STOP);
1063                 }
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))));
1067
1068                 curr = 1;
1069                 cnt = count;
1070                 reset = TRUE;
1071
1072                 if (eosaf)
1073                         break;
1074
1075                 /* Save the first stats collected. Used for example in next_slice() function */
1076                 copy_structures(act, id_seq, record_hdr, 2, 0);
1077
1078                 do {
1079                         eosaf = read_next_sample(ifd, ign_flag, curr, file,
1080                                                  &rtype, tab, file_magic, file_actlst,
1081                                                  rectime, UEOF_CONT);
1082
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);
1086                                 }
1087
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);
1091
1092                                 if (next) {
1093                                         curr ^= 1;
1094                                         if (cnt > 0) {
1095                                                 cnt--;
1096                                         }
1097                                 }
1098                                 reset = FALSE;
1099                         }
1100                 }
1101                 while (cnt && !eosaf && (rtype != R_RESTART));
1102
1103                 if (!cnt) {
1104                         /* Go to next Linux restart, if possible */
1105                         do {
1106                                 eosaf = read_next_sample(ifd, ign_flag, curr, file,
1107                                                          &rtype, tab, file_magic, file_actlst,
1108                                                          rectime, UEOF_CONT);
1109                         }
1110                         while (!eosaf && (rtype != R_RESTART));
1111                 }
1112                 reset = TRUE;
1113         }
1114         while (!eosaf);
1115
1116         if (*fmt[f_position]->f_statistics) {
1117                 (*fmt[f_position]->f_statistics)(&tab, F_END, act, id_seq);
1118         }
1119
1120         if (ign_flag == IGNORE_NOTHING) {
1121                 /*
1122                  * RESTART and COMMENT records have already been processed.
1123                  * Display possible trailing data then terminate.
1124                  */
1125                 if (*fmt[f_position]->f_restart) {
1126                         (*fmt[f_position]->f_restart)(&tab, F_END, NULL, NULL,
1127                                                       FALSE, &file_hdr, NULL);
1128                 }
1129                 if (DISPLAY_COMMENT(flags) && (*fmt[f_position]->f_comment)) {
1130                         (*fmt[f_position]->f_comment)(&tab, F_END, NULL, NULL, 0, NULL,
1131                                                       &file_hdr, NULL);
1132                 }
1133                 goto terminate;
1134         }
1135
1136         /* Rewind file */
1137         seek_file_position(ifd, DO_RESTORE);
1138
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,
1142                                               &file_hdr, NULL);
1143         }
1144
1145         do {
1146                 eosaf = read_next_sample(ifd, IGNORE_COMMENT, 0,
1147                                          file, &rtype, tab, file_magic, file_actlst,
1148                                          rectime, UEOF_CONT);
1149         }
1150         while (!eosaf);
1151
1152         if (*fmt[f_position]->f_restart) {
1153                 (*fmt[f_position]->f_restart)(&tab, F_END, NULL, NULL, FALSE, &file_hdr, NULL);
1154         }
1155
1156         /* Rewind file */
1157         seek_file_position(ifd, DO_RESTORE);
1158
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,
1163                                                       &file_hdr, NULL);
1164                 }
1165                 do {
1166                         eosaf = read_next_sample(ifd, IGNORE_RESTART, 0,
1167                                                  file, &rtype, tab, file_magic, file_actlst,
1168                                                  rectime, UEOF_CONT);
1169                 }
1170                 while (!eosaf);
1171
1172                 if (*fmt[f_position]->f_comment) {
1173                         (*fmt[f_position]->f_comment)(&tab, F_END, NULL, NULL, 0, NULL,
1174                                                       &file_hdr, NULL);
1175                 }
1176         }
1177
1178 terminate:
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);
1183         }
1184 }
1185
1186 /*
1187  ***************************************************************************
1188  * Display file contents in selected format (logic #2).
1189  * Logic #2:    Grouped by activity. Sorted by timestamp. Stop on RESTART
1190  *              records.
1191  * Formats:     ppc, CSV, raw
1192  *
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'
1201  *
1202  * IN:
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  ***************************************************************************
1212  */
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)
1215 {
1216         int i, p;
1217         int curr = 1, rtype;
1218         int eosaf = TRUE, reset = FALSE;
1219         long cnt = 1;
1220
1221         /* Read system statistics from file */
1222         do {
1223                 /*
1224                  * If this record is a special (RESTART or COMMENT) one, print it and
1225                  * (try to) get another one.
1226                  */
1227                 do {
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 */
1232                                 return;
1233                 }
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)));
1237
1238                 /* Save the first stats collected. Used for example in next_slice() function */
1239                 copy_structures(act, id_seq, record_hdr, 2, 0);
1240
1241                 /* Set flag to reset last_uptime variable. Should be done after a LINUX RESTART record */
1242                 reset = TRUE;
1243
1244                 /* Save current file position */
1245                 seek_file_position(ifd, DO_SAVE);
1246
1247                 /* Read and write stats located between two possible Linux restarts */
1248
1249                 if (DISPLAY_HORIZONTALLY(flags)) {
1250                         /*
1251                          * If stats are displayed horizontally, then all activities
1252                          * are printed on the same line.
1253                          */
1254                         rw_curr_act_stats(ifd, &curr, &cnt, &eosaf,
1255                                           ALL_ACTIVITIES, &reset, file_actlst,
1256                                           rectime, file, file_magic);
1257                 }
1258                 else {
1259                         /* For each requested activity... */
1260                         for (i = 0; i < NR_ACT; i++) {
1261
1262                                 if (!id_seq[i])
1263                                         continue;
1264
1265                                 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
1266                                 if (!IS_SELECTED(act[p]->options))
1267                                         continue;
1268
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);
1273                                 }
1274                                 else {
1275                                         unsigned int optf, msk;
1276
1277                                         optf = act[p]->opt_flags;
1278
1279                                         for (msk = 1; msk < 0x100; msk <<= 1) {
1280                                                 if ((act[p]->opt_flags & 0xff) & msk) {
1281                                                         act[p]->opt_flags &= (0xffffff00 + msk);
1282
1283                                                         rw_curr_act_stats(ifd, &curr, &cnt, &eosaf,
1284                                                                           act[p]->id, &reset, file_actlst,
1285                                                                           rectime, file,
1286                                                                           file_magic);
1287                                                         act[p]->opt_flags = optf;
1288                                                 }
1289                                         }
1290                                 }
1291                         }
1292                 }
1293
1294                 if (!cnt) {
1295                         /* Go to next Linux restart, if possible */
1296                         do {
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);
1300                         }
1301                         while (!eosaf && (rtype != R_RESTART));
1302                 }
1303
1304                 /*
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.
1308                  */
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);
1314                 }
1315         }
1316         while (!eosaf);
1317 }
1318
1319 /*
1320  ***************************************************************************
1321  * Display file contents in SVG format.
1322  *
1323  * IN:
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  ***************************************************************************
1333  */
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)
1336 {
1337         struct svg_hdr_parm parm;
1338         int i, p;
1339         int curr = 1, rtype, g_nr = 0, views_per_row = 1, nr_act_dispd;
1340         int eosaf = TRUE, reset = TRUE;
1341         long cnt = 1;
1342         int graph_nr = 0;
1343
1344         /* Init custom colors palette */
1345         init_custom_color_palette();
1346
1347         /*
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.
1350          */
1351         graph_nr = get_svg_graph_nr(ifd, file, file_magic,
1352                                     file_actlst, rectime, &views_per_row, &nr_act_dispd);
1353
1354         if (SET_CANVAS_HEIGHT(flags)) {
1355                 /*
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.
1358                  */
1359                 graph_nr = canvas_height;
1360         }
1361
1362
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;
1366
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);
1371         }
1372
1373         /*
1374         * If this record is a special (RESTART or COMMENT) one, ignore it and
1375         * (try to) get another one.
1376         */
1377         do {
1378                 if (read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT, 0,
1379                                      file, &rtype, 0, file_magic, file_actlst,
1380                                      rectime, UEOF_CONT))
1381                 {
1382                         /* End of sa data file: No views displayed */
1383                         parm.graph_nr = 0;
1384                         goto close_svg;
1385                 }
1386         }
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)));
1390
1391         /* Save the first stats collected. Used for example in next_slice() function */
1392         copy_structures(act, id_seq, record_hdr, 2, 0);
1393
1394         /* Save current file position */
1395         seek_file_position(ifd, DO_SAVE);
1396
1397         /* For each requested activity, display graphs */
1398         for (i = 0; i < NR_ACT; i++) {
1399
1400                 if (!id_seq[i])
1401                         continue;
1402
1403                 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
1404                 if (!IS_SELECTED(act[p]->options) || !act[p]->g_nr)
1405                         continue;
1406
1407                 if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) {
1408                         display_curr_act_graphs(ifd, &curr, &cnt, &eosaf,
1409                                                 p, &reset, file_actlst,
1410                                                 rectime, file,
1411                                                 file_magic, &g_nr, nr_act_dispd);
1412                 }
1413                 else {
1414                         unsigned int optf, msk;
1415
1416                         optf = act[p]->opt_flags;
1417
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,
1423                                                                 rectime, file,
1424                                                                 file_magic, &g_nr, nr_act_dispd);
1425                                         act[p]->opt_flags = optf;
1426                                 }
1427                         }
1428                 }
1429         }
1430
1431         /* Real number of graphs that have been displayed */
1432         parm.graph_nr = g_nr;
1433
1434 close_svg:
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);
1439         }
1440 }
1441
1442 /*
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.
1447  *
1448  * IN:
1449  * @dfile       System activity data file name.
1450  * @pcparchive  PCP archive file name.
1451  ***************************************************************************
1452  */
1453 void read_stats_from_file(char dfile[], char pcparchive[])
1454 {
1455         struct file_magic file_magic;
1456         struct file_activity *file_actlst = NULL;
1457         struct tm rectime;
1458         int ifd, tab = 0;
1459
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);
1463
1464         if (DISPLAY_HDR_ONLY(flags)) {
1465                 if (*fmt[f_position]->f_header) {
1466                         if (format == F_PCP_OUTPUT) {
1467                                 dfile = pcparchive;
1468                         }
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);
1472                 }
1473                 exit(0);
1474         }
1475
1476         /* Perform required allocations */
1477         allocate_structures(act);
1478
1479         if (SET_LC_NUMERIC_C(fmt[f_position]->options)) {
1480                 /* Use a decimal point */
1481                 setlocale(LC_NUMERIC, "C");
1482         }
1483
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);
1488         }
1489
1490         close(ifd);
1491
1492         free(file_actlst);
1493         free_structures(act);
1494 }
1495
1496 /*
1497  ***************************************************************************
1498  * Main entry to the sadf program
1499  ***************************************************************************
1500  */
1501 int main(int argc, char **argv)
1502 {
1503         int opt = 1, sar_options = 0;
1504         int day_offset = 0;
1505         int i, rc, p, q;
1506         char dfile[MAX_FILE_LEN], pcparchive[MAX_FILE_LEN];
1507         char *t, *v;
1508
1509         /* Compute page shift in kB */
1510         get_kb_shift();
1511
1512         dfile[0] = pcparchive[0] = '\0';
1513
1514 #ifdef USE_NLS
1515         /* Init National Language Support */
1516         init_nls();
1517 #endif
1518
1519         tm_start.use = tm_end.use = FALSE;
1520
1521         /* Allocate and init activity bitmaps */
1522         allocate_bitmaps(act);
1523
1524         /* Init some structures */
1525         init_structures();
1526
1527         /* Process options */
1528         while (opt < argc) {
1529
1530                 if (!strcmp(argv[opt], "-P")) {
1531                         if (parse_sa_P_opt(argv, &opt, &flags, act)) {
1532                                 usage(argv[0]);
1533                         }
1534                 }
1535
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);
1540                 }
1541
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);
1546                 }
1547
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;
1556                 }
1557
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);
1562                 }
1563
1564                 else if (!strcmp(argv[opt], "-s")) {
1565                         /* Get time start */
1566                         if (parse_timestamp(argv, &opt, &tm_start, DEF_TMSTART)) {
1567                                 usage(argv[0]);
1568                         }
1569                 }
1570
1571                 else if (!strcmp(argv[opt], "-e")) {
1572                         /* Get time end */
1573                         if (parse_timestamp(argv, &opt, &tm_end, DEF_TMEND)) {
1574                                 usage(argv[0]);
1575                         }
1576                 }
1577
1578 #ifdef TEST
1579                 else if (!strncmp(argv[opt], "--getenv", 8)) {
1580                         __env = TRUE;
1581                         opt++;
1582                 }
1583 #endif
1584
1585                 else if (!strcmp(argv[opt], "-O")) {
1586                         /* Parse output options */
1587                         if (!argv[++opt] || sar_options) {
1588                                 usage(argv[0]);
1589                         }
1590                         for (t = strtok(argv[opt], ","); t; t = strtok(NULL, ",")) {
1591                                 if (!strcmp(t, K_SKIP_EMPTY)) {
1592                                         flags |= S_F_SVG_SKIP;
1593                                 }
1594                                 else if (!strcmp(t, K_AUTOSCALE)) {
1595                                         flags |= S_F_SVG_AUTOSCALE;
1596                                 }
1597                                 else if (!strcmp(t, K_ONEDAY)) {
1598                                         flags |= S_F_SVG_ONE_DAY;
1599                                 }
1600                                 else if (!strcmp(t, K_SHOWIDLE)) {
1601                                         flags |= S_F_SVG_SHOW_IDLE;
1602                                 }
1603                                 else if (!strcmp(t, K_SHOWINFO)) {
1604                                         flags |= S_F_SVG_SHOW_INFO;
1605                                 }
1606                                 else if (!strcmp(t, K_DEBUG)) {
1607                                         flags |= S_F_DEBUG_MODE;
1608                                 }
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))) {
1612                                                 usage(argv[0]);
1613                                         }
1614                                         canvas_height = atoi(v);
1615                                         flags |= S_F_SVG_HEIGHT;
1616                                 }
1617                                 else if (!strcmp(t, K_PACKED)) {
1618                                         flags |= S_F_SVG_PACKED;
1619                                 }
1620                                 else if (!strcmp(t, K_SHOWTOC)) {
1621                                         flags |= S_F_SVG_SHOW_TOC;
1622                                 }
1623                                 else if (!strcmp(t, K_CUSTOMCOL)) {
1624                                         palette = SVG_CUSTOM_COL_PALETTE;
1625                                 }
1626                                 else if (!strcmp(t, K_BWCOL)) {
1627                                         palette = SVG_BW_COL_PALETTE;
1628                                 }
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';
1633                                 }
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))) {
1637                                                 usage(argv[0]);
1638                                         }
1639                                         user_hz = atoi(v);
1640                                 }
1641                                 else {
1642                                         usage(argv[0]);
1643                                 }
1644                         }
1645                         opt++;
1646                 }
1647
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 */
1654                                 usage(argv[0]);
1655                         }
1656                         day_offset = atoi(argv[opt++] + 1);
1657                 }
1658
1659                 else if (!strcmp(argv[opt], "--")) {
1660                         sar_options = 1;
1661                         opt++;
1662                 }
1663
1664                 else if (!strcmp(argv[opt], "-m")) {
1665                         if (!argv[++opt] || !sar_options) {
1666                                 usage(argv[0]);
1667                         }
1668                         /* Parse sar's option -m */
1669                         if (parse_sar_m_opt(argv, &opt, act)) {
1670                                 usage(argv[0]);
1671                         }
1672                 }
1673
1674                 else if (!strcmp(argv[opt], "-n")) {
1675                         if (!argv[++opt] || !sar_options) {
1676                                 usage(argv[0]);
1677                         }
1678                         /* Parse sar's option -n */
1679                         if (parse_sar_n_opt(argv, &opt, act)) {
1680                                 usage(argv[0]);
1681                         }
1682                 }
1683
1684                 else if (!strcmp(argv[opt], "-q")) {
1685                         if (!sar_options) {
1686                                 usage(argv[0]);
1687                         }
1688                         else if (!argv[++opt]) {
1689                                 SELECT_ACTIVITY(A_QUEUE);
1690                         }
1691                         /* Parse option -q */
1692                         else if (parse_sar_q_opt(argv, &opt, act)) {
1693                                 SELECT_ACTIVITY(A_QUEUE);
1694                         }
1695                 }
1696
1697                 else if (!strncmp(argv[opt], "-", 1)) {
1698                         /* Other options not previously tested */
1699                         if (sar_options) {
1700                                 if ((rc = parse_sar_opt(argv, &opt, act, &flags, C_SADF)) != 0) {
1701                                         if (rc == 1) {
1702                                                 usage(argv[0]);
1703                                         }
1704                                         exit(1);
1705                                 }
1706                         }
1707                         else {
1708
1709                                 for (i = 1; *(argv[opt] + i); i++) {
1710
1711                                         switch (*(argv[opt] + i)) {
1712
1713                                         case 'C':
1714                                                 flags |= S_F_COMMENT;
1715                                                 break;
1716
1717                                         case 'c':
1718                                                 if (format) {
1719                                                         usage(argv[0]);
1720                                                 }
1721                                                 format = F_CONV_OUTPUT;
1722                                                 break;
1723
1724                                         case 'd':
1725                                                 if (format) {
1726                                                         usage(argv[0]);
1727                                                 }
1728                                                 format = F_DB_OUTPUT;
1729                                                 break;
1730
1731                                         case 'g':
1732                                                 if (format) {
1733                                                         usage(argv[0]);
1734                                                 }
1735                                                 format = F_SVG_OUTPUT;
1736                                                 break;
1737
1738                                         case 'h':
1739                                                 flags |= S_F_HORIZONTALLY;
1740                                                 break;
1741
1742                                         case 'H':
1743                                                 flags |= S_F_HDR_ONLY;
1744                                                 break;
1745
1746                                         case 'j':
1747                                                 if (format) {
1748                                                         usage(argv[0]);
1749                                                 }
1750                                                 format = F_JSON_OUTPUT;
1751                                                 break;
1752
1753                                         case 'l':
1754                                                 if (format) {
1755                                                         usage(argv[0]);
1756                                                 }
1757                                                 format = F_PCP_OUTPUT;
1758                                                 break;
1759
1760                                         case 'p':
1761                                                 if (format) {
1762                                                         usage(argv[0]);
1763                                                 }
1764                                                 format = F_PPC_OUTPUT;
1765                                                 break;
1766
1767                                         case 'r':
1768                                                 if (format) {
1769                                                         usage(argv[0]);
1770                                                 }
1771                                                 format = F_RAW_OUTPUT;
1772                                                 break;
1773
1774                                         case 'T':
1775                                                 flags |= S_F_LOCAL_TIME;
1776                                                 break;
1777
1778                                         case 't':
1779                                                 flags |= S_F_TRUE_TIME;
1780                                                 break;
1781
1782                                         case 'U':
1783                                                 flags |= S_F_SEC_EPOCH;
1784                                                 break;
1785
1786                                         case 'x':
1787                                                 if (format) {
1788                                                         usage(argv[0]);
1789                                                 }
1790                                                 format = F_XML_OUTPUT;
1791                                                 break;
1792
1793                                         case 'V':
1794                                                 print_version();
1795                                                 break;
1796
1797                                         default:
1798                                                 usage(argv[0]);
1799                                         }
1800                                 }
1801                         }
1802                         opt++;
1803                 }
1804
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 */
1809                                 usage(argv[0]);
1810                         }
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);
1816                 }
1817
1818                 else if (interval < 0) {
1819                         /* Get interval */
1820                         if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1821                                 usage(argv[0]);
1822                         }
1823                         interval = atol(argv[opt++]);
1824                         if (interval < 1) {
1825                                 usage(argv[0]);
1826                         }
1827                 }
1828
1829                 else {
1830                         /* Get count value */
1831                         if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1832                                 usage(argv[0]);
1833                         }
1834                         if (count) {
1835                                 /* Count parameter already set */
1836                                 usage(argv[0]);
1837                         }
1838                         count = atol(argv[opt++]);
1839                         if (count < 0) {
1840                                 usage(argv[0]);
1841                         }
1842                         else if (!count) {
1843                                 count = -1;     /* To generate a report continuously */
1844                         }
1845                 }
1846         }
1847
1848         if (USE_OPTION_A(flags)) {
1849                 /* Set -P ALL -I ALL if needed */
1850                 set_bitmaps(act, &flags);
1851         }
1852
1853         /* sadf reads current daily data file by default */
1854         if (!dfile[0]) {
1855                 set_default_file(dfile, day_offset, -1);
1856         }
1857
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);
1861         }
1862
1863 #ifndef HAVE_PCP
1864         if (format == F_PCP_OUTPUT) {
1865                 fprintf(stderr, _("PCP support not compiled in\n"));
1866                 exit(1);
1867         }
1868 #endif
1869
1870         if (tm_start.use && tm_end.use && (tm_end.tm_hour < tm_start.tm_hour)) {
1871                 tm_end.tm_hour += 24;
1872         }
1873
1874         if (DISPLAY_PRETTY(flags)) {
1875                 dm_major = get_devmap_major();
1876         }
1877
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) {
1881                 usage(argv[0]);
1882         }
1883
1884         /*
1885          * Display all the contents of the daily data file if the count parameter
1886          * was not set on the command line.
1887          */
1888         if (!count) {
1889                 count = -1;
1890         }
1891
1892         /* Default is CPU activity */
1893         select_default_activity(act);
1894
1895         /* Check options consistency with selected output format. Default is PPC display */
1896         check_format_options();
1897
1898         if (interval < 0) {
1899                 interval = 1;
1900         }
1901
1902         if (format == F_CONV_OUTPUT) {
1903                 /* Convert file to current format */
1904                 convert_file(dfile, act);
1905         }
1906         else {
1907                 /* Read stats from file */
1908                 read_stats_from_file(dfile, pcparchive);
1909         }
1910
1911         /* Free bitmaps */
1912         free_bitmaps(act);
1913
1914         return 0;
1915 }