]> granicus.if.org Git - sysstat/blob - sadf.c
mpstat.h: Remove unneeded 'aligned' attribute
[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> ]\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], "-I")) {
1531                         if (!sar_options) {
1532                                 usage(argv[0]);
1533                         }
1534                         if (parse_sar_I_opt(argv, &opt, &flags, act)) {
1535                                 usage(argv[0]);
1536                         }
1537                 }
1538
1539                 else if (!strcmp(argv[opt], "-P")) {
1540                         if (parse_sa_P_opt(argv, &opt, &flags, act)) {
1541                                 usage(argv[0]);
1542                         }
1543                 }
1544
1545                 else if (!strncmp(argv[opt], "--dev=", 6)) {
1546                         /* Parse devices entered on the command line */
1547                         p = get_activity_position(act, A_DISK, EXIT_IF_NOT_FOUND);
1548                         parse_sa_devices(argv[opt], act[p], MAX_DEV_LEN, &opt, 6);
1549                 }
1550
1551                 else if (!strncmp(argv[opt], "--fs=", 5)) {
1552                         /* Parse devices entered on the command line */
1553                         p = get_activity_position(act, A_FS, EXIT_IF_NOT_FOUND);
1554                         parse_sa_devices(argv[opt], act[p], MAX_FS_LEN, &opt, 5);
1555                 }
1556
1557                 else if (!strncmp(argv[opt], "--iface=", 8)) {
1558                         /* Parse devices entered on the command line */
1559                         p = get_activity_position(act, A_NET_DEV, EXIT_IF_NOT_FOUND);
1560                         parse_sa_devices(argv[opt], act[p], MAX_IFACE_LEN, &opt, 8);
1561                         q = get_activity_position(act, A_NET_EDEV, EXIT_IF_NOT_FOUND);
1562                         act[q]->item_list = act[p]->item_list;
1563                         act[q]->item_list_sz = act[p]->item_list_sz;
1564                         act[q]->options |= AO_LIST_ON_CMDLINE;
1565                 }
1566
1567                 else if (!strcmp(argv[opt], "-s")) {
1568                         /* Get time start */
1569                         if (parse_timestamp(argv, &opt, &tm_start, DEF_TMSTART)) {
1570                                 usage(argv[0]);
1571                         }
1572                 }
1573
1574                 else if (!strcmp(argv[opt], "-e")) {
1575                         /* Get time end */
1576                         if (parse_timestamp(argv, &opt, &tm_end, DEF_TMEND)) {
1577                                 usage(argv[0]);
1578                         }
1579                 }
1580
1581 #ifdef TEST
1582                 else if (!strncmp(argv[opt], "--getenv", 8)) {
1583                         __env = TRUE;
1584                         opt++;
1585                 }
1586 #endif
1587
1588                 else if (!strcmp(argv[opt], "-O")) {
1589                         /* Parse output options */
1590                         if (!argv[++opt] || sar_options) {
1591                                 usage(argv[0]);
1592                         }
1593                         for (t = strtok(argv[opt], ","); t; t = strtok(NULL, ",")) {
1594                                 if (!strcmp(t, K_SKIP_EMPTY)) {
1595                                         flags |= S_F_SVG_SKIP;
1596                                 }
1597                                 else if (!strcmp(t, K_AUTOSCALE)) {
1598                                         flags |= S_F_SVG_AUTOSCALE;
1599                                 }
1600                                 else if (!strcmp(t, K_ONEDAY)) {
1601                                         flags |= S_F_SVG_ONE_DAY;
1602                                 }
1603                                 else if (!strcmp(t, K_SHOWIDLE)) {
1604                                         flags |= S_F_SVG_SHOW_IDLE;
1605                                 }
1606                                 else if (!strcmp(t, K_SHOWINFO)) {
1607                                         flags |= S_F_SVG_SHOW_INFO;
1608                                 }
1609                                 else if (!strcmp(t, K_DEBUG)) {
1610                                         flags |= S_F_DEBUG_MODE;
1611                                 }
1612                                 else if (!strncmp(t, K_HEIGHT, strlen(K_HEIGHT))) {
1613                                         v = t + strlen(K_HEIGHT);
1614                                         if (!strlen(v) || (strspn(v, DIGITS) != strlen(v))) {
1615                                                 usage(argv[0]);
1616                                         }
1617                                         canvas_height = atoi(v);
1618                                         flags |= S_F_SVG_HEIGHT;
1619                                 }
1620                                 else if (!strcmp(t, K_PACKED)) {
1621                                         flags |= S_F_SVG_PACKED;
1622                                 }
1623                                 else if (!strcmp(t, K_SHOWTOC)) {
1624                                         flags |= S_F_SVG_SHOW_TOC;
1625                                 }
1626                                 else if (!strcmp(t, K_CUSTOMCOL)) {
1627                                         palette = SVG_CUSTOM_COL_PALETTE;
1628                                 }
1629                                 else if (!strcmp(t, K_BWCOL)) {
1630                                         palette = SVG_BW_COL_PALETTE;
1631                                 }
1632                                 else if (!strncmp(t, K_PCPARCHIVE, strlen(K_PCPARCHIVE))) {
1633                                         v = t + strlen(K_PCPARCHIVE);
1634                                         strncpy(pcparchive, v, sizeof(pcparchive));
1635                                         pcparchive[sizeof(pcparchive) - 1] = '\0';
1636                                 }
1637                                 else if (!strncmp(t, K_HZ, strlen(K_HZ))) {
1638                                         v = t + strlen(K_HZ);
1639                                         if (!strlen(v) || (strspn(v, DIGITS) != strlen(v))) {
1640                                                 usage(argv[0]);
1641                                         }
1642                                         user_hz = atoi(v);
1643                                 }
1644                                 else {
1645                                         usage(argv[0]);
1646                                 }
1647                         }
1648                         opt++;
1649                 }
1650
1651                 else if ((strlen(argv[opt]) > 1) &&
1652                          (strlen(argv[opt]) < 4) &&
1653                          !strncmp(argv[opt], "-", 1) &&
1654                          (strspn(argv[opt] + 1, DIGITS) == (strlen(argv[opt]) - 1))) {
1655                         if (dfile[0] || day_offset) {
1656                                 /* File already specified */
1657                                 usage(argv[0]);
1658                         }
1659                         day_offset = atoi(argv[opt++] + 1);
1660                 }
1661
1662                 else if (!strcmp(argv[opt], "--")) {
1663                         sar_options = 1;
1664                         opt++;
1665                 }
1666
1667                 else if (!strcmp(argv[opt], "-m")) {
1668                         if (!argv[++opt] || !sar_options) {
1669                                 usage(argv[0]);
1670                         }
1671                         /* Parse sar's option -m */
1672                         if (parse_sar_m_opt(argv, &opt, act)) {
1673                                 usage(argv[0]);
1674                         }
1675                 }
1676
1677                 else if (!strcmp(argv[opt], "-n")) {
1678                         if (!argv[++opt] || !sar_options) {
1679                                 usage(argv[0]);
1680                         }
1681                         /* Parse sar's option -n */
1682                         if (parse_sar_n_opt(argv, &opt, act)) {
1683                                 usage(argv[0]);
1684                         }
1685                 }
1686
1687                 else if (!strcmp(argv[opt], "-q")) {
1688                         if (!sar_options) {
1689                                 usage(argv[0]);
1690                         }
1691                         else if (!argv[++opt]) {
1692                                 SELECT_ACTIVITY(A_QUEUE);
1693                         }
1694                         /* Parse option -q */
1695                         else if (parse_sar_q_opt(argv, &opt, act)) {
1696                                 SELECT_ACTIVITY(A_QUEUE);
1697                         }
1698                 }
1699
1700                 else if (!strncmp(argv[opt], "-", 1)) {
1701                         /* Other options not previously tested */
1702                         if (sar_options) {
1703                                 if ((rc = parse_sar_opt(argv, &opt, act, &flags, C_SADF)) != 0) {
1704                                         if (rc == 1) {
1705                                                 usage(argv[0]);
1706                                         }
1707                                         exit(1);
1708                                 }
1709                         }
1710                         else {
1711
1712                                 for (i = 1; *(argv[opt] + i); i++) {
1713
1714                                         switch (*(argv[opt] + i)) {
1715
1716                                         case 'C':
1717                                                 flags |= S_F_COMMENT;
1718                                                 break;
1719
1720                                         case 'c':
1721                                                 if (format) {
1722                                                         usage(argv[0]);
1723                                                 }
1724                                                 format = F_CONV_OUTPUT;
1725                                                 break;
1726
1727                                         case 'd':
1728                                                 if (format) {
1729                                                         usage(argv[0]);
1730                                                 }
1731                                                 format = F_DB_OUTPUT;
1732                                                 break;
1733
1734                                         case 'g':
1735                                                 if (format) {
1736                                                         usage(argv[0]);
1737                                                 }
1738                                                 format = F_SVG_OUTPUT;
1739                                                 break;
1740
1741                                         case 'h':
1742                                                 flags |= S_F_HORIZONTALLY;
1743                                                 break;
1744
1745                                         case 'H':
1746                                                 flags |= S_F_HDR_ONLY;
1747                                                 break;
1748
1749                                         case 'j':
1750                                                 if (format) {
1751                                                         usage(argv[0]);
1752                                                 }
1753                                                 format = F_JSON_OUTPUT;
1754                                                 break;
1755
1756                                         case 'l':
1757                                                 if (format) {
1758                                                         usage(argv[0]);
1759                                                 }
1760                                                 format = F_PCP_OUTPUT;
1761                                                 break;
1762
1763                                         case 'p':
1764                                                 if (format) {
1765                                                         usage(argv[0]);
1766                                                 }
1767                                                 format = F_PPC_OUTPUT;
1768                                                 break;
1769
1770                                         case 'r':
1771                                                 if (format) {
1772                                                         usage(argv[0]);
1773                                                 }
1774                                                 format = F_RAW_OUTPUT;
1775                                                 break;
1776
1777                                         case 'T':
1778                                                 flags |= S_F_LOCAL_TIME;
1779                                                 break;
1780
1781                                         case 't':
1782                                                 flags |= S_F_TRUE_TIME;
1783                                                 break;
1784
1785                                         case 'U':
1786                                                 flags |= S_F_SEC_EPOCH;
1787                                                 break;
1788
1789                                         case 'x':
1790                                                 if (format) {
1791                                                         usage(argv[0]);
1792                                                 }
1793                                                 format = F_XML_OUTPUT;
1794                                                 break;
1795
1796                                         case 'V':
1797                                                 print_version();
1798                                                 break;
1799
1800                                         default:
1801                                                 usage(argv[0]);
1802                                         }
1803                                 }
1804                         }
1805                         opt++;
1806                 }
1807
1808                 /* Get data file name */
1809                 else if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1810                         if (dfile[0] || day_offset) {
1811                                 /* File already specified */
1812                                 usage(argv[0]);
1813                         }
1814                         /* Write data to file */
1815                         strncpy(dfile, argv[opt++], sizeof(dfile));
1816                         dfile[sizeof(dfile) - 1] = '\0';
1817                         /* Check if this is an alternate directory for sa files */
1818                         check_alt_sa_dir(dfile, 0, -1);
1819                 }
1820
1821                 else if (interval < 0) {
1822                         /* Get interval */
1823                         if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1824                                 usage(argv[0]);
1825                         }
1826                         interval = atol(argv[opt++]);
1827                         if (interval < 1) {
1828                                 usage(argv[0]);
1829                         }
1830                 }
1831
1832                 else {
1833                         /* Get count value */
1834                         if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1835                                 usage(argv[0]);
1836                         }
1837                         if (count) {
1838                                 /* Count parameter already set */
1839                                 usage(argv[0]);
1840                         }
1841                         count = atol(argv[opt++]);
1842                         if (count < 0) {
1843                                 usage(argv[0]);
1844                         }
1845                         else if (!count) {
1846                                 count = -1;     /* To generate a report continuously */
1847                         }
1848                 }
1849         }
1850
1851         if (USE_OPTION_A(flags)) {
1852                 /* Set -P ALL -I ALL if needed */
1853                 set_bitmaps(act, &flags);
1854         }
1855
1856         /* sadf reads current daily data file by default */
1857         if (!dfile[0]) {
1858                 set_default_file(dfile, day_offset, -1);
1859         }
1860
1861         /* PCP mode: If no archive file specified then use the name of the daily data file */
1862         if (!pcparchive[0] && (format == F_PCP_OUTPUT)) {
1863                 strcpy(pcparchive, dfile);
1864         }
1865
1866 #ifndef HAVE_PCP
1867         if (format == F_PCP_OUTPUT) {
1868                 fprintf(stderr, _("PCP support not compiled in\n"));
1869                 exit(1);
1870         }
1871 #endif
1872
1873         if (tm_start.use && tm_end.use && (tm_end.tm_hour < tm_start.tm_hour)) {
1874                 tm_end.tm_hour += 24;
1875         }
1876
1877         if (DISPLAY_PRETTY(flags)) {
1878                 dm_major = get_devmap_major();
1879         }
1880
1881         /* Options -T, -t and -U are mutually exclusive */
1882         if ((PRINT_LOCAL_TIME(flags) + PRINT_TRUE_TIME(flags) +
1883             PRINT_SEC_EPOCH(flags)) > 1) {
1884                 usage(argv[0]);
1885         }
1886
1887         /*
1888          * Display all the contents of the daily data file if the count parameter
1889          * was not set on the command line.
1890          */
1891         if (!count) {
1892                 count = -1;
1893         }
1894
1895         /* Default is CPU activity */
1896         select_default_activity(act);
1897
1898         /* Check options consistency with selected output format. Default is PPC display */
1899         check_format_options();
1900
1901         if (interval < 0) {
1902                 interval = 1;
1903         }
1904
1905         if (format == F_CONV_OUTPUT) {
1906                 /* Convert file to current format */
1907                 convert_file(dfile, act);
1908         }
1909         else {
1910                 /* Read stats from file */
1911                 read_stats_from_file(dfile, pcparchive);
1912         }
1913
1914         /* Free bitmaps */
1915         free_bitmaps(act);
1916
1917         return 0;
1918 }