]> granicus.if.org Git - sysstat/blob - sadf.c
Merge branch 'clang' of https://github.com/pschiffe/sysstat
[sysstat] / sadf.c
1 /*
2  * sadf: system activity data formatter
3  * (C) 1999-2016 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 <stdarg.h>
26 #include <unistd.h>
27 #include <time.h>
28 #include <errno.h>
29
30 #include "version.h"
31 #include "sadf.h"
32 #include "sa.h"
33 #include "common.h"
34 #include "ioconf.h"
35
36 # include <locale.h>    /* For setlocale() */
37 #ifdef USE_NLS
38 # include <libintl.h>
39 # define _(string) gettext(string)
40 #else
41 # define _(string) (string)
42 #endif
43
44 #define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
45 char *sccsid(void) { return (SCCSID); }
46
47 long interval = -1, count = 0;
48
49 unsigned int flags = 0;
50 unsigned int dm_major;          /* Device-mapper major number */
51 unsigned int format = 0;        /* Output format */
52 unsigned int f_position = 0;    /* Output format position in array */
53
54 /* File header */
55 struct file_header file_hdr;
56
57 /*
58  * Activity sequence.
59  * This array must always be entirely filled (even with trailing zeros).
60  */
61 unsigned int id_seq[NR_ACT];
62 /* Total number of SVG graphs for each activity */
63 int id_g_nr[NR_ACT];
64
65 /* Current record header */
66 struct record_header record_hdr[3];
67
68 /* Contain the date specified by -s and -e options */
69 struct tstamp tm_start, tm_end;
70 char *args[MAX_ARGV_NR];
71
72 extern struct activity *act[];
73 extern struct report_format *fmt[];
74
75 /*
76  ***************************************************************************
77  * Print usage and exit.
78  *
79  * IN:
80  * @progname    Name of sysstat command.
81  ***************************************************************************
82  */
83 void usage(char *progname)
84 {
85         fprintf(stderr,
86                 _("Usage: %s [ options ] [ <interval> [ <count> ] ] [ <datafile> | -[0-9]+ ]\n"),
87                 progname);
88
89         fprintf(stderr, _("Options are:\n"
90                           "[ -C ] [ -c | -d | -g | -j | -p | -x ] [ -H ] [ -h ] [ -T | -t | -U ] [ -V ]\n"
91                           "[ -O <opts> [,...] ] [ -P { <cpu> [,...] | ALL } ]\n"
92                           "[ -s [ <hh:mm[:ss]> ] ] [ -e [ <hh:mm[:ss]> ] ]\n"
93                           "[ -- <sar_options> ]\n"));
94         exit(1);
95 }
96
97 /*
98  ***************************************************************************
99  * Init structures.
100  ***************************************************************************
101  */
102 void init_structures(void)
103 {
104         int i;
105
106         for (i = 0; i < 3; i++) {
107                 memset(&record_hdr[i], 0, RECORD_HEADER_SIZE);
108         }
109 }
110
111 /*
112  ***************************************************************************
113  * Look for output format in array.
114  *
115  * IN:
116  * @fmt         Array of output formats.
117  * @format      Output format to look for.
118  *
119  * RETURNS:
120  * Position of output format in array.
121  ***************************************************************************
122  */
123 int get_format_position(struct report_format *fmt[], unsigned int format)
124 {
125         int i;
126
127         for (i = 0; i < NR_FMT; i++) {
128                 if (fmt[i]->id == format)
129                         break;
130         }
131
132         if (i == NR_FMT)
133                 /* Should never happen */
134                 return 0;
135
136         return i;
137 }
138
139 /*
140  ***************************************************************************
141  * Check that options entered on the command line are consistent with
142  * selected output format. If no output format has been explicitly entered,
143  * then select a default one.
144  ***************************************************************************
145  */
146 void check_format_options(void)
147 {
148         if (!format) {
149                 /* Select output format if none has been selected */
150                 if (DISPLAY_HDR_ONLY(flags)) {
151                         format = F_HEADER_OUTPUT;
152                 }
153                 else {
154                         format = F_PPC_OUTPUT;
155                 }
156         }
157
158         /* Get format position in array */
159         f_position = get_format_position(fmt, format);
160
161         /* Check options consistency wrt output format */
162         if (!ACCEPT_HEADER_ONLY(fmt[f_position]->options)) {
163                 /* Remove option -H */
164                 flags &= ~S_F_HDR_ONLY;
165         }
166         if (!ACCEPT_HORIZONTALLY(fmt[f_position]->options)) {
167                 /* Remove option -h */
168                 flags &= ~S_F_HORIZONTALLY;
169         }
170         if (!ACCEPT_LOCAL_TIME(fmt[f_position]->options)) {
171                 /* Remove options -T and -t */
172                 flags &= ~(S_F_LOCAL_TIME + S_F_TRUE_TIME);
173         }
174         if (!ACCEPT_SEC_EPOCH(fmt[f_position]->options)) {
175                 /* Remove option -U */
176                 flags &= ~S_F_SEC_EPOCH;
177         }
178         if (REJECT_TRUE_TIME(fmt[f_position]->options)) {
179                 /* Remove option -t */
180                 flags &= ~S_F_TRUE_TIME;
181         }
182 }
183
184 /*
185  ***************************************************************************
186  * Print tabulations
187  *
188  * IN:
189  * @nr_tab      Number of tabs to print.
190  ***************************************************************************
191  */
192 void prtab(int nr_tab)
193 {
194         int i;
195
196         for (i = 0; i < nr_tab; i++) {
197                 printf("\t");
198         }
199 }
200
201 /*
202  ***************************************************************************
203  * printf() function modified for logic #1 (XML-like) display. Don't print a
204  * CR at the end of the line.
205  *
206  * IN:
207  * @nr_tab      Number of tabs to print.
208  * @fmtf        printf() format.
209  ***************************************************************************
210  */
211 void xprintf0(int nr_tab, const char *fmtf, ...)
212 {
213         static char buf[1024];
214         va_list args;
215
216         va_start(args, fmtf);
217         vsnprintf(buf, sizeof(buf), fmtf, args);
218         va_end(args);
219
220         prtab(nr_tab);
221         printf("%s", buf);
222 }
223
224 /*
225  ***************************************************************************
226  * printf() function modified for logic #1 (XML-like) display. Print a CR
227  * at the end of the line.
228  *
229  * IN:
230  * @nr_tab      Number of tabs to print.
231  * @fmtf        printf() format.
232  ***************************************************************************
233  */
234 void xprintf(int nr_tab, const char *fmtf, ...)
235 {
236         static char buf[1024];
237         va_list args;
238
239         va_start(args, fmtf);
240         vsnprintf(buf, sizeof(buf), fmtf, args);
241         va_end(args);
242
243         prtab(nr_tab);
244         printf("%s\n", buf);
245 }
246
247 /*
248  ***************************************************************************
249  * Save or restore number of items for all known activities.
250  *
251  * IN:
252  * @save_act_nr Array containing number of items to restore for each
253  *              activity.
254  * @action      DO_SAVE to save number of items, or DO_RESTORE to restore.
255  *
256  * OUT:
257  * @save_act_nr Array containing number of items saved for each activity.
258  ***************************************************************************
259  */
260 void sr_act_nr(__nr_t save_act_nr[], int action)
261 {
262         int i;
263
264         if (action == DO_SAVE) {
265                 /* Save number of items for all activities */
266                 for (i = 0; i < NR_ACT; i++) {
267                         save_act_nr[i] = act[i]->nr;
268                 }
269         }
270         else if (action == DO_RESTORE) {
271                 /*
272                  * Restore number of items for all activities
273                  * and reallocate structures accordingly.
274                  */
275                 for (i = 0; i < NR_ACT; i++) {
276                         if (save_act_nr[i] > 0) {
277                                 reallocate_vol_act_structures(act, save_act_nr[i],
278                                                               act[i]->id);
279                         }
280                 }
281         }
282 }
283
284 /*
285  ***************************************************************************
286  * Read next sample statistics. If it's a special record (R_RESTART or
287  * R_COMMENT) then display it if requested. Also fill timestamps structures.
288  *
289  * IN:
290  * @ifd         File descriptor
291  * @action      Flags indicating if special records should be displayed or
292  *              not.
293  * @curr        Index in array for current sample statistics.
294  * @file        System activity data file name (name of file being read).
295  * @tab         Number of tabulations to print.
296  * @file_actlst List of (known or unknown) activities in file.
297  * @file_magic  System activity file magic header.
298  * @rectime     Structure where timestamp (expressed in local time or in UTC
299  *              depending on whether options -T/-t have been used or not) can
300  *              be saved for current record.
301  * @loctime     Structure where timestamp (expressed in local time) can be
302  *              saved for current record.
303  *
304  * OUT:
305  * @rtype       Type of record read (R_RESTART, R_COMMENT, etc.)
306  * @rectime     Structure where timestamp (expressed in local time or in UTC
307  *              depending on options used) has been saved for current record.
308  *              If current record was a special one (RESTART or COMMENT) and
309  *              noted to be ignored, then the timestamp is saved only if
310  *              explicitly told to do so with the SET_TIMESTAMPS action flag.
311  * @loctime     Structure where timestamp (expressed in local time) has been
312  *              saved for current record.
313  *              If current record was a special one (RESTART or COMMENT) and
314  *              noted to be ignored, then the timestamp is saved only if
315  *              explicitly told to do so with the SET_TIMESTAMPS action flag.
316  *
317  * RETURNS:
318  * TRUE if end of file has been reached.
319  ***************************************************************************
320  */
321 int read_next_sample(int ifd, int action, int curr, char *file, int *rtype, int tab,
322                      struct file_magic *file_magic, struct file_activity *file_actlst,
323                      struct tm *rectime, struct tm *loctime)
324 {
325         int eosaf;
326
327         /* Read current record */
328         eosaf = sa_fread(ifd, &record_hdr[curr], RECORD_HEADER_SIZE, SOFT_SIZE);
329         *rtype = record_hdr[curr].record_type;
330
331         if (!eosaf) {
332                 if (*rtype == R_COMMENT) {
333                         if (action & IGNORE_COMMENT) {
334                                 /* Ignore COMMENT record */
335                                 if (lseek(ifd, MAX_COMMENT_LEN, SEEK_CUR) < MAX_COMMENT_LEN) {
336                                         perror("lseek");
337                                 }
338                                 if (action & SET_TIMESTAMPS) {
339                                         sa_get_record_timestamp_struct(flags, &record_hdr[curr],
340                                                                        rectime, loctime);
341                                 }
342                         }
343                         else {
344                                 /* Display COMMENT record */
345                                 print_special_record(&record_hdr[curr], flags, &tm_start, &tm_end,
346                                                      *rtype, ifd, rectime, loctime, file, tab,
347                                                      file_magic, &file_hdr, act, fmt[f_position]);
348                         }
349                 }
350                 else if (*rtype == R_RESTART) {
351                         if (action & IGNORE_RESTART) {
352                                 /*
353                                  * Ignore RESTART record (don't display it)
354                                  * but anyway we have to reallocate volatile
355                                  * activities structures (unless we don't want to
356                                  * do it now).
357                                  */
358                                 if (!(action & DONT_READ_VOLATILE)) {
359                                         read_vol_act_structures(ifd, act, file, file_magic,
360                                                                 file_hdr.sa_vol_act_nr);
361                                 }
362                                 if (action & SET_TIMESTAMPS) {
363                                         sa_get_record_timestamp_struct(flags, &record_hdr[curr],
364                                                                        rectime, loctime);
365                                 }
366                         }
367                         else {
368                                 /* Display RESTART record */
369                                 print_special_record(&record_hdr[curr], flags, &tm_start, &tm_end,
370                                                      *rtype, ifd, rectime, loctime, file, tab,
371                                                      file_magic, &file_hdr, act, fmt[f_position]);
372                         }
373                 }
374                 else {
375                         /*
376                          * OK: Previous record was not a special one.
377                          * So read now the extra fields.
378                          */
379                         read_file_stat_bunch(act, curr, ifd, file_hdr.sa_act_nr,
380                                              file_actlst);
381                         sa_get_record_timestamp_struct(flags, &record_hdr[curr], rectime, loctime);
382                 }
383         }
384
385         return eosaf;
386 }
387
388 /*
389  ***************************************************************************
390  * Display the field list (used eg. in database format).
391  *
392  * IN:
393  * @act_d       Activity to display, or ~0 for all.
394  ***************************************************************************
395  */
396 void list_fields(unsigned int act_id)
397 {
398         int i, j;
399         unsigned int msk;
400         char *hl;
401         char hline[HEADER_LINE_LEN] = "";
402
403         printf("# hostname;interval;timestamp");
404
405         for (i = 0; i < NR_ACT; i++) {
406
407                 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
408                         continue;
409
410                 if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
411                         if (!HAS_MULTIPLE_OUTPUTS(act[i]->options)) {
412                                 printf(";%s", act[i]->hdr_line);
413                                 if ((act[i]->nr > 1) && DISPLAY_HORIZONTALLY(flags)) {
414                                         printf("[...]");
415                                 }
416                         }
417                         else {
418                                 msk = 1;
419                                 strncpy(hline, act[i]->hdr_line, HEADER_LINE_LEN - 1);
420                                 hline[HEADER_LINE_LEN - 1] = '\0';
421                                 for (hl = strtok(hline, "|"); hl; hl = strtok(NULL, "|"), msk <<= 1) {
422                                         if ((hl != NULL) && ((act[i]->opt_flags & 0xff) & msk)) {
423                                                 if (strchr(hl, '&')) {
424                                                         j = strcspn(hl, "&");
425                                                         if ((act[i]->opt_flags & 0xff00) & (msk << 8)) {
426                                                                 /* Display whole header line */
427                                                                 *(hl + j) = ';';
428                                                                 printf(";%s", hl);
429                                                         }
430                                                         else {
431                                                                 /* Display only the first part of the header line */
432                                                                 *(hl + j) = '\0';
433                                                                 printf(";%s", hl);
434                                                         }
435                                                         *(hl + j) = '&';
436                                                 }
437                                                 else {
438                                                         printf(";%s", hl);
439                                                 }
440                                                 if ((act[i]->nr > 1) && DISPLAY_HORIZONTALLY(flags)) {
441                                                         printf("[...]");
442                                                 }
443                                         }
444                                 }
445                         }
446                 }
447         }
448         printf("\n");
449 }
450
451 /*
452  ***************************************************************************
453  * Determine the time (expressed in seconds since the epoch) used as the
454  * origin on X axis for SVG graphs. If S_F_SVG_ONE_DAY is set, then origin
455  * will be the beginning of current day (00:00:00) else it will be the time
456  * of the first sample collected.
457  *
458  * RETURNS:
459  * Time origin on X axis (expressed in seconds since the epoch).
460  ***************************************************************************
461  */
462 time_t get_time_ref(void)
463 {
464         struct tm *ltm;
465         time_t t;
466
467         if (DISPLAY_ONE_DAY(flags)) {
468                 ltm = localtime((time_t *) &(record_hdr[2].ust_time));
469
470                 /* Move back to midnight */
471                 ltm->tm_sec = ltm->tm_min = ltm->tm_hour = 0;
472
473                 t = mktime(ltm);
474                 if (t != -1)
475                         return t;
476         }
477
478         return record_hdr[2].ust_time;
479 }
480
481 /*
482  ***************************************************************************
483  * Compute the number of SVG graphs to display. Each activity selected may
484  * have several graphs. Moreover we have to take into account volatile
485  * activities (eg. CPU) for which the number of graphs will depend on the
486  * highest number of items (eg. maximum number of CPU) saved in the file.
487  * This number may be higher than the real number of graphs that will be
488  * displayed since some items have a preallocation constant.
489  *
490  * IN:
491  * @ifd         File descriptor of input file.
492  * @file        Name of file being read.
493  * @file_magic  file_magic structure filled with file magic header data.
494  * @file_actlst List of (known or unknown) activities in file.
495  * @rectime     Structure where timestamp (expressed in local time or in UTC
496  *              depending on whether options -T/-t have been used or not) can
497  *              be saved for current record.
498  * @loctime     Structure where timestamp (expressed in local time) can be
499  *              saved for current record.
500  *
501  * RETURNS:
502  * Total number of graphs to display, taking into account only activities
503  * to be displayed, and selected period of time (options -s/-e).
504  ***************************************************************************
505  */
506 int get_svg_graph_nr(int ifd, char *file, struct file_magic *file_magic,
507                      struct file_activity *file_actlst, struct tm *rectime,
508                      struct tm *loctime)
509 {
510         int i, n, p, eosaf;
511         int rtype, new_tot_g_nr, tot_g_nr = 0;
512         off_t fpos;
513         __nr_t save_act_nr[NR_ACT] = {0};
514
515         /* Save current file position and items number */
516         if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) {
517                 perror("lseek");
518                 exit(2);
519         }
520         sr_act_nr(save_act_nr, DO_SAVE);
521
522         /* Init total number of graphs for each activity */
523         for (i = 0; i < NR_ACT; i++) {
524                 id_g_nr[i] = 0;
525         }
526
527         /* Look for the first record that will be displayed */
528         do {
529                 eosaf = read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT | SET_TIMESTAMPS,
530                                          0, file, &rtype, 0, file_magic, file_actlst,
531                                          rectime, loctime);
532                 if (eosaf)
533                         /* No record to display => no graph too */
534                         return 0;
535         }
536         while ((tm_start.use && (datecmp(loctime, &tm_start) < 0)) ||
537                (tm_end.use && (datecmp(loctime, &tm_end) >= 0)));
538
539         do {
540                 new_tot_g_nr = 0;
541
542                 for (i = 0; i < NR_ACT; i++) {
543                         if (!id_seq[i])
544                                 continue;
545
546                         p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
547                         if (!IS_SELECTED(act[p]->options))
548                                 continue;
549
550                         if (ONE_GRAPH_PER_ITEM(act[p]->options)) {
551                                  n = act[p]->g_nr * act[p]->nr;
552                         }
553                         else {
554                                 n = act[p]->g_nr;
555                         }
556
557                         if (n > id_g_nr[i]) {
558                                  id_g_nr[i] = n;
559                          }
560                         new_tot_g_nr += n;
561                 }
562
563                 if (new_tot_g_nr > tot_g_nr) {
564                         tot_g_nr = new_tot_g_nr;
565                 }
566
567                 do {
568                         eosaf = read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT | SET_TIMESTAMPS,
569                                                  0, file, &rtype, 0, file_magic, file_actlst,
570                                                  rectime, loctime);
571                         if (eosaf ||
572                             (tm_end.use && (datecmp(loctime, &tm_end) >= 0)))
573                                 /* End of data file or end time exceeded */
574                                 break;
575                 }
576                 while (rtype != R_RESTART);
577
578                 if (eosaf ||
579                     (tm_end.use && (datecmp(loctime, &tm_end) >= 0)))
580                         /*
581                          * End of file, or end time exceeded:
582                          * Current number of graphs is up-to-date.
583                          */
584                         break;
585
586         /*
587          * If we have found a RESTART record then we have also read the list of volatile
588          * activities following it, reallocated the structures and changed the number of
589          * items (act[p]->nr) for those volatile activities. So loop again to compute
590          * the new total number of graphs.
591          */
592         }
593         while (rtype == R_RESTART);
594
595         /* Rewind file and restore items number */
596         if (lseek(ifd, fpos, SEEK_SET) < fpos) {
597                 perror("lseek");
598                 exit(2);
599         }
600         sr_act_nr(save_act_nr, DO_RESTORE);
601
602         return tot_g_nr;
603 }
604 /*
605  ***************************************************************************
606  * Display *one* sample of statistics for one or several activities,
607  * checking that all conditions are met before printing (time start, time
608  * end, interval). Current record should be a record of statistics (R_STATS),
609  * not a special one (R_RESTART or R_COMMENT).
610  *
611  * IN:
612  * @curr                Index in array for current sample statistics.
613  * @use_tm_start        Set to TRUE if option -s has been used.
614  * @use_tm_end          Set to TRUE if option -e has been used.
615  * @reset               Set to TRUE if last_uptime should be reinitialized
616  *                      (used in next_slice() function).
617  * @parm                Pointer on parameters depending on output format
618  *                      (eg.: number of tabulations to print).
619  * @cpu_nr              Number of processors.
620  * @rectime             Structure where timestamp (expressed in local time
621  *                      or in UTC depending on whether options -T/-t have
622  *                      been used or not) has been saved for current record.
623  * @loctime             Structure where timestamp (expressed in local time)
624  *                      has been saved for current record.
625  * @reset_cd            TRUE if static cross_day variable should be reset.
626  * @act_id              Activity to display (only for formats where
627  *                      activities are displayed one at a time) or
628  *                      ALL_ACTIVITIES for all.
629  *
630  * OUT:
631  * @cnt                 Set to 0 to indicate that no other lines of stats
632  *                      should be displayed.
633  *
634  * RETURNS:
635  * 1 if stats have been successfully displayed.
636  ***************************************************************************
637  */
638 int generic_write_stats(int curr, int use_tm_start, int use_tm_end, int reset,
639                         long *cnt, void *parm, __nr_t cpu_nr, struct tm *rectime,
640                         struct tm *loctime, int reset_cd, unsigned int act_id)
641 {
642         int i;
643         unsigned long long dt, itv, g_itv;
644         char cur_date[32], cur_time[32], *pre = NULL;
645         static int cross_day = FALSE;
646
647         if (reset_cd) {
648                 /*
649                  * See note in sar.c.
650                  * NB: Reseting cross_day is needed only if datafile
651                  * may be rewinded (eg. in db or ppc output formats).
652                  */
653                 cross_day = 0;
654         }
655
656         /*
657          * Check time (1).
658          * For this first check, we use the time interval entered on
659          * the command line. This is equivalent to sar's option -i which
660          * selects records at seconds as close as possible to the number
661          * specified by the interval parameter.
662          */
663         if (!next_slice(record_hdr[2].uptime0, record_hdr[curr].uptime0,
664                         reset, interval))
665                 /* Not close enough to desired interval */
666                 return 0;
667
668         /* Check if we are beginning a new day */
669         if (use_tm_start && record_hdr[!curr].ust_time &&
670             (record_hdr[curr].ust_time > record_hdr[!curr].ust_time) &&
671             (record_hdr[curr].hour < record_hdr[!curr].hour)) {
672                 cross_day = TRUE;
673         }
674
675         if (cross_day) {
676                 /*
677                  * This is necessary if we want to properly handle something like:
678                  * sar -s time_start -e time_end with
679                  * time_start(day D) > time_end(day D+1)
680                  */
681                 loctime->tm_hour += 24;
682         }
683
684         /* Check time (2) */
685         if (use_tm_start && (datecmp(loctime, &tm_start) < 0))
686                 /* it's too soon... */
687                 return 0;
688
689         /* Get interval values */
690         get_itv_value(&record_hdr[curr], &record_hdr[!curr],
691                       cpu_nr, &itv, &g_itv);
692
693         /* Check time (3) */
694         if (use_tm_end && (datecmp(loctime, &tm_end) > 0)) {
695                 /* It's too late... */
696                 *cnt = 0;
697                 return 0;
698         }
699
700         dt = itv / HZ;
701         /* Correct rounding error for dt */
702         if ((itv % HZ) >= (HZ / 2)) {
703                 dt++;
704         }
705
706         /* Set date and time strings for current record */
707         set_record_timestamp_string(flags, &record_hdr[curr],
708                                     cur_date, cur_time, 32, rectime);
709
710         if (*fmt[f_position]->f_timestamp) {
711                 pre = (char *) (*fmt[f_position]->f_timestamp)(parm, F_BEGIN, cur_date, cur_time,
712                                                                dt, &file_hdr, flags);
713         }
714
715         /* Display statistics */
716         for (i = 0; i < NR_ACT; i++) {
717
718                 if ((act_id != ALL_ACTIVITIES) && (act[i]->id != act_id))
719                         continue;
720
721                 if ((TEST_MARKUP(fmt[f_position]->options) && CLOSE_MARKUP(act[i]->options)) ||
722                     (IS_SELECTED(act[i]->options) && (act[i]->nr > 0))) {
723
724                         if (format == F_JSON_OUTPUT) {
725                                 /* JSON output */
726                                 int *tab = (int *) parm;
727
728                                 if (IS_SELECTED(act[i]->options) && (act[i]->nr > 0)) {
729
730                                         if (*fmt[f_position]->f_timestamp) {
731                                                 (*fmt[f_position]->f_timestamp)(tab, F_MAIN, cur_date, cur_time,
732                                                                                 dt, &file_hdr, flags);
733                                         }
734                                 }
735                                 (*act[i]->f_json_print)(act[i], curr, *tab, NEED_GLOBAL_ITV(act[i]->options) ?
736                                                         g_itv : itv);
737                         }
738
739                         else if (format == F_XML_OUTPUT) {
740                                 /* XML output */
741                                 int *tab = (int *) parm;
742
743                                 (*act[i]->f_xml_print)(act[i], curr, *tab, NEED_GLOBAL_ITV(act[i]->options) ?
744                                                        g_itv : itv);
745                         }
746
747                         else if (format == F_SVG_OUTPUT) {
748                                 /* SVG output */
749                                 struct svg_parm *svg_p = (struct svg_parm *) parm;
750
751                                 svg_p->dt = (unsigned long) dt;
752                                 (*act[i]->f_svg_print)(act[i], curr, F_MAIN, svg_p,
753                                                        NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv,
754                                                        &record_hdr[curr]);
755                         }
756
757                         else {
758                                 /* Other output formats: db, ppc */
759                                 (*act[i]->f_render)(act[i], (format == F_DB_OUTPUT), pre, curr,
760                                                     NEED_GLOBAL_ITV(act[i]->options) ? g_itv : itv);
761                         }
762                 }
763         }
764
765         if (*fmt[f_position]->f_timestamp) {
766                 (*fmt[f_position]->f_timestamp)(parm, F_END, cur_date, cur_time,
767                                                 dt, &file_hdr, flags);
768         }
769
770         return 1;
771 }
772
773 /*
774  ***************************************************************************
775  * Read stats for current activity from file and print them.
776  * Display at most <count> lines of stats (and possibly comments inserted
777  * in file) located between two LINUX RESTART messages.
778  *
779  * IN:
780  * @ifd         File descriptor of input file.
781  * @fpos        Position in file where reading must start.
782  * @curr        Index in array for current sample statistics.
783  * @act_id      Activity to display, or ~0 for all.
784  * @file_actlst List of (known or unknown) activities in file.
785  * @cpu_nr      Number of processors for current activity data file.
786  * @rectime     Structure where timestamp (expressed in local time or in UTC
787  *              depending on whether options -T/-t have been used or not) can
788  *              be saved for current record.
789  * @loctime     Structure where timestamp (expressed in local time) can be
790  *              saved for current record.
791  * @file        Name of file being read.
792  * @file_magic  file_magic structure filled with file magic header data.
793  *
794  * OUT:
795  * @curr        Index in array for next sample statistics.
796  * @cnt         Number of lines of stats remaining to write.
797  * @eosaf       Set to TRUE if EOF (end of file) has been reached.
798  * @reset       Set to TRUE if last_uptime variable should be
799  *              reinitialized (used in next_slice() function).
800  ***************************************************************************
801  */
802 void rw_curr_act_stats(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf,
803                        unsigned int act_id, int *reset, struct file_activity *file_actlst,
804                         __nr_t cpu_nr, struct tm *rectime, struct tm *loctime,
805                         char *file, struct file_magic *file_magic)
806 {
807         int rtype;
808         int next, reset_cd;
809
810         if (lseek(ifd, fpos, SEEK_SET) < fpos) {
811                 perror("lseek");
812                 exit(2);
813         }
814
815         if (DISPLAY_FIELD_LIST(fmt[f_position]->options)) {
816                 /* Print field list */
817                 list_fields(act_id);
818         }
819
820         /*
821          * Restore the first stats collected.
822          * Used to compute the rate displayed on the first line.
823          */
824         copy_structures(act, id_seq, record_hdr, !*curr, 2);
825
826         *cnt  = count;
827         reset_cd = 1;
828
829         do {
830                 /* Display <count> lines of stats */
831                 *eosaf = read_next_sample(ifd, IGNORE_RESTART | DONT_READ_VOLATILE,
832                                           *curr, file, &rtype, 0, file_magic,
833                                           file_actlst, rectime, loctime);
834
835                 if (!*eosaf && (rtype != R_RESTART) && (rtype != R_COMMENT)) {
836                         next = generic_write_stats(*curr, tm_start.use, tm_end.use, *reset, cnt,
837                                                    NULL, cpu_nr, rectime, loctime, reset_cd, act_id);
838                         reset_cd = 0;
839
840                         if (next) {
841                                 /*
842                                  * next is set to 1 when we were close enough to desired interval.
843                                  * In this case, the call to generic_write_stats() has actually
844                                  * displayed a line of stats.
845                                  */
846                                 *curr ^= 1;
847                                 if (*cnt > 0) {
848                                         (*cnt)--;
849                                 }
850                         }
851                         *reset = FALSE;
852                 }
853         }
854         while (*cnt && !*eosaf && (rtype != R_RESTART));
855
856         *reset = TRUE;
857 }
858
859 /*
860  ***************************************************************************
861  * Read stats for current activity from file and display its SVG graphs.
862  * At most <count> lines of stats are taken into account.
863  *
864  * IN:
865  * @ifd         File descriptor of input file.
866  * @fpos        Position in file where reading must start.
867  * @curr        Index in array for current sample statistics.
868  * @p           Current activity position.
869  * @file_actlst List of (known or unknown) activities in file.
870  * @cpu_nr      Number of processors for current activity data file.
871  * @rectime     Structure where timestamp (expressed in local time or in UTC
872  *              depending on whether options -T/-t have been used or not) can
873  *              be saved for current record.
874  * @loctime     Structure where timestamp (expressed in local time) can be
875  *              saved for current record.
876  * @file        Name of file being read.
877  * @file_magic  file_magic structure filled with file magic header data.
878  * @save_act_nr Array where the number of volatile activities are saved
879  *              for current position in file.
880  * @g_nr        Number of graphs already displayed (for all activities).
881  *
882  * OUT:
883  * @cnt         Number of lines of stats remaining to write.
884  * @eosaf       Set to TRUE if EOF (end of file) has been reached.
885  * @reset       Set to TRUE if last_uptime variable should be
886  *              reinitialized (used in next_slice() function).
887  * @g_nr        Total number of views displayed (including current activity).
888  ***************************************************************************
889  */
890 void display_curr_act_graphs(int ifd, off_t fpos, int *curr, long *cnt, int *eosaf,
891                              int p, int *reset, struct file_activity *file_actlst,
892                              __nr_t cpu_nr, struct tm *rectime, struct tm *loctime,
893                              char *file, struct file_magic *file_magic,
894                              __nr_t save_act_nr[], int *g_nr)
895 {
896         struct svg_parm parm;
897         int rtype;
898         int next, reset_cd;
899
900         /* Rewind file... */
901         if (lseek(ifd, fpos, SEEK_SET) < fpos) {
902                 perror("lseek");
903                 exit(2);
904         }
905         /*
906          * ... and restore number of items for volatile activities
907          * for this position in file.
908          */
909         sr_act_nr(save_act_nr, DO_RESTORE);
910
911         /*
912          * Restore the first stats collected.
913          * Used to compute the rate displayed on the first line.
914          */
915         copy_structures(act, id_seq, record_hdr, !*curr, 2);
916
917         parm.graph_no = *g_nr;
918         parm.ust_time_ref = get_time_ref();
919         parm.ust_time_first = record_hdr[2].ust_time;
920         parm.restart = TRUE;
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, loctime);
932
933                 if (!*eosaf && (rtype != R_COMMENT) && (rtype != R_RESTART)) {
934
935                         next = generic_write_stats(*curr, tm_start.use, tm_end.use, *reset, cnt,
936                                                    &parm, cpu_nr, rectime, loctime, reset_cd, act[p]->id);
937                         reset_cd = 0;
938                         if (next) {
939                                 /*
940                                  * next is set to 1 when we were close enough to desired interval.
941                                  * In this case, the call to generic_write_stats() has actually
942                                  * displayed a line of stats.
943                                  */
944                                 parm.restart = FALSE;
945                                 *curr ^= 1;
946                                 if (*cnt > 0) {
947                                         (*cnt)--;
948                                 }
949                         }
950                         *reset = FALSE;
951                 }
952                 if (!*eosaf && (rtype == R_RESTART)) {
953                         parm.restart = TRUE;
954                         *reset = TRUE;
955                         /* Go to next statistics record, if possible */
956                         do {
957                                 *eosaf = read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT | SET_TIMESTAMPS,
958                                                           *curr, file, &rtype, 0, file_magic,
959                                                           file_actlst, rectime, loctime);
960                         }
961                         while (!*eosaf && ((rtype == R_RESTART) || (rtype == R_COMMENT)));
962                         *curr ^= 1;
963                 }
964                 
965         }
966         while (!*eosaf);
967
968         *reset = TRUE;
969
970         /* Determine X axis end value */
971         if (DISPLAY_ONE_DAY(flags) &&
972             (parm.ust_time_ref + (3600 * 24) > record_hdr[!*curr].ust_time)) {
973                 parm.ust_time_end = parm.ust_time_ref + (3600 * 24);
974         }
975         else {
976                 parm.ust_time_end = record_hdr[!*curr].ust_time;
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
991  *
992  * IN:
993  * @ifd         File descriptor of input file.
994  * @file_actlst List of (known or unknown) activities in file.
995  * @file        System activity data file name (name of file being read).
996  * @file_magic  System activity file magic header.
997  * @cpu_nr      Number of processors for current activity data file.
998  * @rectime     Structure where timestamp (expressed in local time or in UTC
999  *              depending on whether options -T/-t have been used or not) can
1000  *              be saved for current record.
1001  * @loctime     Structure where timestamp (expressed in local time) can be
1002  *              saved for current record.
1003  ***************************************************************************
1004  */
1005 void logic1_display_loop(int ifd, struct file_activity *file_actlst, char *file,
1006                          struct file_magic *file_magic, __nr_t cpu_nr,
1007                          struct tm *rectime, struct tm *loctime)
1008 {
1009         int curr, tab = 0, rtype;
1010         int eosaf, next, reset = FALSE;
1011         __nr_t save_act_nr[NR_ACT] = {0};
1012         long cnt = 1;
1013         off_t fpos;
1014
1015         /* Save current file position */
1016         if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) {
1017                 perror("lseek");
1018                 exit(2);
1019         }
1020         /* Save number of activities items for current file position */
1021         sr_act_nr(save_act_nr, DO_SAVE);
1022
1023         /* Print header (eg. XML file header) */
1024         if (*fmt[f_position]->f_header) {
1025                 (*fmt[f_position]->f_header)(&tab, F_BEGIN, file, file_magic,
1026                                              &file_hdr, cpu_nr, act, id_seq);
1027         }
1028
1029         /* Process activities */
1030         if (*fmt[f_position]->f_statistics) {
1031                 (*fmt[f_position]->f_statistics)(&tab, F_BEGIN);
1032         }
1033
1034         do {
1035                 /*
1036                  * If this record is a special (RESTART or COMMENT) one,
1037                  * skip it and try to read the next record in file.
1038                  */
1039                 do {
1040                         eosaf = read_next_sample(ifd, IGNORE_COMMENT | IGNORE_RESTART, 0,
1041                                                  file, &rtype, tab, file_magic, file_actlst,
1042                                                  rectime, loctime);
1043                 }
1044                 while (!eosaf && ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
1045                         (tm_start.use && (datecmp(loctime, &tm_start) < 0)) ||
1046                         (tm_end.use && (datecmp(loctime, &tm_end) >= 0))));
1047
1048                 /* Save the first stats collected. Used for example in next_slice() function */
1049                 copy_structures(act, id_seq, record_hdr, 2, 0);
1050
1051                 curr = 1;
1052                 cnt = count;
1053                 reset = TRUE;
1054
1055                 if (!eosaf) {
1056                         do {
1057                                 eosaf = read_next_sample(ifd, IGNORE_COMMENT | IGNORE_RESTART, curr,
1058                                                          file, &rtype, tab, file_magic, file_actlst,
1059                                                          rectime, loctime);
1060
1061                                 if (!eosaf && (rtype != R_COMMENT) && (rtype != R_RESTART)) {
1062                                         if (*fmt[f_position]->f_statistics) {
1063                                                 (*fmt[f_position]->f_statistics)(&tab, F_MAIN);
1064                                         }
1065
1066                                         /* next is set to 1 when we were close enough to desired interval */
1067                                         next = generic_write_stats(curr, tm_start.use, tm_end.use, reset,
1068                                                                   &cnt, &tab, cpu_nr, rectime, loctime,
1069                                                                   FALSE, ALL_ACTIVITIES);
1070
1071                                         if (next) {
1072                                                 curr ^= 1;
1073                                                 if (cnt > 0) {
1074                                                         cnt--;
1075                                                 }
1076                                         }
1077                                         reset = FALSE;
1078                                 }
1079                         }
1080                         while (cnt && !eosaf && (rtype != R_RESTART));
1081
1082                         if (!cnt) {
1083                                 /* Go to next Linux restart, if possible */
1084                                 do {
1085                                         eosaf = read_next_sample(ifd, IGNORE_COMMENT | IGNORE_RESTART, curr,
1086                                                                  file, &rtype, tab, file_magic, file_actlst,
1087                                                                  rectime, loctime);
1088                                 }
1089                                 while (!eosaf && (rtype != R_RESTART));
1090                         }
1091                         reset = TRUE;
1092                 }
1093         }
1094         while (!eosaf);
1095
1096         if (*fmt[f_position]->f_statistics) {
1097                 (*fmt[f_position]->f_statistics)(&tab, F_END);
1098         }
1099
1100         /* Rewind file... */
1101         if (lseek(ifd, fpos, SEEK_SET) < fpos) {
1102                 perror("lseek");
1103                 exit(2);
1104         }
1105         /*
1106          * ... and restore number of items for volatile activities
1107          * for this position in file.
1108          */
1109         sr_act_nr(save_act_nr, DO_RESTORE);
1110
1111         /* Process now RESTART entries to display restart messages */
1112         if (*fmt[f_position]->f_restart) {
1113                 (*fmt[f_position]->f_restart)(&tab, F_BEGIN, NULL, NULL, FALSE,
1114                                               &file_hdr, 0);
1115         }
1116
1117         do {
1118                 eosaf = read_next_sample(ifd, IGNORE_COMMENT, 0,
1119                                          file, &rtype, tab, file_magic, file_actlst,
1120                                          rectime, loctime);
1121         }
1122         while (!eosaf);
1123
1124         if (*fmt[f_position]->f_restart) {
1125                 (*fmt[f_position]->f_restart)(&tab, F_END, NULL, NULL, FALSE, &file_hdr, 0);
1126         }
1127
1128         /* Rewind file... */
1129         if (lseek(ifd, fpos, SEEK_SET) < fpos) {
1130                 perror("lseek");
1131                 exit(2);
1132         }
1133         /*
1134          * ... and restore number of items for volatile activities
1135          * for this position in file.
1136          */
1137         sr_act_nr(save_act_nr, DO_RESTORE);
1138
1139         /* Last, process COMMENT entries to display comments */
1140         if (DISPLAY_COMMENT(flags)) {
1141                 if (*fmt[f_position]->f_comment) {
1142                         (*fmt[f_position]->f_comment)(&tab, F_BEGIN, NULL, NULL, 0, NULL,
1143                                                       &file_hdr);
1144                 }
1145                 do {
1146                         eosaf = read_next_sample(ifd, IGNORE_RESTART, 0,
1147                                                  file, &rtype, tab, file_magic, file_actlst,
1148                                                  rectime, loctime);
1149                 }
1150                 while (!eosaf);
1151
1152                 if (*fmt[f_position]->f_comment) {
1153                         (*fmt[f_position]->f_comment)(&tab, F_END, NULL, NULL, 0, NULL,
1154                                                       &file_hdr);
1155                 }
1156         }
1157
1158         /* Print header trailer */
1159         if (*fmt[f_position]->f_header) {
1160                 (*fmt[f_position]->f_header)(&tab, F_END, file, file_magic,
1161                                              &file_hdr, cpu_nr, act, id_seq);
1162         }
1163 }
1164
1165 /*
1166  ***************************************************************************
1167  * Display file contents in selected format (logic #2).
1168  * Logic #2:    Grouped by activity. Sorted by timestamp. Stop on RESTART
1169  *              records.
1170  * Formats:     ppc, CSV
1171  *
1172  * IN:
1173  * @ifd         File descriptor of input file.
1174  * @file_actlst List of (known or unknown) activities in file.
1175  * @cpu_nr      Number of processors for current activity data file.
1176  * @rectime     Structure where timestamp (expressed in local time or in UTC
1177  *              depending on whether options -T/-t have been used or not) can
1178  *              be saved for current record.
1179  * @loctime     Structure where timestamp (expressed in local time) can be
1180  *              saved for current record.
1181  * @file        Name of file being read.
1182  * @file_magic  file_magic structure filled with file magic header data.
1183  ***************************************************************************
1184  */
1185 void logic2_display_loop(int ifd, struct file_activity *file_actlst, __nr_t cpu_nr,
1186                          struct tm *rectime, struct tm *loctime, char *file,
1187                          struct file_magic *file_magic)
1188 {
1189         int i, p;
1190         int curr = 1, rtype;
1191         int eosaf = TRUE, reset = FALSE;
1192         long cnt = 1;
1193         off_t fpos;
1194
1195         /* Read system statistics from file */
1196         do {
1197                 /*
1198                  * If this record is a special (RESTART or COMMENT) one, print it and
1199                  * (try to) get another one.
1200                  */
1201                 do {
1202                         if (read_next_sample(ifd, IGNORE_NOTHING, 0,
1203                                              file, &rtype, 0, file_magic, file_actlst,
1204                                              rectime, loctime))
1205                                 /* End of sa data file */
1206                                 return;
1207                 }
1208                 while ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
1209                        (tm_start.use && (datecmp(loctime, &tm_start) < 0)) ||
1210                        (tm_end.use && (datecmp(loctime, &tm_end) >= 0)));
1211
1212                 /* Save the first stats collected. Used for example in next_slice() function */
1213                 copy_structures(act, id_seq, record_hdr, 2, 0);
1214
1215                 /* Set flag to reset last_uptime variable. Should be done after a LINUX RESTART record */
1216                 reset = TRUE;
1217
1218                 /* Save current file position */
1219                 if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) {
1220                         perror("lseek");
1221                         exit(2);
1222                 }
1223
1224                 /* Read and write stats located between two possible Linux restarts */
1225
1226                 if (DISPLAY_HORIZONTALLY(flags)) {
1227                         /*
1228                          * If stats are displayed horizontally, then all activities
1229                          * are printed on the same line.
1230                          */
1231                         rw_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf,
1232                                           ALL_ACTIVITIES, &reset, file_actlst,
1233                                           cpu_nr, rectime, loctime, file, file_magic);
1234                 }
1235                 else {
1236                         /* For each requested activity... */
1237                         for (i = 0; i < NR_ACT; i++) {
1238
1239                                 if (!id_seq[i])
1240                                         continue;
1241
1242                                 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
1243                                 if (!IS_SELECTED(act[p]->options))
1244                                         continue;
1245
1246                                 if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) {
1247                                         rw_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf,
1248                                                           act[p]->id, &reset, file_actlst,
1249                                                           cpu_nr, rectime, loctime, file,
1250                                                           file_magic);
1251                                 }
1252                                 else {
1253                                         unsigned int optf, msk;
1254
1255                                         optf = act[p]->opt_flags;
1256
1257                                         for (msk = 1; msk < 0x100; msk <<= 1) {
1258                                                 if ((act[p]->opt_flags & 0xff) & msk) {
1259                                                         act[p]->opt_flags &= (0xffffff00 + msk);
1260
1261                                                         rw_curr_act_stats(ifd, fpos, &curr, &cnt, &eosaf,
1262                                                                           act[p]->id, &reset, file_actlst,
1263                                                                           cpu_nr, rectime, loctime, file,
1264                                                                           file_magic);
1265                                                         act[p]->opt_flags = optf;
1266                                                 }
1267                                         }
1268                                 }
1269                         }
1270                 }
1271
1272                 if (!cnt) {
1273                         /* Go to next Linux restart, if possible */
1274                         do {
1275                                 eosaf = read_next_sample(ifd, IGNORE_RESTART | DONT_READ_VOLATILE,
1276                                                          curr, file, &rtype, 0, file_magic,
1277                                                          file_actlst, rectime, loctime);
1278                         }
1279                         while (!eosaf && (rtype != R_RESTART));
1280                 }
1281
1282                 /*
1283                  * The last record we read was a RESTART one: Print it.
1284                  * NB: Unlike COMMENTS records (which are displayed for each
1285                  * activity), RESTART ones are only displayed once.
1286                  */
1287                 if (!eosaf && (record_hdr[curr].record_type == R_RESTART)) {
1288                         print_special_record(&record_hdr[curr], flags, &tm_start, &tm_end,
1289                                              R_RESTART, ifd, rectime, loctime, file, 0,
1290                                              file_magic, &file_hdr, act, fmt[f_position]);
1291                 }
1292         }
1293         while (!eosaf);
1294 }
1295
1296 /*
1297  ***************************************************************************
1298  * Display file contents in selected format (logic #3).
1299  * Logic #3:    Special logic for SVG output format.
1300  * Formats:     SVG
1301  *
1302  * IN:
1303  * @ifd         File descriptor of input file.
1304  * @file_actlst List of (known or unknown) activities in file.
1305  * @cpu_nr      Number of processors for current activity data file.
1306  * @rectime     Structure where timestamp (expressed in local time or in UTC
1307  *              depending on whether options -T/-t have been used or not) can
1308  *              be saved for current record.
1309  * @loctime     Structure where timestamp (expressed in local time) can be
1310  *              saved for current record.
1311  * @file        Name of file being read.
1312  * @file_magic  file_magic structure filled with file magic header data.
1313  ***************************************************************************
1314  */
1315 void logic3_display_loop(int ifd, struct file_activity *file_actlst, __nr_t cpu_nr,
1316                          struct tm *rectime, struct tm *loctime, char *file,
1317                          struct file_magic *file_magic)
1318 {
1319         int i, p;
1320         int curr = 1, rtype, g_nr = 0;
1321         int eosaf = TRUE, reset = TRUE;
1322         long cnt = 1;
1323         off_t fpos;
1324         int graph_nr = 0;
1325         __nr_t save_act_nr[NR_ACT] = {0};
1326
1327         /* Use a decimal point to make SVG code locale independent */
1328         setlocale(LC_NUMERIC, "C");
1329
1330         /* Calculate the number of graphs to display */
1331         graph_nr = get_svg_graph_nr(ifd, file, file_magic,
1332                                     file_actlst, rectime, loctime);
1333         if (!graph_nr)
1334                 /* No graph to display */
1335                 return;
1336
1337         /* Print SVG header */
1338         if (*fmt[f_position]->f_header) {
1339                 (*fmt[f_position]->f_header)(&graph_nr, F_BEGIN + F_MAIN, file, file_magic,
1340                                              &file_hdr, cpu_nr, act, id_seq);
1341         }
1342
1343         /*
1344         * If this record is a special (RESTART or COMMENT) one, ignore it and
1345         * (try to) get another one.
1346         */
1347         do {
1348                 if (read_next_sample(ifd, IGNORE_RESTART | IGNORE_COMMENT, 0,
1349                                      file, &rtype, 0, file_magic, file_actlst,
1350                                      rectime, loctime))
1351                         /* End of sa data file */
1352                         return;
1353         }
1354         while ((rtype == R_RESTART) || (rtype == R_COMMENT) ||
1355                (tm_start.use && (datecmp(loctime, &tm_start) < 0)) ||
1356                (tm_end.use && (datecmp(loctime, &tm_end) >= 0)));
1357
1358         /* Save the first stats collected. Used for example in next_slice() function */
1359         copy_structures(act, id_seq, record_hdr, 2, 0);
1360
1361         /* Save current file position */
1362         if ((fpos = lseek(ifd, 0, SEEK_CUR)) < 0) {
1363                 perror("lseek");
1364                 exit(2);
1365         }
1366         /* Save number of activities items for current file position */
1367         sr_act_nr(save_act_nr, DO_SAVE);
1368
1369         /* For each requested activity, display graphs */
1370         for (i = 0; i < NR_ACT; i++) {
1371
1372                 if (!id_seq[i])
1373                         continue;
1374
1375                 p = get_activity_position(act, id_seq[i], EXIT_IF_NOT_FOUND);
1376                 if (!IS_SELECTED(act[p]->options) || !act[p]->g_nr)
1377                         continue;
1378
1379                 if (!HAS_MULTIPLE_OUTPUTS(act[p]->options)) {
1380                         display_curr_act_graphs(ifd, fpos, &curr, &cnt, &eosaf,
1381                                                 p, &reset, file_actlst,
1382                                                 cpu_nr, rectime, loctime, file,
1383                                                 file_magic, save_act_nr, &g_nr);
1384                 }
1385                 else {
1386                         unsigned int optf, msk;
1387
1388                         optf = act[p]->opt_flags;
1389
1390                         for (msk = 1; msk < 0x100; msk <<= 1) {
1391                                 if ((act[p]->opt_flags & 0xff) & msk) {
1392                                         act[p]->opt_flags &= (0xffffff00 + msk);
1393                                         display_curr_act_graphs(ifd, fpos, &curr, &cnt, &eosaf,
1394                                                                 p, &reset, file_actlst,
1395                                                                 cpu_nr, rectime, loctime, file,
1396                                                                 file_magic, save_act_nr, &g_nr);
1397                                         act[p]->opt_flags = optf;
1398                                 }
1399                         }
1400                 }
1401         }
1402
1403         /* Print SVG trailer */
1404         if (*fmt[f_position]->f_header) {
1405                 (*fmt[f_position]->f_header)(&graph_nr, F_END, file, file_magic,
1406                                              &file_hdr, cpu_nr, act, id_seq);
1407         }
1408 }
1409
1410 /*
1411  ***************************************************************************
1412  * Check system activity datafile contents before displaying stats.
1413  * Display file header if option -H has been entered, else call function
1414  * corresponding to selected output format.
1415  *
1416  * IN:
1417  * @dfile       System activity data file name.
1418  ***************************************************************************
1419  */
1420 void read_stats_from_file(char dfile[])
1421 {
1422         struct file_magic file_magic;
1423         struct file_activity *file_actlst = NULL;
1424         struct tm rectime, loctime;
1425         int ifd, ignore, tab = 0;
1426         __nr_t cpu_nr;
1427
1428         /* Prepare file for reading and read its headers */
1429         ignore = ACCEPT_BAD_FILE_FORMAT(fmt[f_position]->options);
1430         check_file_actlst(&ifd, dfile, act, &file_magic, &file_hdr,
1431                           &file_actlst, id_seq, ignore);
1432
1433         /* Now pick up number of proc for this file */
1434         cpu_nr = act[get_activity_position(act, A_CPU, EXIT_IF_NOT_FOUND)]->nr;
1435
1436         if (DISPLAY_HDR_ONLY(flags)) {
1437                 if (*fmt[f_position]->f_header) {
1438                         /* Display only data file header then exit */
1439                         (*fmt[f_position]->f_header)(&tab, F_BEGIN + F_END, dfile, &file_magic,
1440                                                      &file_hdr, cpu_nr, act, id_seq);
1441                 }
1442                 exit(0);
1443         }
1444
1445         /* Perform required allocations */
1446         allocate_structures(act);
1447
1448         /* Call function corresponding to selected output format */
1449         if (format == F_SVG_OUTPUT) {
1450                 logic3_display_loop(ifd, file_actlst, cpu_nr,
1451                                     &rectime, &loctime, dfile, &file_magic);
1452         }
1453         else if (DISPLAY_GROUPED_STATS(fmt[f_position]->options)) {
1454                 logic2_display_loop(ifd, file_actlst, cpu_nr,
1455                                     &rectime, &loctime, dfile, &file_magic);
1456         }
1457         else {
1458                 logic1_display_loop(ifd, file_actlst, dfile,
1459                                     &file_magic, cpu_nr, &rectime, &loctime);
1460         }
1461
1462         close(ifd);
1463
1464         free(file_actlst);
1465         free_structures(act);
1466 }
1467
1468 /*
1469  ***************************************************************************
1470  * Main entry to the sadf program
1471  ***************************************************************************
1472  */
1473 int main(int argc, char **argv)
1474 {
1475         int opt = 1, sar_options = 0;
1476         int day_offset = 0;
1477         int i, rc;
1478         char dfile[MAX_FILE_LEN];
1479         char *t;
1480
1481         /* Get HZ */
1482         get_HZ();
1483
1484         /* Compute page shift in kB */
1485         get_kb_shift();
1486
1487         dfile[0] = '\0';
1488
1489 #ifdef USE_NLS
1490         /* Init National Language Support */
1491         init_nls();
1492 #endif
1493
1494         tm_start.use = tm_end.use = FALSE;
1495
1496         /* Allocate and init activity bitmaps */
1497         allocate_bitmaps(act);
1498
1499         /* Init some structures */
1500         init_structures();
1501
1502         /* Process options */
1503         while (opt < argc) {
1504
1505                 if (!strcmp(argv[opt], "-I")) {
1506                         if (argv[++opt] && sar_options) {
1507                                 if (parse_sar_I_opt(argv, &opt, act)) {
1508                                         usage(argv[0]);
1509                                 }
1510                         }
1511                         else {
1512                                 usage(argv[0]);
1513                         }
1514                 }
1515
1516                 else if (!strcmp(argv[opt], "-P")) {
1517                         if (parse_sa_P_opt(argv, &opt, &flags, act)) {
1518                                 usage(argv[0]);
1519                         }
1520                 }
1521
1522                 else if (!strcmp(argv[opt], "-s")) {
1523                         /* Get time start */
1524                         if (parse_timestamp(argv, &opt, &tm_start, DEF_TMSTART)) {
1525                                 usage(argv[0]);
1526                         }
1527                 }
1528
1529                 else if (!strcmp(argv[opt], "-e")) {
1530                         /* Get time end */
1531                         if (parse_timestamp(argv, &opt, &tm_end, DEF_TMEND)) {
1532                                 usage(argv[0]);
1533                         }
1534                 }
1535
1536                 else if (!strcmp(argv[opt], "-O")) {
1537                         /* Parse SVG options */
1538                         if (!argv[++opt] || sar_options) {
1539                                 usage(argv[0]);
1540                         }
1541                         for (t = strtok(argv[opt], ","); t; t = strtok(NULL, ",")) {
1542                                 if (!strcmp(t, K_SKIP_EMPTY)) {
1543                                         flags |= S_F_SVG_SKIP;
1544                                 }
1545                                 else if (!strcmp(t, K_AUTOSCALE)) {
1546                                         flags |= S_F_SVG_AUTOSCALE;
1547                                 }
1548                                 else if (!strcmp(t, K_ONEDAY)) {
1549                                         flags |= S_F_SVG_ONE_DAY;
1550                                 }
1551                                 else {
1552                                         usage(argv[0]);
1553                                 }
1554                         }
1555                         opt++;
1556                 }
1557
1558                 else if ((strlen(argv[opt]) > 1) &&
1559                          (strlen(argv[opt]) < 4) &&
1560                          !strncmp(argv[opt], "-", 1) &&
1561                          (strspn(argv[opt] + 1, DIGITS) == (strlen(argv[opt]) - 1))) {
1562                         if (dfile[0] || day_offset) {
1563                                 /* File already specified */
1564                                 usage(argv[0]);
1565                         }
1566                         day_offset = atoi(argv[opt++] + 1);
1567                 }
1568
1569                 else if (!strcmp(argv[opt], "--")) {
1570                         sar_options = 1;
1571                         opt++;
1572                 }
1573
1574                 else if (!strcmp(argv[opt], "-m")) {
1575                         if (argv[++opt] && sar_options) {
1576                                 /* Parse sar's option -m */
1577                                 if (parse_sar_m_opt(argv, &opt, act)) {
1578                                         usage(argv[0]);
1579                                 }
1580                         }
1581                         else {
1582                                 usage(argv[0]);
1583                         }
1584                 }
1585
1586                 else if (!strcmp(argv[opt], "-n")) {
1587                         if (argv[++opt] && sar_options) {
1588                                 /* Parse sar's option -n */
1589                                 if (parse_sar_n_opt(argv, &opt, act)) {
1590                                         usage(argv[0]);
1591                                 }
1592                         }
1593                         else {
1594                                 usage(argv[0]);
1595                         }
1596                 }
1597
1598                 else if (!strncmp(argv[opt], "-", 1)) {
1599                         /* Other options not previously tested */
1600                         if (sar_options) {
1601                                 if ((rc = parse_sar_opt(argv, &opt, act, &flags, C_SADF)) != 0) {
1602                                         if (rc == 1) {
1603                                                 usage(argv[0]);
1604                                         }
1605                                         exit(1);
1606                                 }
1607                         }
1608                         else {
1609
1610                                 for (i = 1; *(argv[opt] + i); i++) {
1611
1612                                         switch (*(argv[opt] + i)) {
1613
1614                                         case 'C':
1615                                                 flags |= S_F_COMMENT;
1616                                                 break;
1617
1618                                         case 'c':
1619                                                 if (format) {
1620                                                         usage(argv[0]);
1621                                                 }
1622                                                 format = F_CONV_OUTPUT;
1623                                                 break;
1624
1625                                         case 'd':
1626                                                 if (format) {
1627                                                         usage(argv[0]);
1628                                                 }
1629                                                 format = F_DB_OUTPUT;
1630                                                 break;
1631
1632                                         case 'g':
1633                                                 if (format) {
1634                                                         usage(argv[0]);
1635                                                 }
1636                                                 format = F_SVG_OUTPUT;
1637                                                 break;
1638
1639                                         case 'h':
1640                                                 flags |= S_F_HORIZONTALLY;
1641                                                 break;
1642
1643                                         case 'H':
1644                                                 flags |= S_F_HDR_ONLY;
1645                                                 break;
1646
1647                                         case 'j':
1648                                                 if (format) {
1649                                                         usage(argv[0]);
1650                                                 }
1651                                                 format = F_JSON_OUTPUT;
1652                                                 break;
1653
1654                                         case 'p':
1655                                                 if (format) {
1656                                                         usage(argv[0]);
1657                                                 }
1658                                                 format = F_PPC_OUTPUT;
1659                                                 break;
1660
1661                                         case 'T':
1662                                                 flags |= S_F_LOCAL_TIME;
1663                                                 break;
1664
1665                                         case 't':
1666                                                 flags |= S_F_TRUE_TIME;
1667                                                 break;
1668
1669                                         case 'U':
1670                                                 flags |= S_F_SEC_EPOCH;
1671                                                 break;
1672
1673                                         case 'x':
1674                                                 if (format) {
1675                                                         usage(argv[0]);
1676                                                 }
1677                                                 format = F_XML_OUTPUT;
1678                                                 break;
1679
1680                                         case 'V':
1681                                                 print_version();
1682                                                 break;
1683
1684                                         default:
1685                                                 usage(argv[0]);
1686                                         }
1687                                 }
1688                         }
1689                         opt++;
1690                 }
1691
1692                 /* Get data file name */
1693                 else if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1694                         if (dfile[0] || day_offset) {
1695                                 /* File already specified */
1696                                 usage(argv[0]);
1697                         }
1698                         if (!strcmp(argv[opt], "-")) {
1699                                 /* File name set to '-' */
1700                                 set_default_file(dfile, 0, -1);
1701                                 opt++;
1702                         }
1703                         else if (!strncmp(argv[opt], "-", 1)) {
1704                                 /* Bad option */
1705                                 usage(argv[0]);
1706                         }
1707                         else {
1708                                 /* Write data to file */
1709                                 strncpy(dfile, argv[opt++], MAX_FILE_LEN);
1710                                 dfile[MAX_FILE_LEN - 1] = '\0';
1711                                 /* Check if this is an alternate directory for sa files */
1712                                 check_alt_sa_dir(dfile, 0, -1);
1713                         }
1714                 }
1715
1716                 else if (interval < 0) {
1717                         /* Get interval */
1718                         if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1719                                 usage(argv[0]);
1720                         }
1721                         interval = atol(argv[opt++]);
1722                         if (interval <= 0) {
1723                                 usage(argv[0]);
1724                         }
1725                 }
1726
1727                 else {
1728                         /* Get count value */
1729                         if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
1730                                 usage(argv[0]);
1731                         }
1732                         if (count) {
1733                                 /* Count parameter already set */
1734                                 usage(argv[0]);
1735                         }
1736                         count = atol(argv[opt++]);
1737                         if (count < 0) {
1738                                 usage(argv[0]);
1739                         }
1740                         else if (!count) {
1741                                 count = -1;     /* To generate a report continuously */
1742                         }
1743                 }
1744         }
1745
1746         /* sadf reads current daily data file by default */
1747         if (!dfile[0]) {
1748                 set_default_file(dfile, day_offset, -1);
1749         }
1750
1751         if (tm_start.use && tm_end.use && (tm_end.tm_hour < tm_start.tm_hour)) {
1752                 tm_end.tm_hour += 24;
1753         }
1754
1755         if (USE_PRETTY_OPTION(flags)) {
1756                 dm_major = get_devmap_major();
1757         }
1758
1759         /* Options -T, -t and -U are mutually exclusive */
1760         if ((PRINT_LOCAL_TIME(flags) + PRINT_TRUE_TIME(flags) +
1761             PRINT_SEC_EPOCH(flags)) > 1) {
1762                 usage(argv[0]);
1763         }
1764
1765         /*
1766          * Display all the contents of the daily data file if the count parameter
1767          * was not set on the command line.
1768          */
1769         if (!count) {
1770                 count = -1;
1771         }
1772
1773         /* Default is CPU activity */
1774         select_default_activity(act);
1775
1776         /* Check options consistency with selected output format. Default is PPC display */
1777         check_format_options();
1778
1779         if (interval < 0) {
1780                 interval = 1;
1781         }
1782
1783         if (format == F_CONV_OUTPUT) {
1784                 /* Convert file to current format */
1785                 convert_file(dfile, act);
1786         }
1787         else {
1788                 /* Read stats from file */
1789                 read_stats_from_file(dfile);
1790         }
1791
1792         /* Free bitmaps */
1793         free_bitmaps(act);
1794
1795         return 0;
1796 }