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