]> granicus.if.org Git - sysstat/blob - svg_stats.c
Merge branch 'clang' of https://github.com/pschiffe/sysstat
[sysstat] / svg_stats.c
1 /*
2  * svg_stats.c: Funtions used by sadf to display statistics in SVG format.
3  * (C) 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 <stdarg.h>
25 #include <stdlib.h>
26 #include <float.h>
27
28 #include "sa.h"
29 #include "sadf.h"
30 #include "ioconf.h"
31 #include "svg_stats.h"
32
33 #ifdef USE_NLS
34 #include <locale.h>
35 #include <libintl.h>
36 #define _(string) gettext(string)
37 #else
38 #define _(string) (string)
39 #endif
40
41 extern unsigned int flags;
42 extern unsigned int dm_major;
43
44 unsigned int svg_colors[] = {0x00cc00, 0xff00bf, 0x00ffff, 0xff0000,
45                              0xe85f00, 0x0000ff, 0x006020, 0x7030a0,
46                              0xffff00, 0x666635, 0xd60093, 0x00bfbf,
47                              0xcc3300, 0xbfbfbf, 0xffffbf, 0xff3300};
48 #define SVG_COLORS_IDX_MASK     0x0f
49
50 /*
51  ***************************************************************************
52  * Compare the values of a statistics sample with the max and min values
53  * already found in previous samples for this same activity. If some new
54  * min or max values are found, then save them.
55  * The structure containing the statistics sample is composed of @llu_nr
56  * unsigned long long fields, followed by @lu_nr unsigned long fields, then
57  * followed by @u_nr unsigned int fields.
58  *
59  * IN:
60  * @llu_nr      Number of unsigned long long fields composing the structure.
61  * @lu_nr       Number of unsigned long fields composing the structure.
62  * @u_nr        Number of unsigned int fields composing the structure.
63  * @cs          Pointer on current sample statistics structure.
64  * @ps          Pointer on previous sample statistics structure (may be NULL).
65  * @itv         Interval of time in jiffies.
66  * @minv        Array containing min values already found for this activity.
67  * @maxv        Array containing max values already found for this activity.
68  *
69  * OUT:
70  * @minv        Array containg the possible new min values for current activity.
71  * @maxv        Array containg the possible new max values for current activity.
72  *
73  * NB: @minv and @maxv arrays contain values in the same order as the fields
74  * in the statistics structure.
75  ***************************************************************************
76  */
77 void save_extrema(int llu_nr, int lu_nr, int u_nr, void *cs, void *ps,
78                   unsigned long long itv, double minv[], double maxv[])
79 {
80         unsigned long long *lluc, *llup;
81         unsigned long *luc, *lup;
82         unsigned int *uc, *up;
83         double val;
84         int i, m = 0;
85
86         /* Compare unsigned long long fields */
87         lluc = (unsigned long long *) cs;
88         llup = (unsigned long long *) ps;
89         for (i = 0; i < llu_nr; i++, m++) {
90                 if (ps) {
91                         val = S_VALUE(*llup, *lluc, itv);
92                 }
93                 else {
94                         /*
95                          * If no pointer on previous sample has been given
96                          * then the value is not a per-second one.
97                          */
98                         val = (double) *lluc;
99                 }
100                 if (val < minv[m]) {
101                         minv[m] = val;
102                 }
103                 if (val > maxv[m]) {
104                         maxv[m] = val;
105                 }
106                 lluc = (unsigned long long *) ((char *) lluc + ULL_ALIGNMENT_WIDTH);
107                 if (ps) {
108                         llup = (unsigned long long *) ((char *) llup + ULL_ALIGNMENT_WIDTH);
109                 }
110         }
111
112         /* Compare unsigned long fields */
113         luc = (unsigned long *) lluc;
114         lup = (unsigned long *) llup;
115         for (i = 0; i < lu_nr; i++, m++) {
116                 if (ps) {
117                         val = S_VALUE(*lup, *luc, itv);
118                 }
119                 else {
120                         val = (double) *luc;
121                 }
122                 if (val < minv[m]) {
123                         minv[m] = val;
124                 }
125                 if (val > maxv[m]) {
126                         maxv[m] = val;
127                 }
128                 luc = (unsigned long *) ((char *) luc + UL_ALIGNMENT_WIDTH);
129                 if (ps) {
130                         lup = (unsigned long *) ((char *) lup + UL_ALIGNMENT_WIDTH);
131                 }
132         }
133
134         /* Compare unsigned int fields */
135         uc = (unsigned int *) luc;
136         up = (unsigned int *) lup;
137         for (i = 0; i < u_nr; i++, m++) {
138                 if (ps) {
139                         val = S_VALUE(*up, *uc, itv);
140                 }
141                 else {
142                         val = (double) *uc;
143                 }
144                 if (val < minv[m]) {
145                         minv[m] = val;
146                 }
147                 if (val > maxv[m]) {
148                         maxv[m] = val;
149                 }
150                 uc = (unsigned int *) ((char *) uc + U_ALIGNMENT_WIDTH);
151                 if (ps) {
152                         up = (unsigned int *) ((char *) up + U_ALIGNMENT_WIDTH);
153                 }
154         }
155 }
156
157 /*
158  ***************************************************************************
159  * Find the min and max values of all the graphs that will be drawn in the
160  * same view. The graphs have their own min and max values in
161  * minv[pos...pos+n-1] and maxv[pos...pos+n-1]. 
162  *
163  * IN:
164  * @pos         Position in array for the first graph extrema value.
165  * @n           Number of graphs to scan.
166  * @minv        Array containing min values for graphs.
167  * @maxv        Array containing max values for graphs.
168  *
169  * OUT:
170  * @gmin        Global min value found.
171  * @gmax        Global max value found.
172  ***************************************************************************
173  */
174 void get_global_extrema(int pos, int n, double minv[], double maxv[], double *gmin, double *gmax)
175 {
176         int i;
177
178         *gmin = minv[pos];
179         *gmax = maxv[pos];
180
181         for (i = 1; i < n; i++) {
182                 if (minv[pos + i] < *gmin) {
183                         *gmin = minv[pos + i];
184                 }
185                 if (maxv[pos + i] > *gmax) {
186                         *gmax = maxv[pos + i];
187                 }
188         }
189 }
190
191 /*
192  ***************************************************************************
193  * Allocate arrays used to save graphs data, min and max values.
194  * @n arrays of chars are allocated for @n graphs to draw. A pointer on this
195  * array is returned. This is equivalent to "char data[][n]" where each
196  * element is of indeterminate size and will contain the graph data (eg.
197  * << path d="M12,14 L13,16..." ... >>.
198  * The size of element data[i] is given by outsize[i].
199  * Also allocate an array to save min values (equivalent to "double spmin[n]")
200  * and an array for max values (equivalent to "double spmax[n]").
201  *
202  * IN:
203  * @n           Number of graphs to draw for current activity.
204  *
205  * OUT:
206  * @outsize     Array that will contain the sizes of each element in array
207  *              of chars. Equivalent to "int outsize[n]" with
208  *              outsize[n] = sizeof(data[][n]).
209  * @spmin       Array that will contain min values for current activity.
210  * @spmax       Array that will contain max values for current activity.
211  *
212  * RETURNS:
213  * Pointer on array of arrays of chars that will contain the graphs data.
214  *
215  * NB: @min and @max arrays contain values in the same order as the fields
216  * in the statistics structure.
217  ***************************************************************************
218  */
219 char **allocate_graph_lines(int n, int **outsize, double **spmin, double **spmax)
220 {
221         char **out;
222         char *out_p;
223         int i;
224
225         /*
226          * Allocate an array of pointers. Each of these pointers will
227          * be an array of chars.
228          */
229         if ((out = (char **) malloc(n * sizeof(char *))) == NULL) {
230                 perror("malloc");
231                 exit(4);
232         }
233         /* Allocate array that will contain the size of each array of chars */
234         if ((*outsize = (int *) malloc(n * sizeof(int))) == NULL) {
235                 perror("malloc");
236                 exit(4);
237         }
238         /* Allocate array that will contain the min value of each graph */
239         if ((*spmin = (double *) malloc(n * sizeof(double))) == NULL) {
240                 perror("malloc");
241                 exit(4);
242         }
243         /* Allocate array that will contain the max value of each graph */
244         if ((*spmax = (double *) malloc(n * sizeof(double))) == NULL) {
245                 perror("malloc");
246                 exit(4);
247         }
248         /* Allocate arrays of chars that will contain graphs data */
249         for (i = 0; i < n; i++) {
250                 if ((out_p = (char *) malloc(CHUNKSIZE * sizeof(char))) == NULL) {
251                         perror("malloc");
252                         exit(4);
253                 }
254                 *(out + i) = out_p;
255                 *out_p = '\0';                  /* Reset string so that it can be safely strncat()'d later */
256                 *(*outsize + i) = CHUNKSIZE;    /* Each array of chars has a default size of CHUNKSIZE */
257                 *(*spmin + i) = DBL_MAX;        /* Init min and max values */
258                 *(*spmax + i) = -DBL_MAX;
259         }
260
261         return out;
262 }
263
264 /*
265  ***************************************************************************
266  * Save SVG code for current graph.
267  *
268  * IN:
269  * @data        SVG code to append to current graph definition.
270  * @out         Pointer on array of chars for current graph definition.
271  * @outsize     Size of array of chars for current graph definition.
272  *
273  * OUT:
274  * @out         Pointer on array of chars for current graph definition that
275  *              has been updated with the addition of current sample data.
276  * @outsize     Array that containing the (possibly new) sizes of each
277  *              element in array of chars.
278  ***************************************************************************
279  */
280 void save_svg_data(char *data, char **out, int *outsize)
281 {
282         char *out_p;
283         int len;
284
285         out_p = *out;
286         /* Determine space left in array */
287         len = *outsize - strlen(out_p) - 1;
288         if (strlen(data) >= len) {
289                 /*
290                  * If current array of chars doesn't have enough space left
291                  * then reallocate it with CHUNKSIZE more bytes.
292                  */
293                 SREALLOC(out_p, char, *outsize + CHUNKSIZE);
294                 *out = out_p;
295                 *outsize += CHUNKSIZE;
296                 len += CHUNKSIZE;
297         }
298         strncat(out_p, data, len);
299 }
300
301 /*
302  ***************************************************************************
303  * Update line graph definition by appending current X,Y coordinates.
304  *
305  * IN:
306  * @timetag     Timestamp in seconds since the epoch for current sample
307  *              stats. Will be used as X coordinate.
308  * @value       Value of current sample metric. Will be used as Y coordinate.
309  * @out         Pointer on array of chars for current graph definition.
310  * @outsize     Size of array of chars for current graph definition.
311  * @restart     Set to TRUE if a RESTART record has been read since the last
312  *              statistics sample.
313  *
314  * OUT:
315  * @out         Pointer on array of chars for current graph definition that
316  *              has been updated with the addition of current sample data.
317  * @outsize     Array that containing the (possibly new) sizes of each
318  *              element in array of chars.
319  ***************************************************************************
320  */
321 void lnappend(unsigned long timetag, double value, char **out, int *outsize, int restart)
322 {
323         char data[128];
324
325         /* Prepare additional graph definition data */
326         snprintf(data, 128, " %c%lu,%.2f", restart ? 'M' : 'L', timetag, value);
327         data[127] = '\0';
328
329         save_svg_data(data, out, outsize);
330 }
331
332 /*
333  ***************************************************************************
334  * Update line graph definition by appending current X,Y coordinates. Use
335  * (unsigned long) integer values here.
336  *
337  * IN:
338  * @timetag     Timestamp in seconds since the epoch for current sample
339  *              stats. Will be used as X coordinate.
340  * @value       Value of current sample metric. Will be used as Y coordinate.
341  * @out         Pointer on array of chars for current graph definition.
342  * @outsize     Size of array of chars for current graph definition.
343  * @restart     Set to TRUE if a RESTART record has been read since the last
344  *              statistics sample.
345  *
346  * OUT:
347  * @out         Pointer on array of chars for current graph definition that
348  *              has been updated with the addition of current sample data.
349  * @outsize     Array that containing the (possibly new) sizes of each
350  *              element in array of chars.
351  ***************************************************************************
352  */
353 void lniappend(unsigned long timetag, unsigned long value, char **out, int *outsize,
354                int restart)
355 {
356         char data[128];
357
358         /* Prepare additional graph definition data */
359         snprintf(data, 128, " %c%lu,%lu", restart ? 'M' : 'L', timetag, value);
360         data[127] = '\0';
361
362         save_svg_data(data, out, outsize);
363 }
364
365 /*
366  ***************************************************************************
367  * Update bar graph definition by adding a new rectangle.
368  *
369  * IN:
370  * @timetag     Timestamp in seconds since the epoch for current sample
371  *              stats. Will be used as X coordinate.
372  * @value       Value of current sample metric. Will be used as rectangle
373  *              height.
374  * @offset      Offset for Y coordinate.
375  * @out         Pointer on array of chars for current graph definition.
376  * @outsize     Size of array of chars for current graph definition.
377  * @dt          Interval of time in seconds between current and previous
378  *              sample.
379  *
380  * OUT:
381  * @out         Pointer on array of chars for current graph definition that
382  *              has been updated with the addition of current sample data.
383  * @outsize     Array that containing the (possibly new) sizes of each
384  *              element in array of chars.
385  ***************************************************************************
386  */
387 void brappend(unsigned long timetag, double offset, double value, char **out, int *outsize,
388               unsigned long dt)
389 {
390         char data[128];
391
392         /* Prepare additional graph definition data */
393         if (value == 0.0)
394                 /* Dont draw a flat rectangle! */
395                 return;
396
397         snprintf(data, 128, "<rect x=\"%lu\" y=\"%.2f\" height=\"%.2f\" width=\"%lu\"/>",
398                  timetag - dt, MINIMUM(offset, 100.0), MINIMUM(value, (100.0 - offset)), dt);
399         data[127] = '\0';
400
401         save_svg_data(data, out, outsize);
402
403 }
404
405 /*
406  ***************************************************************************
407  * Update CPU graph and min/max values for each metric.
408  *
409  * IN:
410  * @timetag     Timestamp in seconds since the epoch for current sample
411  *              stats. Will be used as X coordinate.
412  * @offset      Offset for Y coordinate.
413  * @value       Value of current CPU metric. Will be used as rectangle
414  *              height.
415  * @out         Pointer on array of chars for current graph definition.
416  * @outsize     Size of array of chars for current graph definition.
417  * @dt          Interval of time in seconds between current and previous
418  *              sample.
419  * @spmin       Min value already found for this CPU metric.
420  * @spmax       Max value already found for this CPU metric.
421  *
422  * OUT:
423  * @offset      New offset value, to use to draw next rectangle
424  * @out         Pointer on array of chars for current graph definition that
425  *              has been updated with the addition of current sample data.
426  * @outsize     Array that containing the (possibly new) sizes of each
427  *              element in array of chars.
428  ***************************************************************************
429  */
430 void cpuappend(unsigned long timetag, double *offset, double value, char **out, int *outsize,
431                unsigned long dt, double *spmin, double *spmax)
432 {
433         /* Save min and max values */
434         if (value < *spmin) {
435                 *spmin = value;
436         }
437         if (value > *spmax) {
438                 *spmax = value;
439         }
440         /* Prepare additional graph definition data */
441         brappend(timetag, *offset, value, out, outsize, dt);
442
443         *offset += value;
444 }
445
446 /*
447  ***************************************************************************
448  * Update rectangular graph and min/max values.
449  *
450  * IN:
451  * @timetag     Timestamp in seconds since the epoch for current sample
452  *              stats. Will be used as X coordinate.
453  * @p_value     Metric value for previous sample
454  * @value       Metric value for current sample.
455  * @out         Pointer on array of chars for current graph definition.
456  * @outsize     Size of array of chars for current graph definition.
457  * @restart     Set to TRUE if a RESTART record has been read since the last
458  *              statistics sample.
459  * @dt          Interval of time in seconds between current and previous
460  *              sample.
461  * @spmin       Min value already found for this metric.
462  * @spmax       Max value already found for this metric.
463  *
464  * OUT:
465  * @out         Pointer on array of chars for current graph definition that
466  *              has been updated with the addition of current sample data.
467  * @outsize     Array that containing the (possibly new) sizes of each
468  *              element in array of chars.
469  * @spmin       Min value for this metric.
470  * @spmax       Max value for this metric.
471  ***************************************************************************
472  */
473 void recappend(unsigned long timetag, double p_value, double value, char **out, int *outsize,
474                int restart, unsigned long dt, double *spmin, double *spmax)
475 {
476         char data[128], data1[128], data2[128];
477
478         /* Save min and max values */
479         if (value < *spmin) {
480                 *spmin = value;
481         }
482         if (value > *spmax) {
483                 *spmax = value;
484         }
485         /* Prepare additional graph definition data */
486         if (restart) {
487                 snprintf(data1, 128, " M%lu,%.2f", timetag - dt, p_value);
488                 data1[127] = '\0';
489         }
490         if (p_value != value) {
491                 snprintf(data2, 128, " L%lu,%.2f", timetag, value);
492                 data2[127] = '\0';
493         }
494         snprintf(data, 128, "%s L%lu,%.2f%s", restart ? data1 : "", timetag, p_value,
495                  p_value != value ? data2 : "");
496         data[127] = '\0';
497
498         save_svg_data(data, out, outsize);
499 }
500
501 /*
502  ***************************************************************************
503  * Calculate 10 raised to the power of n.
504  *
505  * IN:
506  * @n   Power number to use.
507  *
508  * RETURNS:
509  * 10 raised to the power of n.
510  ***************************************************************************
511  */
512 unsigned int pwr10(int n)
513 {
514         int i;
515         unsigned int e = 1;
516
517         for (i = 0; i < n; i++) {
518                 e = e * 10;
519         }
520
521         return e;
522 }
523
524 /*
525  ***************************************************************************
526  * Calculate the value on the Y axis between two horizontal lines that will
527  * make the graph background grid.
528  *
529  * IN:
530  * @lmax        Max value reached for this graph.
531  *
532  * OUT:
533  * @dp          Number of decimal places for Y graduations.
534  *
535  * RETURNS:
536  * Value between two horizontal lines.
537  ***************************************************************************
538  */
539 double ygrid(double lmax, int *dp)
540 {
541         char val[32];
542         int l;
543         unsigned int e;
544         long n = 0;
545
546         *dp = 0;
547         if (lmax == 0) {
548                 lmax = 1;
549         }
550         n = (long) (lmax / SVG_H_GRIDNR);
551         if (!n) {
552                 *dp = 2;
553                 return (lmax / SVG_H_GRIDNR);
554         }
555         snprintf(val, 32, "%ld", n);
556         val[31] = '\0';
557         l = strlen(val);
558         if (l < 2)
559                 return n;
560         e = pwr10(l - 1);
561
562         return ((double) (((long) (n / e)) * e));
563 }
564
565 /*
566  ***************************************************************************
567  * Calculate the value on the X axis between two vertical lines that will
568  * make the graph background grid.
569  *
570  * IN:
571  * @timestart   First data timestamp (X coordinate of the first data point).
572  * @timeend     Last data timestamp (X coordinate of the last data point).
573  * @v_gridnr    Number of vertical lines to display. Its value is normally
574  *              SVG_V_GRIDNR, except when option "oneday" is used, in which
575  *              case it is set to 12.
576  *
577  * RETURNS:
578  * Value between two vertical lines.
579  ***************************************************************************
580  */
581 long int xgrid(unsigned long timestart, unsigned long timeend, int v_gridnr)
582 {
583         if ((timeend - timestart) <= v_gridnr)
584                 return 1;
585         else
586                 return ((timeend - timestart) / v_gridnr);
587 }
588
589 /*
590  ***************************************************************************
591  * Free global graphs structures.
592  *
593  * IN:
594  * @out         Pointer on array of chars for each graph definition.
595  * @outsize     Size of array of chars for each graph definition.
596  * @spmin       Array containing min values for graphs.
597  * @spmax       Array containing max values for graphs.
598  ***************************************************************************
599  */
600 void free_graphs(char **out, int *outsize, double *spmin, double *spmax)
601 {
602         if (out) {
603                 free(out);
604         }
605         if (outsize) {
606                 free(outsize);
607         }
608         if (spmin) {
609                 free(spmin);
610         }
611         if (spmax) {
612                 free(spmax);
613         }
614 }
615
616 /*
617  ***************************************************************************
618  * Display all graphs for current activity.
619  *
620  * IN:
621  * @g_nr        Number of sets of graphs (views) to display.
622  * @g_type      Type of graph (SVG_LINE_GRAPH, SVG_BAR_GRAPH).
623  * @title       Titles for each set of graphs.
624  * @g_title     Titles for each graph.
625  * @item_name   Item (network interface, etc.) name.
626  * @group       Indicate how graphs are grouped together to make sets.
627  * @spmin       Array containing min values for graphs.
628  * @spmax       Array containing max values for graphs.
629  * @out         Pointer on array of chars for each graph definition.
630  * @outsize     Size of array of chars for each graph definition.
631  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
632  *              time for the first sample of stats (.@ust_time_first), and
633  *              times used as start and end values on the X axis
634  *              (.@ust_time_ref and .@ust_time_end).
635  * @record_hdr  Pointer on record header of current stats sample.
636  ***************************************************************************
637  */
638 void draw_activity_graphs(int g_nr, int g_type, char *title[], char *g_title[], char *item_name,
639                           int group[], double *spmin, double *spmax, char **out, int *outsize,
640                           struct svg_parm *svg_p, struct record_header *record_hdr)
641 {
642         struct record_header stamp;
643         struct tm rectime;
644         char *out_p;
645         int i, j, dp, pos = 0, views_nr = 0;
646         int v_gridnr;
647         unsigned int asfactor[16];
648         long int k;
649         double lmax, xfactor, yfactor, ypos, gmin, gmax;
650         char cur_time[32], val[32];
651
652         /* Translate to proper position for current activity */
653         printf("<g id=\"g%d\" transform=\"translate(0,%d)\">\n",
654                svg_p->graph_no,
655                SVG_H_YSIZE + svg_p->graph_no * SVG_T_YSIZE);
656
657         /* For each set of graphs which are part of current activity */
658         for (i = 0; i < g_nr; i++) {
659
660                 /* Get global min and max value for current set of graphs */
661                 get_global_extrema(pos, group[i], spmin, spmax, &gmin, &gmax);
662
663                 /* Don't display empty views if requested */
664                 if (SKIP_EMPTY_VIEWS(flags) && (gmax < 0.005))
665                         continue;
666                 /* Increment number of views actually displayed */
667                 views_nr++;
668
669                 /* Graph background */
670                 printf("<rect x=\"0\" y=\"%d\" height=\"%d\" width=\"%d\"/>\n",
671                        i * SVG_T_YSIZE,
672                        SVG_V_YSIZE, SVG_V_XSIZE);
673
674                 /* Graph title */
675                 printf("<text x=\"0\" y=\"%d\" style=\"fill: yellow; stroke: none\">%s",
676                        20 + i * SVG_T_YSIZE, title[i]);
677                 if (item_name) {
678                         printf(" [%s]", item_name);
679                 }
680                 printf("\n");
681                 printf("<tspan x=\"%d\" y=\"%d\" style=\"fill: yellow; stroke: none; font-size: 12px\">"
682                        "(Min, Max values)</tspan>\n</text>\n",
683                        5 + SVG_M_XSIZE + SVG_G_XSIZE,
684                        25 + i * SVG_T_YSIZE);
685
686                 /*
687                  * At least two samples are needed.
688                  * And a min and max value should have been found.
689                  */
690                 if ((record_hdr->ust_time == svg_p->ust_time_first) ||
691                     (*(spmin + pos) == DBL_MAX) || (*(spmax + pos) == -DBL_MIN)) {
692                         /* No data found */
693                         printf("<text x=\"0\" y=\"%d\" style=\"fill: red; stroke: none\">No data</text>\n",
694                                SVG_M_YSIZE + i * SVG_T_YSIZE);
695                         continue;
696                 }
697
698                 /* X and Y axis */
699                 printf("<polyline points=\"%d,%d %d,%d %d,%d\" stroke=\"white\" stroke-width=\"2\"/>\n",
700                        SVG_M_XSIZE, SVG_M_YSIZE + i * SVG_T_YSIZE,
701                        SVG_M_XSIZE, SVG_M_YSIZE + SVG_G_YSIZE + i * SVG_T_YSIZE,
702                        SVG_M_XSIZE + SVG_G_XSIZE, SVG_M_YSIZE + SVG_G_YSIZE + i * SVG_T_YSIZE);
703
704                 for (j = 0; j < 16; j++) {
705                         /* Init autoscale factors */
706                         asfactor[j] = 1;
707                 }
708
709                 if (AUTOSCALE_ON(flags) && (group[i] > 1) && gmax && (g_type == SVG_LINE_GRAPH)) {
710                         /* Autoscaling... */
711                         for (j = 0; (j < group[i]) && (j < 16); j++) {
712                                 if (!*(spmax + pos + j) || (*(spmax + pos + j) == gmax))
713                                         continue;
714
715                                 snprintf(val, 32, "%u", (unsigned int) (gmax / *(spmax + pos + j)));
716                                 if (strlen(val) > 0) {
717                                         asfactor[j] = pwr10(strlen(val) - 1);
718                                 }
719                         }
720                 }
721
722                 /* Caption */
723                 for (j = 0; j < group[i]; j++) {
724                         /* Set dp to TRUE (1) if current metric is based on integer values */
725                         dp = (g_title[pos + j][0] == '~');
726                         snprintf(val, 32, "x%u ", asfactor[j]);
727                         printf("<text x=\"%d\" y=\"%d\" style=\"fill: #%06x; stroke: none; font-size: 12px\">"
728                                "%s %s(%.*f, %.*f)</text>\n",
729                                5 + SVG_M_XSIZE + SVG_G_XSIZE, SVG_M_YSIZE + i * SVG_T_YSIZE + j * 15,
730                                svg_colors[(pos + j) & SVG_COLORS_IDX_MASK], g_title[pos + j] + dp,
731                                asfactor[j] == 1 ? "" : val,
732                                !dp * 2, *(spmin + pos + j) * asfactor[j],
733                                !dp * 2, *(spmax + pos + j) * asfactor[j]);
734                 }
735
736                 /* Translate to proper position for current graph within current activity */
737                 printf("<g transform=\"translate(%d,%d)\">\n",
738                        SVG_M_XSIZE, SVG_M_YSIZE + SVG_G_YSIZE + i * SVG_T_YSIZE);
739
740                 /* Grid */
741                 if (g_type == SVG_LINE_GRAPH) {
742                         /* For line graphs */
743                         if (!gmax) {
744                                 /* If all values are zero then set current max value to 1 */
745                                 lmax = 1.0;
746                         }
747                         else {
748                                 lmax = gmax;
749                         }
750                         /* Max value cannot be too small, else Y graduations will be meaningless */
751                         if (lmax < SVG_H_GRIDNR * 0.01) {
752                                 lmax = SVG_H_GRIDNR * 0.01;
753                         }
754                         ypos = ygrid(lmax, &dp);
755                 }
756                 else {
757                         /* For bar graphs (used for %values) */
758                         ypos = 25.0;    /* Draw lines at 25%, 50%, 75% and 100% */
759                         dp = 0;         /* No decimals */
760
761                         /* Max should be always 100% except for percentage values greater than 100% */
762                         if (gmax > 100.0) {
763                                 lmax = gmax;
764                         }
765                         else {
766                                 lmax = 100.0;
767                         }
768                 }
769                 yfactor = (double) -SVG_G_YSIZE / lmax;
770                 j = 1;
771                 do {
772                         printf("<polyline points=\"0,%.2f %d,%.2f\" style=\"vector-effect: non-scaling-stroke; "
773                                "stroke: #202020\" transform=\"scale(1,%f)\"/>\n",
774                                ypos * j, SVG_G_XSIZE, ypos * j, yfactor);
775                         j++;
776                 }
777                 while (ypos * j <= lmax);
778                 j = 0;
779                 do {
780                         printf("<text x=\"0\" y=\"%ld\" style=\"fill: white; stroke: none; font-size: 12px; "
781                                "text-anchor: end\">%.*f.</text>\n",
782                                (long) (ypos * j * yfactor), dp, ypos * j);
783                         j++;
784                 }
785                 while (ypos * j <= lmax);
786
787                 /* Set number of vertical lines to 12 when option "oneday" is used */
788                 v_gridnr = DISPLAY_ONE_DAY(flags) ? 12 : SVG_V_GRIDNR;
789
790                 k = xgrid(svg_p->ust_time_ref, svg_p->ust_time_end, v_gridnr);
791                 xfactor = (double) SVG_G_XSIZE / (svg_p->ust_time_end - svg_p->ust_time_ref);
792                 stamp.ust_time = svg_p->ust_time_ref; /* Only ust_time field needs to be set. TRUE_TIME not allowed */
793
794                 for (j = 0; (j <= v_gridnr) && (stamp.ust_time <= svg_p->ust_time_end); j++) {
795                         sa_get_record_timestamp_struct(flags, &stamp, &rectime, NULL);
796                         set_record_timestamp_string(flags, &stamp, NULL, cur_time, 32, &rectime);
797                         printf("<polyline points=\"%ld,0 %ld,%d\" style=\"vector-effect: non-scaling-stroke; "
798                                "stroke: #202020\" transform=\"scale(%f,1)\"/>\n",
799                                k * j, k * j, -SVG_G_YSIZE, xfactor);
800                         /*
801                          * NB: We may have tm_min != 0 if we have more than 24H worth of data in one datafile.
802                          * In this case, we should rather display the exact time instead of only the hour.
803                          */
804                         if (DISPLAY_ONE_DAY(flags) && (rectime.tm_min == 0)) {
805                                 printf("<text x=\"%ld\" y=\"15\" style=\"fill: white; stroke: none; font-size: 14px; "
806                                        "text-anchor: start\">%2dH</text>\n",
807                                        (long) (k * j * xfactor) - 8, rectime.tm_hour);
808                         }
809                         else {
810                                 printf("<text x=\"%ld\" y=\"10\" style=\"fill: white; stroke: none; font-size: 12px; "
811                                        "text-anchor: start\" transform=\"rotate(45,%ld,0)\">%s</text>\n",
812                                        (long) (k * j * xfactor), (long) (k * j * xfactor), cur_time);
813                         }
814                         stamp.ust_time += k;
815                 }
816                 if (!PRINT_LOCAL_TIME(flags)) {
817                         printf("<text x=\"-10\" y=\"30\" style=\"fill: yellow; stroke: none; font-size: 12px; "
818                                "text-anchor: end\">UTC</text>\n");
819                 }
820
821                 /* Draw current graphs set */
822                 for (j = 0; j < group[i]; j++) {
823                         out_p = *(out + pos + j);
824                         if (g_type == SVG_LINE_GRAPH) {
825                                 /* Line graphs */
826                                 printf("<path id=\"g%dp%d\" d=\"%s\" "
827                                        "style=\"vector-effect: non-scaling-stroke; "
828                                        "stroke: #%06x; stroke-width: 1; fill-opacity: 0\" "
829                                        "transform=\"scale(%f,%f)\"/>\n",
830                                        svg_p->graph_no, pos + j, out_p,
831                                        svg_colors[(pos + j) & SVG_COLORS_IDX_MASK],
832                                        xfactor,
833                                        yfactor * asfactor[j]);
834                         }
835                         else if (*out_p) {      /* Ignore flat bars */
836                                 /* Bar graphs */
837                                 printf("<g style=\"fill: #%06x; stroke: none\" transform=\"scale(%f,%f)\">\n",
838                                        svg_colors[(pos + j) & SVG_COLORS_IDX_MASK], xfactor, yfactor);
839                                 printf("%s\n", out_p);
840                                 printf("</g>\n");
841                         }
842                         free(out_p);
843                 }
844                 printf("</g>\n");
845                 pos += group[i];
846         }
847         printf("</g>\n");
848
849         /* Next graph */
850         (svg_p->graph_no) += views_nr;
851 }
852
853 /*
854  ***************************************************************************
855  * Display CPU statistics in SVG.
856  *
857  * IN:
858  * @a           Activity structure with statistics.
859  * @curr        Index in array for current sample statistics.
860  * @action      Action expected from current function.
861  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
862  *              flag indicating that a restart record has been previously
863  *              found (.@restart), and time used for the X axis origin
864  *              (@ust_time_ref).
865  * @itv         Interval of time in jiffies (only with F_MAIN action).
866  * @record_hdr  Pointer on record header of current stats sample.
867  ***************************************************************************
868  */
869 __print_funct_t svg_print_cpu_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
870                                     unsigned long long g_itv, struct record_header *record_hdr)
871 {
872         struct stats_cpu *scc, *scp;
873         int group1[] = {5};
874         int group2[] = {9};
875         char *title[] = {"CPU load"};
876         char *g_title1[] = {"%user", "%nice", "%system", "%iowait", "%steal", "%idle"};
877         char *g_title2[] = {"%usr", "%nice", "%sys", "%iowait", "%steal", "%irq", "%soft", "%guest", "%gnice", "%idle"};
878         static double *spmin, *spmax;
879         static char **out;
880         static int *outsize;
881         char item_name[8];
882         double offset, val;
883         int i, j, k, pos, cpu_offline;
884
885         if (action & F_BEGIN) {
886                 /*
887                  * Allocate arrays that will contain the graphs data
888                  * and the min/max values.
889                  */
890                 out = allocate_graph_lines(10 * a->nr, &outsize, &spmin, &spmax);
891         }
892
893         if (action & F_MAIN) {
894                 /* For each CPU */
895                 for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {
896
897                         scc = (struct stats_cpu *) ((char *) a->buf[curr]  + i * a->msize);
898                         scp = (struct stats_cpu *) ((char *) a->buf[!curr] + i * a->msize);
899
900                         /* Should current CPU (including CPU "all") be displayed? */
901                         if (!(a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))))
902                                 /* No */
903                                 continue;
904
905                         pos = i * 10;
906                         offset = 0.0;
907
908                         if (i) {        /* Don't test CPU "all" here */
909                                 /*
910                                  * If the CPU is offline then it is omited from /proc/stat:
911                                  * All the fields couldn't have been read and the sum of them is zero.
912                                  * (Remember that guest/guest_nice times are already included in
913                                  * user/nice modes.)
914                                  */
915                                 if ((scc->cpu_user    + scc->cpu_nice + scc->cpu_sys   +
916                                      scc->cpu_iowait  + scc->cpu_idle + scc->cpu_steal +
917                                      scc->cpu_hardirq + scc->cpu_softirq) == 0) {
918                                         /*
919                                          * Set current struct fields (which have been set to zero)
920                                          * to values from previous iteration. Hence their values won't
921                                          * jump from zero when the CPU comes back online.
922                                          */
923                                         *scc = *scp;
924
925                                         g_itv = 0;
926                                         cpu_offline = TRUE;
927                                 }
928                                 else {
929                                         /*
930                                          * Recalculate interval for current proc.
931                                          * If result is 0 then current CPU is a tickless one.
932                                          */
933                                         g_itv = get_per_cpu_interval(scc, scp);
934                                         cpu_offline = FALSE;
935                                 }
936
937                                 if (!g_itv) {   /* Current CPU is offline or tickless */
938
939                                         val = (cpu_offline ? 0.0        /* Offline CPU: %idle = 0% */
940                                                            : 100.0);    /* Tickless CPU: %idle = 100% */
941
942                                         if (DISPLAY_CPU_DEF(a->opt_flags)) {
943                                                 j  = 5; /* -u */
944                                         }
945                                         else {  /* DISPLAY_CPU_ALL(a->opt_flags) */
946                                                 j = 9;  /* -u ALL */
947                                         }
948
949                                         /* Check min/max values for %user, etc. */
950                                         for (k = 0; k < j; k++) {
951                                                 if (0.0 < *(spmin + pos + k)) {
952                                                         *(spmin + pos + k) = 0.0;
953                                                 }
954                                                 if (0.0 > *(spmax + pos + k)) {
955                                                         *(spmax + pos + k) = 0.0;
956                                                 }
957                                         }
958
959                                         /* %idle */
960                                         cpuappend(record_hdr->ust_time - svg_p->ust_time_ref,
961                                                   &offset, val,
962                                                   out + pos + j, outsize + pos + j, svg_p->dt,
963                                                   spmin + pos + j, spmax + pos + j);
964                                         continue;
965                                 }
966                         }
967
968                         if (DISPLAY_CPU_DEF(a->opt_flags)) {
969                                 /* %user */
970                                 cpuappend(record_hdr->ust_time - svg_p->ust_time_ref,
971                                           &offset, ll_sp_value(scp->cpu_user, scc->cpu_user, g_itv),
972                                           out + pos, outsize + pos, svg_p->dt,
973                                           spmin + pos, spmax + pos);
974                         }
975                         else {
976                                 /* %usr */
977                                 cpuappend(record_hdr->ust_time - svg_p->ust_time_ref,
978                                           &offset,
979                                           (scc->cpu_user - scc->cpu_guest) < (scp->cpu_user - scp->cpu_guest) ?
980                                            0.0 :
981                                            ll_sp_value(scp->cpu_user - scp->cpu_guest,
982                                                        scc->cpu_user - scc->cpu_guest, g_itv),
983                                           out + pos, outsize + pos, svg_p->dt,
984                                           spmin + pos, spmax + pos);
985                         }
986
987                         if (DISPLAY_CPU_DEF(a->opt_flags)) {
988                                 /* %nice */
989                                 cpuappend(record_hdr->ust_time - svg_p->ust_time_ref,
990                                           &offset, ll_sp_value(scp->cpu_nice, scc->cpu_nice, g_itv),
991                                           out + pos + 1, outsize + pos + 1, svg_p->dt,
992                                           spmin + pos + 1, spmax + pos + 1);
993                         }
994                         else {
995                                 /* %nice */
996                                 cpuappend(record_hdr->ust_time - svg_p->ust_time_ref,
997                                           &offset,
998                                           (scc->cpu_nice - scc->cpu_guest_nice) < (scp->cpu_nice - scp->cpu_guest_nice) ?
999                                            0.0 :
1000                                            ll_sp_value(scp->cpu_nice - scp->cpu_guest_nice,
1001                                                        scc->cpu_nice - scc->cpu_guest_nice, g_itv),
1002                                           out + pos + 1, outsize + pos + 1, svg_p->dt,
1003                                           spmin + pos + 1, spmax + pos + 1);
1004                         }
1005
1006                         if (DISPLAY_CPU_DEF(a->opt_flags)) {
1007                                 /* %system */
1008                                 cpuappend(record_hdr->ust_time - svg_p->ust_time_ref,
1009                                           &offset,
1010                                           ll_sp_value(scp->cpu_sys + scp->cpu_hardirq + scp->cpu_softirq,
1011                                                       scc->cpu_sys + scc->cpu_hardirq + scc->cpu_softirq,
1012                                                       g_itv),
1013                                           out + pos + 2, outsize + pos + 2, svg_p->dt,
1014                                           spmin + pos + 2, spmax + pos + 2);
1015                         }
1016                         else {
1017                                 /* %sys */
1018                                 cpuappend(record_hdr->ust_time - svg_p->ust_time_ref,
1019                                           &offset, ll_sp_value(scp->cpu_sys, scc->cpu_sys, g_itv),
1020                                           out + pos + 2, outsize + pos + 2, svg_p->dt,
1021                                           spmin + pos + 2, spmax + pos + 2);
1022                         }
1023
1024                         /* %iowait */
1025                         cpuappend(record_hdr->ust_time - svg_p->ust_time_ref,
1026                                   &offset, ll_sp_value(scp->cpu_iowait, scc->cpu_iowait, g_itv),
1027                                   out + pos + 3, outsize + pos + 3, svg_p->dt,
1028                                   spmin + pos + 3, spmax + pos + 3);
1029
1030                         /* %steal */
1031                         cpuappend(record_hdr->ust_time - svg_p->ust_time_ref,
1032                                   &offset, ll_sp_value(scp->cpu_steal, scc->cpu_steal, g_itv),
1033                                   out + pos + 4, outsize + pos + 4, svg_p->dt,
1034                                   spmin + pos + 4, spmax + pos + 4);
1035
1036                         if (DISPLAY_CPU_ALL(a->opt_flags)) {
1037                                 /* %irq */
1038                                 cpuappend(record_hdr->ust_time - svg_p->ust_time_ref,
1039                                           &offset, ll_sp_value(scp->cpu_hardirq, scc->cpu_hardirq, g_itv),
1040                                           out + pos + 5, outsize + pos + 5, svg_p->dt,
1041                                           spmin + pos + 5, spmax + pos + 5);
1042
1043                                 /* %soft */
1044                                 cpuappend(record_hdr->ust_time - svg_p->ust_time_ref,
1045                                           &offset, ll_sp_value(scp->cpu_softirq, scc->cpu_softirq, g_itv),
1046                                           out + pos + 6, outsize + pos + 6, svg_p->dt,
1047                                           spmin + pos + 6, spmax + pos + 6);
1048
1049                                 /* %guest */
1050                                 cpuappend(record_hdr->ust_time - svg_p->ust_time_ref,
1051                                           &offset, ll_sp_value(scp->cpu_guest, scc->cpu_guest, g_itv),
1052                                           out + pos + 7, outsize + pos + 7, svg_p->dt,
1053                                           spmin + pos + 7, spmax + pos + 7);
1054
1055                                 /* %gnice */
1056                                 cpuappend(record_hdr->ust_time - svg_p->ust_time_ref,
1057                                           &offset, ll_sp_value(scp->cpu_guest_nice, scc->cpu_guest_nice, g_itv),
1058                                           out + pos + 8, outsize + pos + 8, svg_p->dt,
1059                                           spmin + pos + 8, spmax + pos + 8);
1060
1061                                 j = 9;
1062                         }
1063                         else {
1064                                 j = 5;
1065                         }
1066
1067                         /* %idle */
1068                         cpuappend(record_hdr->ust_time - svg_p->ust_time_ref,
1069                                   &offset,
1070                                   (scc->cpu_idle < scp->cpu_idle ? 0.0 :
1071                                    ll_sp_value(scp->cpu_idle, scc->cpu_idle, g_itv)),
1072                                   out + pos + j, outsize + pos + j, svg_p->dt,
1073                                   spmin + pos + j, spmax + pos + j);
1074                 }
1075         }
1076
1077         if (action & F_END) {
1078                 for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {
1079
1080                         /* Should current CPU (including CPU "all") be displayed? */
1081                         if (!(a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))))
1082                                 /* No */
1083                                 continue;
1084
1085                         pos = i * 10;
1086                         if (!i) {
1087                                 /* This is CPU "all" */
1088                                 strcpy(item_name, "all");
1089                         }
1090                         else {
1091                                 sprintf(item_name, "%d", i - 1);
1092                         }
1093
1094                         if (DISPLAY_CPU_DEF(a->opt_flags)) {
1095                                 draw_activity_graphs(a->g_nr, SVG_BAR_GRAPH,
1096                                                      title, g_title1, item_name, group1,
1097                                                      spmin + pos, spmax + pos, out + pos, outsize + pos,
1098                                                      svg_p, record_hdr);
1099                         }
1100                         else {
1101                                 draw_activity_graphs(a->g_nr, SVG_BAR_GRAPH,
1102                                                      title, g_title2, item_name, group2,
1103                                                      spmin + pos, spmax + pos, out + pos, outsize + pos,
1104                                                      svg_p, record_hdr);
1105                         }
1106                 }
1107
1108                 /* Free remaining structures */
1109                 free_graphs(out, outsize, spmin, spmax);
1110         }
1111 }
1112
1113 /*
1114  ***************************************************************************
1115  * Display task creation and context switch statistics in SVG.
1116  *
1117  * IN:
1118  * @a           Activity structure with statistics.
1119  * @curr        Index in array for current sample statistics.
1120  * @action      Action expected from current function.
1121  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1122  *              flag indicating that a restart record has been previously
1123  *              found (.@restart) and time used for the X axis origin
1124  *              (@ust_time_ref).
1125  * @itv         Interval of time in jiffies (only with F_MAIN action).
1126  * @record_hdr  Pointer on record header of current stats sample.
1127  ***************************************************************************
1128  */
1129 __print_funct_t svg_print_pcsw_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1130                                      unsigned long long itv, struct record_header *record_hdr)
1131 {
1132         struct stats_pcsw
1133                 *spc = (struct stats_pcsw *) a->buf[curr],
1134                 *spp = (struct stats_pcsw *) a->buf[!curr];
1135         int group[] = {1, 1};
1136         char *title[] = {"Switching activity", "Task creation"};
1137         char *g_title[] = {"cswch/s",
1138                            "proc/s"};
1139         static double *spmin, *spmax;
1140         static char **out;
1141         static int *outsize;
1142
1143         if (action & F_BEGIN) {
1144                 /*
1145                  * Allocate arrays that will contain the graphs data
1146                  * and the min/max values.
1147                  */
1148                 out = allocate_graph_lines(2, &outsize, &spmin, &spmax);
1149         }
1150
1151         if (action & F_MAIN) {
1152                 /* Check for min/max values */
1153                 save_extrema(1, 1, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
1154                              itv, spmin, spmax);
1155                 /* cswch/s */
1156                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1157                          S_VALUE(spp->context_switch, spc->context_switch, itv),
1158                          out, outsize, svg_p->restart);
1159                 /* proc/s */
1160                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1161                          S_VALUE(spp->processes, spc->processes, itv),
1162                          out + 1, outsize + 1, svg_p->restart);
1163         }
1164
1165         if (action & F_END) {
1166                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1167                                      spmin, spmax, out, outsize, svg_p, record_hdr);
1168
1169                 /* Free remaining structures */
1170                 free_graphs(out, outsize, spmin, spmax);
1171         }
1172 }
1173
1174 /*
1175  ***************************************************************************
1176  * Display swap statistics in SVG.
1177  *
1178  * IN:
1179  * @a           Activity structure with statistics.
1180  * @curr        Index in array for current sample statistics.
1181  * @action      Action expected from current function.
1182  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1183  *              flag indicating that a restart record has been previously
1184  *              found (.@restart) and time used for the X axis origin
1185  *              (@ust_time_ref).
1186  * @itv         Interval of time in jiffies (only with F_MAIN action).
1187  * @record_hdr  Pointer on record header of current stats sample.
1188  ***************************************************************************
1189  */
1190 __print_funct_t svg_print_swap_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1191                                      unsigned long long itv, struct record_header *record_hdr)
1192 {
1193         struct stats_swap
1194                 *ssc = (struct stats_swap *) a->buf[curr],
1195                 *ssp = (struct stats_swap *) a->buf[!curr];
1196         int group[] = {2};
1197         char *title[] = {"Swap activity"};
1198         char *g_title[] = {"pswpin/s", "pswpout/s" };
1199         static double *spmin, *spmax;
1200         static char **out;
1201         static int *outsize;
1202
1203         if (action & F_BEGIN) {
1204                 /*
1205                  * Allocate arrays that will contain the graphs data
1206                  * and the min/max values.
1207                  */
1208                 out = allocate_graph_lines(2, &outsize, &spmin, &spmax);
1209         }
1210
1211         if (action & F_MAIN) {
1212                 /* Check for min/max values */
1213                 save_extrema(0, 2, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
1214                              itv, spmin, spmax);
1215                 /* pswpin/s */
1216                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1217                          S_VALUE(ssp->pswpin, ssc->pswpin, itv),
1218                          out, outsize, svg_p->restart);
1219                 /* pswpout/s */
1220                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1221                          S_VALUE(ssp->pswpout, ssc->pswpout, itv),
1222                          out + 1, outsize + 1, svg_p->restart);
1223         }
1224
1225         if (action & F_END) {
1226                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1227                                      spmin, spmax, out, outsize, svg_p, record_hdr);
1228
1229                 /* Free remaining structures */
1230                 free_graphs(out, outsize, spmin, spmax);
1231         }
1232 }
1233
1234 /*
1235  ***************************************************************************
1236  * Display paging statistics in SVG.
1237  *
1238  * IN:
1239  * @a           Activity structure with statistics.
1240  * @curr        Index in array for current sample statistics.
1241  * @action      Action expected from current function.
1242  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1243  *              flag indicating that a restart record has been previously
1244  *              found (.@restart) and time used for the X axis origin
1245  *              (@ust_time_ref).
1246  * @itv         Interval of time in jiffies (only with F_MAIN action).
1247  * @record_hdr  Pointer on record header of current stats sample.
1248  ***************************************************************************
1249  */
1250 __print_funct_t svg_print_paging_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1251                                        unsigned long long itv, struct record_header *record_hdr)
1252 {
1253         struct stats_paging
1254                 *spc = (struct stats_paging *) a->buf[curr],
1255                 *spp = (struct stats_paging *) a->buf[!curr];
1256         int group[] = {2, 2, 4};
1257         char *title[] = {"Paging activity (1)", "Paging activity (2)", "Paging activity (3)"};
1258         char *g_title[] = {"pgpgin/s", "pgpgout/s",
1259                            "fault/s", "majflt/s",
1260                            "pgfree/s", "pgscank/s", "pgscand/s", "pgsteal/s"};
1261         static double *spmin, *spmax;
1262         static char **out;
1263         static int *outsize;
1264
1265         if (action & F_BEGIN) {
1266                 /*
1267                  * Allocate arrays that will contain the graphs data
1268                  * and the min/max values.
1269                  */
1270                 out = allocate_graph_lines(8, &outsize, &spmin, &spmax);
1271         }
1272
1273         if (action & F_MAIN) {
1274                 /* Check for min/max values */
1275                 save_extrema(0, 8, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
1276                              itv, spmin, spmax);
1277                 /* pgpgin/s */
1278                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1279                          S_VALUE(spp->pgpgin, spc->pgpgin, itv),
1280                          out, outsize, svg_p->restart);
1281                 /* pgpgout/s */
1282                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1283                          S_VALUE(spp->pgpgout, spc->pgpgout, itv),
1284                          out + 1, outsize + 1, svg_p->restart);
1285                 /* fault/s */
1286                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1287                          S_VALUE(spp->pgfault, spc->pgfault, itv),
1288                          out + 2, outsize + 2, svg_p->restart);
1289                 /* majflt/s */
1290                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1291                          S_VALUE(spp->pgmajfault, spc->pgmajfault, itv),
1292                          out + 3, outsize + 3, svg_p->restart);
1293                 /* pgfree/s */
1294                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1295                          S_VALUE(spp->pgfree, spc->pgfree, itv),
1296                          out + 4, outsize + 4, svg_p->restart);
1297                 /* pgscank/s */
1298                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1299                          S_VALUE(spp->pgscan_kswapd, spc->pgscan_kswapd, itv),
1300                          out + 5, outsize + 5, svg_p->restart);
1301                 /* pgscand/s */
1302                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1303                          S_VALUE(spp->pgscan_direct, spc->pgscan_direct, itv),
1304                          out + 6, outsize + 6, svg_p->restart);
1305                 /* pgsteal/s */
1306                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1307                          S_VALUE(spp->pgsteal, spc->pgsteal, itv),
1308                          out + 7, outsize + 7, svg_p->restart);
1309         }
1310
1311         if (action & F_END) {
1312                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1313                                      spmin, spmax, out, outsize, svg_p, record_hdr);
1314
1315                 /* Free remaining structures */
1316                 free_graphs(out, outsize, spmin, spmax);
1317         }
1318 }
1319
1320 /*
1321  ***************************************************************************
1322  * Display I/O and transfer rate statistics in SVG.
1323  *
1324  * IN:
1325  * @a           Activity structure with statistics.
1326  * @curr        Index in array for current sample statistics.
1327  * @action      Action expected from current function.
1328  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1329  *              flag indicating that a restart record has been previously
1330  *              found (.@restart) and time used for the X axis origin
1331  *              (@ust_time_ref).
1332  * @itv         Interval of time in jiffies (only with F_MAIN action).
1333  * @record_hdr  Pointer on record header of current stats sample.
1334  ***************************************************************************
1335  */
1336 __print_funct_t svg_print_io_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1337                                    unsigned long long itv, struct record_header *record_hdr)
1338 {
1339         struct stats_io
1340                 *sic = (struct stats_io *) a->buf[curr],
1341                 *sip = (struct stats_io *) a->buf[!curr];
1342         int group[] = {3, 2};
1343         char *title[] = {"I/O and transfer rate statistics (1)", "I/O and transfer rate statistics (2)"};
1344         char *g_title[] = {"tps", "rtps", "wtps",
1345                            "bread/s", "bwrtn/s"};
1346         static double *spmin, *spmax;
1347         static char **out;
1348         static int *outsize;
1349
1350         if (action & F_BEGIN) {
1351                 /*
1352                  * Allocate arrays that will contain the graphs data
1353                  * and the min/max values.
1354                  */
1355                 out = allocate_graph_lines(5, &outsize, &spmin, &spmax);
1356         }
1357
1358         if (action & F_MAIN) {
1359                 /* Check for min/max values */
1360                 save_extrema(0, 5, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
1361                              itv, spmin, spmax);
1362
1363                 /* tps */
1364                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1365                          S_VALUE(sip->dk_drive, sic->dk_drive, itv),
1366                          out, outsize, svg_p->restart);
1367                 /* rtps */
1368                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1369                          S_VALUE(sip->dk_drive_rio,  sic->dk_drive_rio, itv),
1370                          out + 1, outsize + 1, svg_p->restart);
1371                 /* wtps */
1372                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1373                          S_VALUE(sip->dk_drive_wio,  sic->dk_drive_wio, itv),
1374                          out + 2, outsize + 2, svg_p->restart);
1375                 /* bread/s */
1376                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1377                          S_VALUE(sip->dk_drive_rblk, sic->dk_drive_rblk, itv),
1378                          out + 3, outsize + 3, svg_p->restart);
1379                 /* bwrtn/s */
1380                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1381                          S_VALUE(sip->dk_drive_wblk, sic->dk_drive_wblk, itv),
1382                          out + 4, outsize + 4, svg_p->restart);
1383         }
1384
1385         if (action & F_END) {
1386                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1387                                      spmin, spmax, out, outsize, svg_p, record_hdr);
1388
1389                 /* Free remaining structures */
1390                 free_graphs(out, outsize, spmin, spmax);
1391         }
1392 }
1393
1394 /*
1395  ***************************************************************************
1396  * Display memory statistics in SVG.
1397  *
1398  * IN:
1399  * @a           Activity structure with statistics.
1400  * @curr        Index in array for current sample statistics.
1401  * @action      Action expected from current function.
1402  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1403  *              flag indicating that a restart record has been previously
1404  *              found (.@restart) and time used for the X axis origin
1405  *              (@ust_time_ref).
1406  * @itv         Interval of time in jiffies (only with F_MAIN action).
1407  * @record_hdr  Pointer on record header of current stats sample.
1408  ***************************************************************************
1409  */
1410 __print_funct_t svg_print_memory_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1411                                        unsigned long long itv, struct record_header *record_hdr)
1412 {
1413         struct stats_memory
1414                 *smc = (struct stats_memory *) a->buf[curr];
1415         int group1a[] = {2, 2};
1416         int group1b[] = {1, 1};
1417         int group1c[] = {4, 5};
1418         int group2a[] = {3};
1419         int group2b[] = {1, 1};
1420         char *title1a[] = {"Memory utilization (1)", "Memory utilization (2)"};
1421         char *title1b[] = {"Memory utilization (3)", "Memory utilization (4)"};
1422         char *title1c[] = {"Memory utilization (5)", "Memory utilization (6)"};
1423         char *title2a[] = {"Swap utilization (1)"};
1424         char *title2b[] = {"Swap utilization (2)", "Swap utilization (3)"};
1425         char *g_title1a[] = {"MBmemfree", "MBmemused",
1426                              "MBcached", "MBbuffers"};
1427         char *g_title1b[] = {"%memused", "%commit"};
1428         char *g_title1c[] = {"MBcommit", "MBactive", "MBinact", "MBdirty",
1429                              "MBanonpg", "MBslab", "MBkstack", "MBpgtbl", "MBvmused"};
1430         char *g_title2a[] = {"MBswpfree", "MBswpused", "MBswpcad"};
1431         char *g_title2b[] = {"%swpused", "%swpcad"};
1432         static double *spmin, *spmax;
1433         static char **out;
1434         static int *outsize;
1435         double tval;
1436         int i;
1437
1438         if (action & F_BEGIN) {
1439                 /*
1440                  * Allocate arrays that will contain the graphs data
1441                  * and the min/max values.
1442                  */
1443                 out = allocate_graph_lines(22, &outsize, &spmin, &spmax);
1444         }
1445
1446         if (action & F_MAIN) {
1447                 /* Check for min/max values */
1448                 save_extrema(0, 16, 0, (void *) a->buf[curr], NULL,
1449                              0, spmin, spmax);
1450                 /* Compute %memused min/max values */
1451                 tval = smc->tlmkb ? SP_VALUE(smc->frmkb, smc->tlmkb, smc->tlmkb) : 0.0;
1452                 if (tval > *(spmax + 16)) {
1453                         *(spmax + 16) = tval;
1454                 }
1455                 if (tval < *(spmin + 16)) {
1456                         *(spmin + 16) = tval;
1457                 }
1458                 /* Compute %commit min/max values */
1459                 tval = (smc->tlmkb + smc->tlskb) ?
1460                        SP_VALUE(0, smc->comkb, smc->tlmkb + smc->tlskb) : 0.0;
1461                 if (tval > *(spmax + 17)) {
1462                         *(spmax + 17) = tval;
1463                 }
1464                 if (tval < *(spmin + 17)) {
1465                         *(spmin + 17) = tval;
1466                 }
1467                 /* Compute %swpused min/max values */
1468                 tval = smc->tlskb ?
1469                        SP_VALUE(smc->frskb, smc->tlskb, smc->tlskb) : 0.0;
1470                 if (tval > *(spmax + 18)) {
1471                         *(spmax + 18) = tval;
1472                 }
1473                 if (tval < *(spmin + 18)) {
1474                         *(spmin + 18) = tval;
1475                 }
1476                 /* Compute %swpcad min/max values */
1477                 tval = (smc->tlskb - smc->frskb) ?
1478                        SP_VALUE(0, smc->caskb, smc->tlskb - smc->frskb) : 0.0;
1479                 if (tval > *(spmax + 19)) {
1480                         *(spmax + 19) = tval;
1481                 }
1482                 if (tval < *(spmin + 19)) {
1483                         *(spmin + 19) = tval;
1484                 }
1485                 /* Compute memused min/max values in MB */
1486                 tval = ((double) (smc->tlmkb - smc->frmkb)) / 1024;
1487                 if (tval > *(spmax + 20)) {
1488                         *(spmax + 20) = tval;
1489                 }
1490                 if (tval < *(spmin + 20)) {
1491                         *(spmin + 20) = tval;
1492                 }
1493                 /* Compute swpused min/max values in MB */
1494                 tval = ((double) (smc->tlskb - smc->frskb)) / 1024;
1495                 if (tval > *(spmax + 21)) {
1496                         *(spmax + 21) = tval;
1497                 }
1498                 if (tval < *(spmin + 21)) {
1499                         *(spmin + 21) = tval;
1500                 }
1501
1502                 /* MBmemfree */
1503                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1504                          ((double) smc->frmkb) / 1024,
1505                          out, outsize, svg_p->restart);
1506                 /* MBmemused */
1507                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1508                          ((double) (smc->tlmkb - smc->frmkb)) / 1024,
1509                          out + 1, outsize + 1, svg_p->restart);
1510                 /* MBcached */
1511                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1512                          ((double) smc->camkb) / 1024,
1513                           out + 2, outsize + 2, svg_p->restart);
1514                 /* MBbuffers */
1515                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1516                          ((double) smc->bufkb) / 1024,
1517                          out + 3, outsize + 3, svg_p->restart);
1518                 /* MBswpfree */
1519                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1520                          ((double) smc->frskb) / 1024,
1521                          out + 4, outsize + 4, svg_p->restart);
1522                 /* MBswpused */
1523                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1524                          ((double) (smc->tlskb - smc->frskb)) / 1024,
1525                          out + 5, outsize + 5, svg_p->restart);
1526                 /* MBswpcad */
1527                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1528                          ((double) smc->caskb) / 1024,
1529                          out + 6, outsize + 6, svg_p->restart);
1530                 /* MBcommit */
1531                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1532                          ((double) smc->comkb) / 1024,
1533                          out + 7, outsize + 7, svg_p->restart);
1534                 /* MBactive */
1535                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1536                          ((double) smc->activekb) / 1024,
1537                          out + 8, outsize + 8, svg_p->restart);
1538                 /* MBinact */
1539                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1540                          ((double) smc->inactkb) / 1024,
1541                          out + 9, outsize + 9, svg_p->restart);
1542                 /* MBdirty */
1543                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1544                          ((double) smc->dirtykb) / 1024,
1545                          out + 10, outsize + 10, svg_p->restart);
1546                 /* MBanonpg */
1547                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1548                          ((double) smc->anonpgkb) / 1024,
1549                          out + 11, outsize + 11, svg_p->restart);
1550                 /* MBslab */
1551                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1552                          ((double) smc->slabkb) / 1024,
1553                          out + 12, outsize + 12, svg_p->restart);
1554                 /* MBkstack */
1555                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1556                          ((double) smc->kstackkb) / 1024,
1557                          out + 13, outsize + 13, svg_p->restart);
1558                 /* MBpgtbl */
1559                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1560                          ((double) smc->pgtblkb) / 1024,
1561                          out + 14, outsize + 14, svg_p->restart);
1562                 /* MBvmused */
1563                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1564                          ((double) smc->vmusedkb) / 1024,
1565                          out + 15, outsize + 15, svg_p->restart);
1566                 /* %memused */
1567                 brappend(record_hdr->ust_time - svg_p->ust_time_ref,
1568                          0.0,
1569                          smc->tlmkb ?
1570                          SP_VALUE(smc->frmkb, smc->tlmkb, smc->tlmkb) : 0.0,
1571                          out + 16, outsize + 16, svg_p->dt);
1572                 /* %commit */
1573                 brappend(record_hdr->ust_time - svg_p->ust_time_ref,
1574                          0.0,
1575                          (smc->tlmkb + smc->tlskb) ?
1576                          SP_VALUE(0, smc->comkb, smc->tlmkb + smc->tlskb) : 0.0,
1577                          out + 17, outsize + 17, svg_p->dt);
1578                 /* %swpused */
1579                 brappend(record_hdr->ust_time - svg_p->ust_time_ref,
1580                          0.0,
1581                          smc->tlskb ?
1582                          SP_VALUE(smc->frskb, smc->tlskb, smc->tlskb) : 0.0,
1583                          out + 18, outsize + 18, svg_p->dt);
1584                 /* %swpcad */
1585                 brappend(record_hdr->ust_time - svg_p->ust_time_ref,
1586                          0.0,
1587                          (smc->tlskb - smc->frskb) ?
1588                          SP_VALUE(0, smc->caskb, smc->tlskb - smc->frskb) : 0.0,
1589                          out + 19, outsize + 19, svg_p->dt);
1590         }
1591
1592         if (action & F_END) {
1593
1594                 /* Conversion kB -> MB */
1595                 for (i = 0; i < 16; i++) {
1596                         *(spmin + i) /= 1024;
1597                         *(spmax + i) /= 1024;
1598                 }
1599
1600                 if (DISPLAY_MEM_AMT(a->opt_flags)) {
1601                         /* frmkb and tlmkb should be together because they will be drawn on the same view */
1602                         *(spmax + 3) = *(spmax + 1);
1603                         *(spmin + 3) = *(spmin + 1);
1604                         /* Move memused min/max values */
1605                         *(spmax + 1) = *(spmax + 20);
1606                         *(spmin + 1) = *(spmin + 20);
1607
1608                         draw_activity_graphs(2, SVG_LINE_GRAPH, title1a, g_title1a, NULL, group1a,
1609                                              spmin, spmax, out, outsize, svg_p, record_hdr);
1610                         draw_activity_graphs(2, SVG_BAR_GRAPH, title1b, g_title1b, NULL, group1b,
1611                                              spmin + 16, spmax + 16, out + 16, outsize + 16, svg_p, record_hdr);
1612                         draw_activity_graphs(DISPLAY_MEM_ALL(a->opt_flags) ? 2 : 1,
1613                                              SVG_LINE_GRAPH, title1c, g_title1c, NULL, group1c,
1614                                              spmin + 7, spmax + 7, out + 7, outsize + 7, svg_p, record_hdr);
1615                 }
1616
1617                 if (DISPLAY_SWAP(a->opt_flags)) {
1618                         /* Move swpused min/max values */
1619                         *(spmax + 5) = *(spmax + 21);
1620                         *(spmin + 5) = *(spmin + 21);
1621
1622                         draw_activity_graphs(1, SVG_LINE_GRAPH, title2a, g_title2a, NULL, group2a,
1623                                              spmin + 4, spmax + 4, out + 4, outsize + 4, svg_p, record_hdr);
1624                         draw_activity_graphs(2, SVG_BAR_GRAPH, title2b, g_title2b, NULL, group2b,
1625                                              spmin + 18, spmax + 18, out + 18, outsize + 18, svg_p, record_hdr);
1626                 }
1627
1628                 /* Free remaining structures */
1629                 free_graphs(out, outsize, spmin, spmax);
1630         }
1631 }
1632
1633 /*
1634  ***************************************************************************
1635  * Display kernel tables statistics in SVG.
1636  *
1637  * IN:
1638  * @a           Activity structure with statistics.
1639  * @curr        Index in array for current sample statistics.
1640  * @action      Action expected from current function.
1641  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1642  *              flag indicating that a restart record has been previously
1643  *              found (.@restart) and time used for the X axis origin
1644  *              (@ust_time_ref).
1645  * @itv         Interval of time in jiffies (only with F_MAIN action).
1646  * @record_hdr  Pointer on record header of current stats sample.
1647  ***************************************************************************
1648  */
1649 __print_funct_t svg_print_ktables_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1650                                         unsigned long long itv, struct record_header *record_hdr)
1651 {
1652         struct stats_ktables
1653                 *skc = (struct stats_ktables *) a->buf[curr];
1654         int group[] = {3, 1};
1655         char *title[] = {"Kernel tables (1)", "Kernel tables (2)"};
1656         char *g_title[] = {"~file-nr", "~inode-nr", "~dentunusd",
1657                            "~pty-nr"};
1658         static double *spmin, *spmax;
1659         static char **out;
1660         static int *outsize;
1661
1662         if (action & F_BEGIN) {
1663                 /*
1664                  * Allocate arrays that will contain the graphs data
1665                  * and the min/max values.
1666                  */
1667                 out = allocate_graph_lines(4, &outsize, &spmin, &spmax);
1668         }
1669
1670         if (action & F_MAIN) {
1671                 /* Check for min/max values */
1672                 save_extrema(0, 0, 4, (void *) a->buf[curr], NULL,
1673                              itv, spmin, spmax);
1674                 /* file-nr */
1675                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
1676                           (unsigned long) skc->file_used,
1677                           out, outsize, svg_p->restart);
1678                 /* inode-nr */
1679                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
1680                           (unsigned long) skc->inode_used,
1681                           out + 1, outsize + 1, svg_p->restart);
1682                 /* dentunusd */
1683                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
1684                           (unsigned long) skc->dentry_stat,
1685                           out + 2, outsize + 2, svg_p->restart);
1686                 /* pty-nr */
1687                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
1688                           (unsigned long) skc->pty_nr,
1689                           out + 3, outsize + 3, svg_p->restart);
1690         }
1691
1692         if (action & F_END) {
1693                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1694                                      spmin, spmax, out, outsize, svg_p, record_hdr);
1695
1696                 /* Free remaining structures */
1697                 free_graphs(out, outsize, spmin, spmax);
1698         }
1699 }
1700
1701 /*
1702  ***************************************************************************
1703  * Display queue and load statistics in SVG.
1704  *
1705  * IN:
1706  * @a           Activity structure with statistics.
1707  * @curr        Index in array for current sample statistics.
1708  * @action      Action expected from current function.
1709  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1710  *              flag indicating that a restart record has been previously
1711  *              found (.@restart) and time used for the X axis origin
1712  *              (@ust_time_ref).
1713  * @itv         Interval of time in jiffies (only with F_MAIN action).
1714  * @record_hdr  Pointer on record header of current stats sample.
1715  ***************************************************************************
1716  */
1717 __print_funct_t svg_print_queue_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1718                                       unsigned long long itv, struct record_header *record_hdr)
1719 {
1720         struct stats_queue
1721                 *sqc = (struct stats_queue *) a->buf[curr];
1722         int group[] = {2, 3, 1};
1723         char *title[] = {"Queue length", "Load average", "Task list"};
1724         char *g_title[] = {"~runq-sz", "~blocked",
1725                            "ldavg-1", "ldavg-5", "ldavg-15",
1726                            "~plist-sz"};
1727         static double *spmin, *spmax;
1728         static char **out;
1729         static int *outsize;
1730
1731         if (action & F_BEGIN) {
1732                 /*
1733                  * Allocate arrays that will contain the graphs data
1734                  * and the min/max values.
1735                  */
1736                 out = allocate_graph_lines(6, &outsize, &spmin, &spmax);
1737         }
1738
1739         if (action & F_MAIN) {
1740                 /* Check for min/max values */
1741                 save_extrema(0, 2, 4, (void *) a->buf[curr], NULL,
1742                              itv, spmin, spmax);
1743                 /* runq-sz */
1744                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
1745                           (unsigned long) sqc->nr_running,
1746                           out, outsize, svg_p->restart);
1747                 /* blocked */
1748                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
1749                           (unsigned long) sqc->procs_blocked,
1750                           out + 1, outsize + 1, svg_p->restart);
1751                 /* ldavg-1 */
1752                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1753                          (double) sqc->load_avg_1 / 100,
1754                          out + 2, outsize + 2, svg_p->restart);
1755                 /* ldavg-5 */
1756                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1757                          (double) sqc->load_avg_5 / 100,
1758                          out + 3, outsize + 3, svg_p->restart);
1759                 /* ldavg-15 */
1760                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1761                          (double) sqc->load_avg_15 / 100,
1762                          out + 4, outsize + 4, svg_p->restart);
1763                 /* plist-sz */
1764                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
1765                           (unsigned long) sqc->nr_threads,
1766                           out + 5, outsize + 5, svg_p->restart);
1767         }
1768
1769         if (action & F_END) {
1770                 /* Fix min/max values for load average */
1771                 *(spmin + 2) /= 100; *(spmax + 2) /= 100;
1772                 *(spmin + 3) /= 100; *(spmax + 3) /= 100;
1773                 *(spmin + 4) /= 100; *(spmax + 4) /= 100;
1774
1775                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1776                                      spmin, spmax, out, outsize, svg_p, record_hdr);
1777
1778                 /* Free remaining structures */
1779                 free_graphs(out, outsize, spmin, spmax);
1780         }
1781 }
1782
1783 /*
1784  ***************************************************************************
1785  * Display network interfaces statistics in SVG.
1786  *
1787  * IN:
1788  * @a           Activity structure with statistics.
1789  * @curr        Index in array for current sample statistics.
1790  * @action      Action expected from current function.
1791  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1792  *              flag indicating that a restart record has been previously
1793  *              found (.@restart) and time used for the X axis origin
1794  *              (@ust_time_ref).
1795  * @itv         Interval of time in jiffies (only with F_MAIN action).
1796  * @record_hdr  Pointer on record header of current stats sample.
1797  ***************************************************************************
1798  */
1799 __print_funct_t svg_print_net_dev_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1800                                         unsigned long long itv, struct record_header *record_hdr)
1801 {
1802         struct stats_net_dev *sndc, *sndp;
1803         int group1[] = {2, 2, 3};
1804         int group2[] = {1};
1805         char *title1[] = {"Network statistics (1)", "Network statistics (2)",
1806                           "Network statistics (3)"};
1807         char *title2[] = {"Network statistics (4)"};
1808         char *g_title1[] = {"rxpck/s", "txpck/s",
1809                             "rxkB/s", "txkB/s",
1810                             "rxcmp/s", "txcmp/s", "rxmcst/s"};
1811         char *g_title2[] = {"%ifutil"};
1812         static double *spmin, *spmax;
1813         static char **out;
1814         static int *outsize;
1815         char *item_name;
1816         double rxkb, txkb, ifutil;
1817         int i, j, k, pos, restart, *unregistered;
1818
1819         if (action & F_BEGIN) {
1820                 /*
1821                  * Allocate arrays (#0..7) that will contain the graphs data
1822                  * and the min/max values.
1823                  * Also allocate one additional array (#8) for each interface:
1824                  * out + 8 will contain the interface name,
1825                  * outsize + 8 will contain a positive value (TRUE) if the interface
1826                  * has either still not been registered, or has been unregistered.
1827                  */
1828                 out = allocate_graph_lines(9 * a->nr, &outsize, &spmin, &spmax);
1829         }
1830
1831         if (action & F_MAIN) {
1832                 restart = svg_p->restart;
1833                 /*
1834                  * Mark previously registered interfaces as now
1835                  * possibly unregistered for all graphs.
1836                  */
1837                 for (k = 0; k < a->nr; k++) {
1838                         unregistered = outsize + k * 9 + 8;
1839                         if (*unregistered == FALSE) {
1840                                 *unregistered = MAYBE;
1841                         }
1842                 }
1843
1844                 /* For each network interfaces structure */
1845                 for (i = 0; i < a->nr; i++) {
1846                         sndc = (struct stats_net_dev *) ((char *) a->buf[curr] + i * a->msize);
1847                         if (!strcmp(sndc->interface, ""))
1848                                 /* Empty structure: Ignore it */
1849                                 continue;
1850
1851                         /* Look for corresponding graph */
1852                         for (k = 0; k < a->nr; k++) {
1853                                 item_name = *(out + k * 9 + 8);
1854                                 if (!strcmp(sndc->interface, item_name))
1855                                         /* Graph found! */
1856                                         break;
1857                         }
1858                         if (k == a->nr) {
1859                                 /* Graph not found: Look for first free entry */
1860                                 for (k = 0; k < a->nr; k++) {
1861                                         item_name = *(out + k * 9 + 8);
1862                                         if (!strcmp(item_name, ""))
1863                                                 break;
1864                                 }
1865                                 if (k == a->nr)
1866                                         /* No free graph entry: Graph for this item won't be drawn */
1867                                         continue;
1868                         }
1869
1870                         pos = k * 9;
1871                         unregistered = outsize + pos + 8;
1872
1873                         j = check_net_dev_reg(a, curr, !curr, i);
1874                         sndp = (struct stats_net_dev *) ((char *) a->buf[!curr] + j * a->msize);
1875
1876                         /*
1877                          * If current interface was marked as previously unregistered,
1878                          * then set restart variable to TRUE so that the graph will be
1879                          * discontinuous, and mark it as now registered.
1880                          */
1881                         if (*unregistered == TRUE) {
1882                                 restart = TRUE;
1883                         }
1884                         *unregistered = FALSE;
1885
1886                         if (!item_name[0]) {
1887                                 /* Save network interface name (if not already done) */
1888                                 strncpy(item_name, sndc->interface, CHUNKSIZE);
1889                                 item_name[CHUNKSIZE - 1] = '\0';
1890                         }
1891
1892                         /* Check for min/max values */
1893                         save_extrema(7, 0, 0, (void *) sndc, (void *) sndp,
1894                                      itv, spmin + pos, spmax + pos);
1895
1896                         rxkb = S_VALUE(sndp->rx_bytes, sndc->rx_bytes, itv);
1897                         txkb = S_VALUE(sndp->tx_bytes, sndc->tx_bytes, itv);
1898                         ifutil = compute_ifutil(sndc, rxkb, txkb);
1899                         if (ifutil < *(spmin + pos + 7)) {
1900                                 *(spmin + pos + 7) = ifutil;
1901                         }
1902                         if (ifutil > *(spmax + pos + 7)) {
1903                                 *(spmax + pos + 7) = ifutil;
1904                         }
1905
1906                         /* rxpck/s */
1907                         lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1908                                  S_VALUE(sndp->rx_packets, sndc->rx_packets, itv),
1909                                  out + pos, outsize + pos, restart);
1910
1911                         /* txpck/s */
1912                         lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1913                                  S_VALUE(sndp->tx_packets, sndc->tx_packets, itv),
1914                                  out + pos + 1, outsize + pos + 1, restart);
1915
1916                         /* rxkB/s */
1917                         lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1918                                  rxkb / 1024,
1919                                  out + pos + 2, outsize + pos + 2, restart);
1920
1921                         /* txkB/s */
1922                         lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1923                                  txkb / 1024,
1924                                  out + pos + 3, outsize + pos + 3, restart);
1925
1926                         /* rxcmp/s */
1927                         lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1928                                  S_VALUE(sndp->rx_compressed, sndc->rx_compressed, itv),
1929                                  out + pos + 4, outsize + pos + 4, restart);
1930
1931                         /* txcmp/s */
1932                         lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1933                                  S_VALUE(sndp->tx_compressed, sndc->tx_compressed, itv),
1934                                  out + pos + 5, outsize + pos + 5, restart);
1935
1936                         /* rxmcst/s */
1937                         lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
1938                                  S_VALUE(sndp->multicast, sndc->multicast, itv),
1939                                  out + pos + 6, outsize + pos + 6, restart);
1940
1941                         /* %ifutil */
1942                         brappend(record_hdr->ust_time - svg_p->ust_time_ref,
1943                                  0.0, ifutil,
1944                                  out + pos + 7, outsize + pos + 7, svg_p->dt);
1945                 }
1946
1947                 /* Mark interfaces not seen here as now unregistered */
1948                 for (k = 0; k < a->nr; k++) {
1949                         unregistered = outsize + k * 9 + 8;
1950                         if (*unregistered != FALSE) {
1951                                 *unregistered = TRUE;
1952                         }
1953                 }
1954         }
1955
1956         if (action & F_END) {
1957                 for (i = 0; i < a->nr; i++) {
1958                         /*
1959                          * Check if there is something to display.
1960                          * Don't test sndc->interface because maybe the network
1961                          * interface has been registered later.
1962                          */
1963                         pos = i * 9;
1964                         if (!**(out + pos))
1965                                 continue;
1966
1967                         /* Recalculate min and max values in kB, not in B */
1968                         *(spmin + pos + 2) /= 1024;
1969                         *(spmax + pos + 2) /= 1024;
1970                         *(spmin + pos + 3) /= 1024;
1971                         *(spmax + pos + 3) /= 1024;
1972
1973                         item_name = *(out + pos + 8);
1974                         draw_activity_graphs(a->g_nr - 1, SVG_LINE_GRAPH,
1975                                              title1, g_title1, item_name, group1,
1976                                              spmin + pos, spmax + pos, out + pos, outsize + pos,
1977                                              svg_p, record_hdr);
1978                         draw_activity_graphs(1, SVG_BAR_GRAPH,
1979                                              title2, g_title2, item_name, group2,
1980                                              spmin + pos + 7, spmax + pos + 7, out + pos + 7, outsize + pos + 7,
1981                                              svg_p, record_hdr);
1982                 }
1983
1984                 /* Free remaining structures */
1985                 free_graphs(out, outsize, spmin, spmax);
1986         }
1987 }
1988
1989 /*
1990  ***************************************************************************
1991  * Display network socket statistics in SVG.
1992  *
1993  * IN:
1994  * @a           Activity structure with statistics.
1995  * @curr        Index in array for current sample statistics.
1996  * @action      Action expected from current function.
1997  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1998  *              flag indicating that a restart record has been previously
1999  *              found (.@restart) and time used for the X axis origin
2000  *              (@ust_time_ref).
2001  * @itv         Interval of time in jiffies (only with F_MAIN action).
2002  * @record_hdr  Pointer on record header of current stats sample.
2003  ***************************************************************************
2004  */
2005 __print_funct_t svg_print_net_sock_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
2006                                          unsigned long long itv, struct record_header *record_hdr)
2007 {
2008         struct stats_net_sock
2009                 *snsc = (struct stats_net_sock *) a->buf[curr];
2010         int group[] = {1, 5};
2011         char *title[] = {"Network sockets (1)", "Network sockets (2)"};
2012         char *g_title[] = {"~totsck",
2013                            "~tcpsck", "~tcp-tw", "~udpsck", "~rawsck", "~ip-frag"};
2014         static double *spmin, *spmax;
2015         static char **out;
2016         static int *outsize;
2017
2018         if (action & F_BEGIN) {
2019                 /*
2020                  * Allocate arrays that will contain the graphs data
2021                  * and the min/max values.
2022                  */
2023                 out = allocate_graph_lines(6, &outsize, &spmin, &spmax);
2024         }
2025
2026         if (action & F_MAIN) {
2027                 /* Check for min/max values */
2028                 save_extrema(0, 0, 6, (void *) a->buf[curr], NULL,
2029                              itv, spmin, spmax);
2030                 /* totsck */
2031                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
2032                           (unsigned long) snsc->sock_inuse,
2033                           out, outsize, svg_p->restart);
2034                 /* tcpsck */
2035                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
2036                           (unsigned long) snsc->tcp_inuse,
2037                           out + 1, outsize + 1, svg_p->restart);
2038                 /* tcp-tw */
2039                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
2040                           (unsigned long) snsc->tcp_tw,
2041                           out + 2, outsize + 2, svg_p->restart);
2042                 /* udpsck */
2043                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
2044                           (unsigned long) snsc->udp_inuse,
2045                           out + 3, outsize + 3, svg_p->restart);
2046                 /* rawsck */
2047                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
2048                           (unsigned long) snsc->raw_inuse,
2049                           out + 4, outsize + 4, svg_p->restart);
2050                 /* ip-frag */
2051                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
2052                           (unsigned long) snsc->frag_inuse,
2053                           out + 5, outsize + 5, svg_p->restart);
2054         }
2055
2056         if (action & F_END) {
2057                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
2058                                      spmin, spmax, out, outsize, svg_p, record_hdr);
2059
2060                 /* Free remaining structures */
2061                 free_graphs(out, outsize, spmin, spmax);
2062         }
2063 }
2064
2065 /*
2066  ***************************************************************************
2067  * Display IPv4 network statistics in SVG.
2068  *
2069  * IN:
2070  * @a           Activity structure with statistics.
2071  * @curr        Index in array for current sample statistics.
2072  * @action      Action expected from current function.
2073  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
2074  *              flag indicating that a restart record has been previously
2075  *              found (.@restart) and time used for the X axis origin
2076  *              (@ust_time_ref).
2077  * @itv         Interval of time in jiffies (only with F_MAIN action).
2078  * @record_hdr  Pointer on record header of current stats sample.
2079  ***************************************************************************
2080  */
2081 __print_funct_t svg_print_net_ip_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
2082                                        unsigned long long itv, struct record_header *record_hdr)
2083 {
2084         struct stats_net_ip
2085                 *snic = (struct stats_net_ip *) a->buf[curr],
2086                 *snip = (struct stats_net_ip *) a->buf[!curr];
2087         int group[] = {4, 2, 2};
2088         char *title[] = {"IPv4 network statistics (1)", "IPv4 network statistics (2)", "IPv4 network statistics (3)"};
2089         char *g_title[] = {"irec/s", "fwddgm/s", "idel/s", "orq/s",
2090                            "asmrq/s", "asmok/s",
2091                            "fragok/s", "fragcrt/s"};
2092         static double *spmin, *spmax;
2093         static char **out;
2094         static int *outsize;
2095
2096         if (action & F_BEGIN) {
2097                 /*
2098                  * Allocate arrays that will contain the graphs data
2099                  * and the min/max values.
2100                  */
2101                 out = allocate_graph_lines(8, &outsize, &spmin, &spmax);
2102         }
2103
2104         if (action & F_MAIN) {
2105                 /* Check for min/max values */
2106                 save_extrema(8, 0, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
2107                              itv, spmin, spmax);
2108
2109                 /* irec/s */
2110                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2111                          S_VALUE(snip->InReceives, snic->InReceives, itv),
2112                          out, outsize, svg_p->restart);
2113                 /* fwddgm/s */
2114                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2115                          S_VALUE(snip->ForwDatagrams, snic->ForwDatagrams, itv),
2116                          out + 1, outsize + 1, svg_p->restart);
2117                 /* idel/s */
2118                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2119                          S_VALUE(snip->InDelivers, snic->InDelivers, itv),
2120                          out + 2, outsize + 2, svg_p->restart);
2121                 /* orq/s */
2122                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2123                          S_VALUE(snip->OutRequests, snic->OutRequests, itv),
2124                          out + 3, outsize + 3, svg_p->restart);
2125                 /* asmrq/s */
2126                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2127                          S_VALUE(snip->ReasmReqds, snic->ReasmReqds, itv),
2128                          out + 4, outsize + 4, svg_p->restart);
2129                 /* asmok/s */
2130                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2131                          S_VALUE(snip->ReasmOKs, snic->ReasmOKs, itv),
2132                          out + 5, outsize + 5, svg_p->restart);
2133                 /* fragok/s */
2134                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2135                          S_VALUE(snip->FragOKs, snic->FragOKs, itv),
2136                          out + 6, outsize + 6, svg_p->restart);
2137                 /* fragcrt/s */
2138                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2139                          S_VALUE(snip->FragCreates, snic->FragCreates, itv),
2140                          out + 7, outsize + 7, svg_p->restart);
2141         }
2142
2143         if (action & F_END) {
2144                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
2145                                      spmin, spmax, out, outsize, svg_p, record_hdr);
2146
2147                 /* Free remaining structures */
2148                 free_graphs(out, outsize, spmin, spmax);
2149         }
2150 }
2151
2152 /*
2153  ***************************************************************************
2154  * Display TCPv4 network statistics in SVG.
2155  *
2156  * IN:
2157  * @a           Activity structure with statistics.
2158  * @curr        Index in array for current sample statistics.
2159  * @action      Action expected from current function.
2160  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
2161  *              flag indicating that a restart record has been previously
2162  *              found (.@restart) and time used for the X axis origin
2163  *              (@ust_time_ref).
2164  * @itv         Interval of time in jiffies (only with F_MAIN action).
2165  * @record_hdr  Pointer on record header of current stats sample.
2166  ***************************************************************************
2167  */
2168 __print_funct_t svg_print_net_tcp_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
2169                                         unsigned long long itv, struct record_header *record_hdr)
2170 {
2171         struct stats_net_tcp
2172                 *sntc = (struct stats_net_tcp *) a->buf[curr],
2173                 *sntp = (struct stats_net_tcp *) a->buf[!curr];
2174         int group[] = {2, 2};
2175         char *title[] = {"TCPv4 network statistics (1)", "TCPv4 network statistics (2)"};
2176         char *g_title[] = {"active/s", "passive/s",
2177                            "iseg/s", "oseg/s"};
2178         static double *spmin, *spmax;
2179         static char **out;
2180         static int *outsize;
2181
2182         if (action & F_BEGIN) {
2183                 /*
2184                  * Allocate arrays that will contain the graphs data
2185                  * and the min/max values.
2186                  */
2187                 out = allocate_graph_lines(4, &outsize, &spmin, &spmax);
2188         }
2189
2190         if (action & F_MAIN) {
2191                 /* Check for min/max values */
2192                 save_extrema(0, 4, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
2193                              itv, spmin, spmax);
2194
2195                 /* active/s */
2196                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2197                          S_VALUE(sntp->ActiveOpens, sntc->ActiveOpens, itv),
2198                          out, outsize, svg_p->restart);
2199                 /* passive/s */
2200                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2201                          S_VALUE(sntp->PassiveOpens, sntc->PassiveOpens, itv),
2202                          out + 1, outsize + 1, svg_p->restart);
2203                 /* iseg/s */
2204                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2205                          S_VALUE(sntp->InSegs, sntc->InSegs, itv),
2206                          out + 2, outsize + 2, svg_p->restart);
2207                 /* oseg/s */
2208                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2209                          S_VALUE(sntp->OutSegs, sntc->OutSegs, itv),
2210                          out + 3, outsize + 3, svg_p->restart);
2211         }
2212
2213         if (action & F_END) {
2214                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
2215                                      spmin, spmax, out, outsize, svg_p, record_hdr);
2216
2217                 /* Free remaining structures */
2218                 free_graphs(out, outsize, spmin, spmax);
2219         }
2220 }
2221
2222 /*
2223  ***************************************************************************
2224  * Display UDPv4 network statistics in SVG.
2225  *
2226  * IN:
2227  * @a           Activity structure with statistics.
2228  * @curr        Index in array for current sample statistics.
2229  * @action      Action expected from current function.
2230  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
2231  *              flag indicating that a restart record has been previously
2232  *              found (.@restart) and time used for the X axis origin
2233  *              (@ust_time_ref).
2234  * @itv         Interval of time in jiffies (only with F_MAIN action).
2235  * @record_hdr  Pointer on record header of current stats sample.
2236  ***************************************************************************
2237  */
2238 __print_funct_t svg_print_net_udp_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
2239                                         unsigned long long itv, struct record_header *record_hdr)
2240 {
2241         struct stats_net_udp
2242                 *snuc = (struct stats_net_udp *) a->buf[curr],
2243                 *snup = (struct stats_net_udp *) a->buf[!curr];
2244         int group[] = {2, 2};
2245         char *title[] = {"UDPv4 network statistics (1)", "UDPv4 network statistics (2)"};
2246         char *g_title[] = {"idgm/s", "odgm/s",
2247                            "noport/s", "idgmerr/s"};
2248         static double *spmin, *spmax;
2249         static char **out;
2250         static int *outsize;
2251
2252         if (action & F_BEGIN) {
2253                 /*
2254                  * Allocate arrays that will contain the graphs data
2255                  * and the min/max values.
2256                  */
2257                 out = allocate_graph_lines(4, &outsize, &spmin, &spmax);
2258         }
2259
2260         if (action & F_MAIN) {
2261                 /* Check for min/max values */
2262                 save_extrema(0, 4, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
2263                              itv, spmin, spmax);
2264
2265                 /* idgm/s */
2266                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2267                          S_VALUE(snup->InDatagrams, snuc->InDatagrams, itv),
2268                          out, outsize, svg_p->restart);
2269                 /* odgm/s */
2270                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2271                          S_VALUE(snup->OutDatagrams, snuc->OutDatagrams, itv),
2272                          out + 1, outsize + 1, svg_p->restart);
2273                 /* noport/s */
2274                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2275                          S_VALUE(snup->NoPorts, snuc->NoPorts, itv),
2276                          out + 2, outsize + 2, svg_p->restart);
2277                 /* idgmerr/s */
2278                 lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2279                          S_VALUE(snup->InErrors, snuc->InErrors, itv),
2280                          out + 3, outsize + 3, svg_p->restart);
2281         }
2282
2283         if (action & F_END) {
2284                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
2285                                      spmin, spmax, out, outsize, svg_p, record_hdr);
2286
2287                 /* Free remaining structures */
2288                 free_graphs(out, outsize, spmin, spmax);
2289         }
2290 }
2291
2292 /*
2293  ***************************************************************************
2294  * Display IPV6 network socket statistics in SVG.
2295  *
2296  * IN:
2297  * @a           Activity structure with statistics.
2298  * @curr        Index in array for current sample statistics.
2299  * @action      Action expected from current function.
2300  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
2301  *              flag indicating that a restart record has been previously
2302  *              found (.@restart) and time used for the X axis origin
2303  *              (@ust_time_ref).
2304  * @itv         Interval of time in jiffies (only with F_MAIN action).
2305  * @record_hdr  Pointer on record header of current stats sample.
2306  ***************************************************************************
2307  */
2308 __print_funct_t svg_print_net_sock6_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
2309                                           unsigned long long itv, struct record_header *record_hdr)
2310 {
2311         struct stats_net_sock6
2312                 *snsc = (struct stats_net_sock6 *) a->buf[curr];
2313         int group[] = {4};
2314         char *title[] = {"IPv6 network sockets"};
2315         char *g_title[] = {"~tcp6sck", "~udp6sck", "~raw6sck", "~ip6-frag"};
2316         static double *spmin, *spmax;
2317         static char **out;
2318         static int *outsize;
2319
2320         if (action & F_BEGIN) {
2321                 /*
2322                  * Allocate arrays that will contain the graphs data
2323                  * and the min/max values.
2324                  */
2325                 out = allocate_graph_lines(4, &outsize, &spmin, &spmax);
2326         }
2327
2328         if (action & F_MAIN) {
2329                 /* Check for min/max values */
2330                 save_extrema(0, 0, 4, (void *) a->buf[curr], NULL,
2331                              itv, spmin, spmax);
2332                 /* tcp6sck */
2333                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
2334                           (unsigned long) snsc->tcp6_inuse,
2335                           out, outsize, svg_p->restart);
2336                 /* udp6sck */
2337                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
2338                           (unsigned long) snsc->udp6_inuse,
2339                           out + 1, outsize + 1, svg_p->restart);
2340                 /* raw6sck */
2341                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
2342                           (unsigned long) snsc->raw6_inuse,
2343                           out + 2, outsize + 2, svg_p->restart);
2344                 /* ip6-frag */
2345                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
2346                           (unsigned long) snsc->frag6_inuse,
2347                           out + 3, outsize + 3, svg_p->restart);
2348         }
2349
2350         if (action & F_END) {
2351                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
2352                                      spmin, spmax, out, outsize, svg_p, record_hdr);
2353
2354                 /* Free remaining structures */
2355                 free_graphs(out, outsize, spmin, spmax);
2356         }
2357 }
2358
2359 /*
2360  ***************************************************************************
2361  * Display CPU frequency statistics in SVG.
2362  *
2363  * IN:
2364  * @a           Activity structure with statistics.
2365  * @curr        Index in array for current sample statistics.
2366  * @action      Action expected from current function.
2367  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
2368  *              flag indicating that a restart record has been previously
2369  *              found (.@restart) and time used for the X axis origin
2370  *              (@ust_time_ref).
2371  * @itv         Interval of time in jiffies (only with F_MAIN action).
2372  * @record_hdr  Pointer on record header of current stats sample.
2373  ***************************************************************************
2374  */
2375 __print_funct_t svg_print_pwr_cpufreq_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
2376                                             unsigned long long g_itv, struct record_header *record_hdr)
2377 {
2378         struct stats_pwr_cpufreq *spc, *spp;
2379         int group[] = {1};
2380         char *title[] = {"CPU frequency"};
2381         char *g_title[] = {"MHz"};
2382         static double *spmin, *spmax;
2383         static char **out;
2384         static int *outsize;
2385         char item_name[8];
2386         int i;
2387
2388         if (action & F_BEGIN) {
2389                 /*
2390                  * Allocate arrays that will contain the graphs data
2391                  * and the min/max values.
2392                  */
2393                 out = allocate_graph_lines(a->nr, &outsize, &spmin, &spmax);
2394         }
2395
2396         if (action & F_MAIN) {
2397                 /* For each CPU */
2398                 for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {
2399
2400                         spc = (struct stats_pwr_cpufreq *) ((char *) a->buf[curr]  + i * a->msize);
2401                         spp = (struct stats_pwr_cpufreq *) ((char *) a->buf[!curr]  + i * a->msize);
2402
2403                         /* Should current CPU (including CPU "all") be displayed? */
2404                         if (!(a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))))
2405                                 /* No */
2406                                 continue;
2407
2408                         /* MHz */
2409                         recappend(record_hdr->ust_time - svg_p->ust_time_ref,
2410                                   ((double) spp->cpufreq) / 100,
2411                                   ((double) spc->cpufreq) / 100,
2412                                   out + i, outsize + i, svg_p->restart, svg_p->dt,
2413                                   spmin + i, spmax + i);
2414                 }
2415         }
2416
2417         if (action & F_END) {
2418                 for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {
2419
2420                         /* Should current CPU (including CPU "all") be displayed? */
2421                         if (!(a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))))
2422                                 /* No */
2423                                 continue;
2424
2425                         if (!i) {
2426                                 /* This is CPU "all" */
2427                                 strcpy(item_name, "all");
2428                         }
2429                         else {
2430                                 sprintf(item_name, "%d", i - 1);
2431                         }
2432
2433                         draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH,
2434                                              title, g_title, item_name, group,
2435                                              spmin + i, spmax + i, out + i, outsize + i,
2436                                              svg_p, record_hdr);
2437                 }
2438
2439                 /* Free remaining structures */
2440                 free_graphs(out, outsize, spmin, spmax);
2441         }
2442 }
2443
2444 /*
2445  ***************************************************************************
2446  * Display fan statistics in SVG.
2447  *
2448  * IN:
2449  * @a           Activity structure with statistics.
2450  * @curr        Index in array for current sample statistics.
2451  * @action      Action expected from current function.
2452  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
2453  *              flag indicating that a restart record has been previously
2454  *              found (.@restart) and time used for the X axis origin
2455  *              (@ust_time_ref).
2456  * @itv         Interval of time in jiffies (only with F_MAIN action).
2457  * @record_hdr  Pointer on record header of current stats sample.
2458  ***************************************************************************
2459  */
2460 __print_funct_t svg_print_pwr_fan_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
2461                                         unsigned long long g_itv, struct record_header *record_hdr)
2462 {
2463         struct stats_pwr_fan *spc, *spp;
2464         int group[] = {1};
2465         char *title[] = {"Fan speed"};
2466         char *g_title[] = {"~rpm"};
2467         static double *spmin, *spmax;
2468         static char **out;
2469         static int *outsize;
2470         char item_name[MAX_SENSORS_DEV_LEN + 8];
2471         int i;
2472
2473         if (action & F_BEGIN) {
2474                 /*
2475                  * Allocate arrays that will contain the graphs data
2476                  * and the min/max values.
2477                  */
2478                 out = allocate_graph_lines(a->nr, &outsize, &spmin, &spmax);
2479         }
2480
2481         if (action & F_MAIN) {
2482                 /* For each fan */
2483                 for (i = 0; i < a->nr; i++) {
2484
2485                         spc = (struct stats_pwr_fan *) ((char *) a->buf[curr]  + i * a->msize);
2486                         spp = (struct stats_pwr_fan *) ((char *) a->buf[!curr]  + i * a->msize);
2487
2488                         /* rpm */
2489                         recappend(record_hdr->ust_time - svg_p->ust_time_ref,
2490                                   (double) spp->rpm,
2491                                   (double) spc->rpm,
2492                                   out + i, outsize + i, svg_p->restart, svg_p->dt,
2493                                   spmin + i, spmax + i);
2494                 }
2495         }
2496
2497         if (action & F_END) {
2498                 for (i = 0; i < a->nr; i++) {
2499
2500                         spc = (struct stats_pwr_fan *) ((char *) a->buf[curr]  + i * a->msize);
2501
2502                         snprintf(item_name, MAX_SENSORS_DEV_LEN + 8, "%d: %s", i + 1, spc->device);
2503                         item_name[MAX_SENSORS_DEV_LEN + 7] = '\0';
2504
2505                         draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH,
2506                                              title, g_title, item_name, group,
2507                                              spmin + i, spmax + i, out + i, outsize + i,
2508                                              svg_p, record_hdr);
2509                 }
2510
2511                 /* Free remaining structures */
2512                 free_graphs(out, outsize, spmin, spmax);
2513         }
2514 }
2515
2516 /*
2517  ***************************************************************************
2518  * Display temperature statistics in SVG.
2519  *
2520  * IN:
2521  * @a           Activity structure with statistics.
2522  * @curr        Index in array for current sample statistics.
2523  * @action      Action expected from current function.
2524  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
2525  *              flag indicating that a restart record has been previously
2526  *              found (.@restart) and time used for the X axis origin
2527  *              (@ust_time_ref).
2528  * @itv         Interval of time in jiffies (only with F_MAIN action).
2529  * @record_hdr  Pointer on record header of current stats sample.
2530  ***************************************************************************
2531  */
2532 __print_funct_t svg_print_pwr_temp_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
2533                                          unsigned long long g_itv, struct record_header *record_hdr)
2534 {
2535         struct stats_pwr_temp *spc;
2536         int group[] = {1};
2537         char *title1[] = {"Device temperature (1)"};
2538         char *title2[] = {"Device temperature (2)"};
2539         char *g1_title[] = {"~degC"};
2540         char *g2_title[] = {"%temp"};
2541         static double *spmin, *spmax;
2542         static char **out;
2543         static int *outsize;
2544         char item_name[MAX_SENSORS_DEV_LEN + 8];
2545         int i;
2546         double tval;
2547
2548         if (action & F_BEGIN) {
2549                 /*
2550                  * Allocate arrays that will contain the graphs data
2551                  * and the min/max values.
2552                  */
2553                 out = allocate_graph_lines(2 * a->nr, &outsize, &spmin, &spmax);
2554         }
2555
2556         if (action & F_MAIN) {
2557                 /* For each temperature  sensor */
2558                 for (i = 0; i < a->nr; i++) {
2559
2560                         spc = (struct stats_pwr_temp *) ((char *) a->buf[curr]  + i * a->msize);
2561
2562                         /* Look for min/max values */
2563                         if (spc->temp < *(spmin + 2 * i)) {
2564                                 *(spmin + 2 * i) = spc->temp;
2565                         }
2566                         if (spc->temp > *(spmax + 2 * i)) {
2567                                 *(spmax + 2 * i) = spc->temp;
2568                         }
2569                         tval = (spc->temp_max - spc->temp_min) ?
2570                                (spc->temp - spc->temp_min) / (spc->temp_max - spc->temp_min) * 100 :
2571                                0.0;
2572                         if (tval < *(spmin + 2 * i + 1)) {
2573                                 *(spmin + 2 * i + 1) = tval;
2574                         }
2575                         if (tval > *(spmax + 2 * i + 1)) {
2576                                 *(spmax + 2 * i + 1) = tval;
2577                         }
2578
2579                         /* degC */
2580                         lnappend(record_hdr->ust_time - svg_p->ust_time_ref,
2581                                  (double) spc->temp,
2582                                  out + 2 * i, outsize + 2 * i, svg_p->restart);
2583                         /* %temp */
2584                         brappend(record_hdr->ust_time - svg_p->ust_time_ref,
2585                                  0.0, tval,
2586                                  out + 2 * i + 1, outsize + 2 * i + 1, svg_p->dt);
2587                 }
2588         }
2589
2590         if (action & F_END) {
2591                 for (i = 0; i < a->nr; i++) {
2592
2593                         spc = (struct stats_pwr_temp *) ((char *) a->buf[curr]  + i * a->msize);
2594
2595                         snprintf(item_name, MAX_SENSORS_DEV_LEN + 8, "%d: %s", i + 1, spc->device);
2596                         item_name[MAX_SENSORS_DEV_LEN + 7] = '\0';
2597
2598                         draw_activity_graphs(1, SVG_LINE_GRAPH,
2599                                              title1, g1_title, item_name, group,
2600                                              spmin + 2 * i, spmax + 2 * i, out + 2 * i, outsize + 2 * i,
2601                                              svg_p, record_hdr);
2602                         draw_activity_graphs(1, SVG_BAR_GRAPH,
2603                                              title2, g2_title, item_name, group,
2604                                              spmin + 2 * i + 1, spmax + 2 * i + 1,
2605                                              out + 2 * i + 1, outsize + 2 * i + 1,
2606                                              svg_p, record_hdr);
2607                 }
2608
2609                 /* Free remaining structures */
2610                 free_graphs(out, outsize, spmin, spmax);
2611         }
2612 }
2613
2614 /*
2615  ***************************************************************************
2616  * Display huge pages statistics in SVG.
2617  *
2618  * IN:
2619  * @a           Activity structure with statistics.
2620  * @curr        Index in array for current sample statistics.
2621  * @action      Action expected from current function.
2622  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
2623  *              flag indicating that a restart record has been previously
2624  *              found (.@restart) and time used for the X axis origin
2625  *              (@ust_time_ref).
2626  * @itv         Interval of time in jiffies (only with F_MAIN action).
2627  * @record_hdr  Pointer on record header of current stats sample.
2628  ***************************************************************************
2629  */
2630 __print_funct_t svg_print_huge_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
2631                                      unsigned long long itv, struct record_header *record_hdr)
2632 {
2633         struct stats_huge
2634                 *smc = (struct stats_huge *) a->buf[curr];
2635         int group1[] = {2};
2636         int group2[] = {1};
2637         char *title1[] = {"Huge pages utilization (1)"};
2638         char *title2[] = {"Huge pages utilization (2)"};
2639         char *g1_title[] = {"~kbhugfree", "~kbhugused"};
2640         char *g2_title[] = {"%hugused"};
2641         static double *spmin, *spmax;
2642         static char **out;
2643         static int *outsize;
2644         double tval;
2645
2646         if (action & F_BEGIN) {
2647                 /*
2648                  * Allocate arrays that will contain the graphs data
2649                  * and the min/max values.
2650                  */
2651                 out = allocate_graph_lines(3, &outsize, &spmin, &spmax);
2652         }
2653
2654         if (action & F_MAIN) {
2655                 /* Check for min/max values */
2656                 save_extrema(0, 1, 0, (void *) a->buf[curr], NULL,
2657                              itv, spmin, spmax);
2658
2659                 if (smc->tlhkb - smc->frhkb < *(spmin + 1)) {
2660                         *(spmin + 1) = smc->tlhkb - smc->frhkb;
2661                 }
2662                 if (smc->tlhkb - smc->frhkb > *(spmax + 1)) {
2663                         *(spmax + 1) = smc->tlhkb - smc->frhkb;
2664                 }
2665                 tval = smc->tlhkb ? SP_VALUE(smc->frhkb, smc->tlhkb, smc->tlhkb) : 0.0;
2666                 if (tval < *(spmin + 2)) {
2667                         *(spmin + 2) = tval;
2668                 }
2669                 if (tval > *(spmax + 2)) {
2670                         *(spmax + 2) = tval;
2671                 }
2672
2673                 /* kbhugfree */
2674                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
2675                           (unsigned long) smc->frhkb,
2676                           out, outsize, svg_p->restart);
2677                 /* hugused */
2678                 lniappend(record_hdr->ust_time - svg_p->ust_time_ref,
2679                           (unsigned long) smc->tlhkb - smc->frhkb,
2680                           out + 1, outsize + 1, svg_p->restart);
2681                 /* %hugused */
2682                 brappend(record_hdr->ust_time - svg_p->ust_time_ref,
2683                          0.0, tval,
2684                          out + 2, outsize + 2, svg_p->dt);
2685         }
2686
2687         if (action & F_END) {
2688                 draw_activity_graphs(1, SVG_LINE_GRAPH,
2689                                      title1, g1_title, NULL, group1,
2690                                      spmin, spmax, out, outsize, svg_p, record_hdr);
2691                 draw_activity_graphs(1, SVG_BAR_GRAPH,
2692                                      title2, g2_title, NULL, group2,
2693                                      spmin + 2, spmax + 2, out + 2, outsize + 2,
2694                                      svg_p, record_hdr);
2695
2696                 /* Free remaining structures */
2697                 free_graphs(out, outsize, spmin, spmax);
2698         }
2699 }