]> granicus.if.org Git - sysstat/blob - svg_stats.c
32155800849a723592766b9a2036e140fb572014
[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 window. 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  * @minv        minv[pos] is modified and contains the global min value found.
171  * @maxv        maxv[pos] is modified and contains the global max value found.
172  ***************************************************************************
173  */
174 void get_global_extrema(int pos, int n, double minv[], double maxv[])
175 {
176         int i;
177
178         for (i = 1; i < n; i++) {
179                 if (minv[pos + i] < minv[pos]) {
180                         minv[pos] = minv[pos + i];
181                 }
182                 if (maxv[pos + i] > maxv[pos]) {
183                         maxv[pos] = maxv[pos + i];
184                 }
185         }
186 }
187
188 /*
189  ***************************************************************************
190  * Allocate arrays used to save graphs data, min and max values.
191  * @n arrays of chars are allocated for @n graphs to draw. A pointer on this
192  * array is returned. This is equivalent to "char data[][n]" where each
193  * element is of indeterminate size and will contain the graph data (eg.
194  * << path d="M12,14 L13,16..." ... >>.
195  * The size of element data[i] is given by outsize[i].
196  * Also allocate an array to save min values (equivalent to "double spmin[n]")
197  * and an array for max values (equivalent to "double spmax[n]").
198  *
199  * IN:
200  * @n           Number of graphs to draw for current activity.
201  *
202  * OUT:
203  * @outsize     Array that will contain the sizes of each element in array
204  *              of chars. Equivalent to "int outsize[n]" with
205  *              outsize[n] = sizeof(data[][n]).
206  * @spmin       Array that will contain min values for current activity.
207  * @spmax       Array that will contain max values for current activity.
208  *
209  * RETURNS:
210  * Pointer on array of arrays of chars that will contain the graphs data.
211  *
212  * NB: @min and @max arrays contain values in the same order as the fields
213  * in the statistics structure.
214  ***************************************************************************
215  */
216 char **allocate_graph_lines(int n, int **outsize, double **spmin, double **spmax)
217 {
218         char **out;
219         char *out_p;
220         int i;
221
222         /*
223          * Allocate an array of pointers. Each of these pointers will
224          * be an array of chars.
225          */
226         if ((out = (char **) malloc(n * sizeof(char *))) == NULL) {
227                 perror("malloc");
228                 exit(4);
229         }
230         /* Allocate array that will contain the size of each array of chars */
231         if ((*outsize = (int *) malloc(n * sizeof(int))) == NULL) {
232                 perror("malloc");
233                 exit(4);
234         }
235         /* Allocate array that will contain the min value of each graph */
236         if ((*spmin = (double *) malloc(n * sizeof(double))) == NULL) {
237                 perror("malloc");
238                 exit(4);
239         }
240         /* Allocate array that will contain the max value of each graph */
241         if ((*spmax = (double *) malloc(n * sizeof(double))) == NULL) {
242                 perror("malloc");
243                 exit(4);
244         }
245         /* Allocate arrays of chars that will contain graphs data */
246         for (i = 0; i < n; i++) {
247                 if ((out_p = (char *) malloc(CHUNKSIZE * sizeof(char))) == NULL) {
248                         perror("malloc");
249                         exit(4);
250                 }
251                 *(out + i) = out_p;
252                 *out_p = '\0';                  /* Reset string so that it can be safely strncat()'d later */
253                 *(*outsize + i) = CHUNKSIZE;    /* Each array of chars has a default size of CHUNKSIZE */
254                 *(*spmin + i) = DBL_MAX;        /* Init min and max values */
255                 *(*spmax + i) = -DBL_MAX;
256         }
257
258         return out;
259 }
260
261 /*
262  ***************************************************************************
263  * Save SVG code for current graph.
264  *
265  * IN:
266  * @data        SVG code to append to current graph definition.
267  * @out         Pointer on array of chars for current graph definition.
268  * @outsize     Size of array of chars for current graph definition.
269  *
270  * OUT:
271  * @out         Pointer on array of chars for current graph definition that
272  *              has been updated with the addition of current sample data.
273  * @outsize     Array that containing the (possibly new) sizes of each
274  *              element in array of chars.
275  ***************************************************************************
276  */
277 void save_svg_data(char *data, char **out, int *outsize)
278 {
279         char *out_p;
280         int len;
281
282         out_p = *out;
283         /* Determine space left in array */
284         len = *outsize - strlen(out_p) - 1;
285         if (strlen(data) >= len) {
286                 /*
287                  * If current array of chars doesn't have enough space left
288                  * then reallocate it with CHUNKSIZE more bytes.
289                  */
290                 SREALLOC(out_p, char, *outsize + CHUNKSIZE);
291                 *out = out_p;
292                 *outsize += CHUNKSIZE;
293                 len += CHUNKSIZE;
294         }
295         strncat(out_p, data, len);
296 }
297
298 /*
299  ***************************************************************************
300  * Update line graph definition by appending current X,Y coordinates.
301  *
302  * IN:
303  * @timetag     Timestamp in seconds since the epoch for current sample
304  *              stats. Will be used as X coordinate.
305  * @value       Value of current sample metric. Will be used as Y coordinate.
306  * @out         Pointer on array of chars for current graph definition.
307  * @outsize     Size of array of chars for current graph definition.
308  * @restart     Set to TRUE if a RESTART record has been read since the last
309  *              statistics sample.
310  *
311  * OUT:
312  * @out         Pointer on array of chars for current graph definition that
313  *              has been updated with the addition of current sample data.
314  * @outsize     Array that containing the (possibly new) sizes of each
315  *              element in array of chars.
316  ***************************************************************************
317  */
318 void lnappend(unsigned long timetag, double value, char **out, int *outsize, int restart)
319 {
320         char data[128];
321
322         /* Prepare additional graph definition data */
323         snprintf(data, 128, " %c%lu,%.2f", restart ? 'M' : 'L', timetag, value);
324         data[127] = '\0';
325
326         save_svg_data(data, out, outsize);
327 }
328
329 /*
330  ***************************************************************************
331  * Update line graph definition by appending current X,Y coordinates. Use
332  * (unsigned long) integer values here.
333  *
334  * IN:
335  * @timetag     Timestamp in seconds since the epoch for current sample
336  *              stats. Will be used as X coordinate.
337  * @value       Value of current sample metric. Will be used as Y coordinate.
338  * @out         Pointer on array of chars for current graph definition.
339  * @outsize     Size of array of chars for current graph definition.
340  * @restart     Set to TRUE if a RESTART record has been read since the last
341  *              statistics sample.
342  *
343  * OUT:
344  * @out         Pointer on array of chars for current graph definition that
345  *              has been updated with the addition of current sample data.
346  * @outsize     Array that containing the (possibly new) sizes of each
347  *              element in array of chars.
348  ***************************************************************************
349  */
350 void lniappend(unsigned long timetag, unsigned long value, char **out, int *outsize,
351                int restart)
352 {
353         char data[128];
354
355         /* Prepare additional graph definition data */
356         snprintf(data, 128, " %c%lu,%lu", restart ? 'M' : 'L', timetag, value);
357         data[127] = '\0';
358
359         save_svg_data(data, out, outsize);
360 }
361
362 /*
363  ***************************************************************************
364  * Update bar graph definition by adding a new rectangle.
365  *
366  * IN:
367  * @timetag     Timestamp in seconds since the epoch for current sample
368  *              stats. Will be used as X coordinate.
369  * @value       Value of current sample metric. Will be used as rectangle
370  *              height.
371  * @offset      Offset for Y coordinate.
372  * @out         Pointer on array of chars for current graph definition.
373  * @outsize     Size of array of chars for current graph definition.
374  * @dt          Interval of time in seconds between current and previous
375  *              sample.
376  *
377  * OUT:
378  * @out         Pointer on array of chars for current graph definition that
379  *              has been updated with the addition of current sample data.
380  * @outsize     Array that containing the (possibly new) sizes of each
381  *              element in array of chars.
382  ***************************************************************************
383  */
384 void brappend(unsigned long timetag, double offset, double value, char **out, int *outsize,
385               unsigned long dt)
386 {
387         char data[128];
388
389         /* Prepare additional graph definition data */
390         if (value == 0.0)
391                 /* Dont draw a flat rectangle! */
392                 return;
393
394         snprintf(data, 128, "<rect x=\"%lu\" y=\"%.2f\" height=\"%.2f\" width=\"%lu\"/>",
395                  timetag - dt, MINIMUM(offset, 100.0), MINIMUM(value, (100.0 - offset)), dt);
396         data[127] = '\0';
397
398         save_svg_data(data, out, outsize);
399
400 }
401
402 /*
403  ***************************************************************************
404  * Update CPU graph and min/max values for each metric.
405  *
406  * IN:
407  * @timetag     Timestamp in seconds since the epoch for current sample
408  *              stats. Will be used as X coordinate.
409  * @offset      Offset for Y coordinate.
410  * @value       Value of current CPU metric. Will be used as rectangle
411  *              height.
412  * @out         Pointer on array of chars for current graph definition.
413  * @outsize     Size of array of chars for current graph definition.
414  * @dt          Interval of time in seconds between current and previous
415  *              sample.
416  * @spmin       Min value already found for this CPU metric.
417  * @spmax       Max value already found for this CPU metric.
418  *
419  * OUT:
420  * @offset      New offset value, to use to draw next rectangle
421  * @out         Pointer on array of chars for current graph definition that
422  *              has been updated with the addition of current sample data.
423  * @outsize     Array that containing the (possibly new) sizes of each
424  *              element in array of chars.
425  ***************************************************************************
426  */
427 void cpuappend(unsigned long timetag, double *offset, double value, char **out, int *outsize,
428                unsigned long dt, double *spmin, double *spmax)
429 {
430         /* Save min and max values */
431         if (value < *spmin) {
432                 *spmin = value;
433         }
434         if (value > *spmax) {
435                 *spmax = value;
436         }
437         /* Prepare additional graph definition data */
438         brappend(timetag, *offset, value, out, outsize, dt);
439
440         *offset += value;
441 }
442
443 /*
444  ***************************************************************************
445  * Update rectangular graph and min/max values.
446  *
447  * IN:
448  * @timetag     Timestamp in seconds since the epoch for current sample
449  *              stats. Will be used as X coordinate.
450  * @p_value     Metric value for previous sample
451  * @value       Metric value for current sample.
452  * @out         Pointer on array of chars for current graph definition.
453  * @outsize     Size of array of chars for current graph definition.
454  * @restart     Set to TRUE if a RESTART record has been read since the last
455  *              statistics sample.
456  * @dt          Interval of time in seconds between current and previous
457  *              sample.
458  * @spmin       Min value already found for this metric.
459  * @spmax       Max value already found for this metric.
460  *
461  * OUT:
462  * @out         Pointer on array of chars for current graph definition that
463  *              has been updated with the addition of current sample data.
464  * @outsize     Array that containing the (possibly new) sizes of each
465  *              element in array of chars.
466  * @spmin       Min value for this metric.
467  * @spmax       Max value for this metric.
468  ***************************************************************************
469  */
470 void recappend(unsigned long timetag, double p_value, double value, char **out, int *outsize,
471                int restart, unsigned long dt, double *spmin, double *spmax)
472 {
473         char data[128], data1[128], data2[128];
474
475         /* Save min and max values */
476         if (value < *spmin) {
477                 *spmin = value;
478         }
479         if (value > *spmax) {
480                 *spmax = value;
481         }
482         /* Prepare additional graph definition data */
483         if (restart) {
484                 snprintf(data1, 128, " M%lu,%.2f", timetag - dt, p_value);
485                 data1[127] = '\0';
486         }
487         if (p_value != value) {
488                 snprintf(data2, 128, " L%lu,%.2f", timetag, value);
489                 data2[127] = '\0';
490         }
491         snprintf(data, 128, "%s L%lu,%.2f%s", restart ? data1 : "", timetag, p_value,
492                  p_value != value ? data2 : "");
493         data[127] = '\0';
494
495         save_svg_data(data, out, outsize);
496 }
497
498 /*
499  ***************************************************************************
500  * Calculate the value on the Y axis between two horizontal lines that will
501  * make the graph background grid.
502  *
503  * IN:
504  * @lmax        Max value reached for this graph.
505  *
506  * OUT:
507  * @dp          Number of decimal places for Y graduations.
508  *
509  * RETURNS:
510  * Value between two horizontal lines.
511  ***************************************************************************
512  */
513 double ygrid(double lmax, int *dp)
514 {
515         char val[32];
516         int l, i, e = 1;
517         long n = 0;
518
519         *dp = 0;
520         if (lmax == 0) {
521                 lmax = 1;
522         }
523         n = (long) (lmax / SVG_H_GRIDNR);
524         if (!n) {
525                 *dp = 2;
526                 return (lmax / SVG_H_GRIDNR);
527         }
528         snprintf(val, 32, "%ld", n);
529         val[31] = '\0';
530         l = strlen(val);
531         if (l < 2)
532                 return n;
533         for (i = 1; i < l; i++) {
534                 e = e * 10;
535         }
536         return ((double) (((long) (n / e)) * e));
537 }
538
539 /*
540  ***************************************************************************
541  * Calculate the value on the X axis between two vertical lines that will
542  * make the graph background grid.
543  *
544  * IN:
545  * @timestart   First data timestamp (X coordinate of the first data point).
546  * @timeend     Last data timestamp (X coordinate of the last data point).
547  *
548  * RETURNS:
549  * Value between two vertical lines.
550  ***************************************************************************
551  */
552 long int xgrid(unsigned long timestart, unsigned long timeend)
553 {
554         if ((timeend - timestart) <= SVG_V_GRIDNR)
555                 return 1;
556         else
557                 return ((timeend - timestart) / SVG_V_GRIDNR);
558 }
559
560 /*
561  ***************************************************************************
562  * Free global graphs structures.
563  *
564  * IN:
565  * @out         Pointer on array of chars for each graph definition.
566  * @outsize     Size of array of chars for each graph definition.
567  * @spmin       Array containing min values for graphs.
568  * @spmax       Array containing max values for graphs.
569  ***************************************************************************
570  */
571 void free_graphs(char **out, int *outsize, double *spmin, double *spmax)
572 {
573         if (out) {
574                 free(out);
575         }
576         if (outsize) {
577                 free(outsize);
578         }
579         if (spmin) {
580                 free(spmin);
581         }
582         if (spmax) {
583                 free(spmax);
584         }
585 }
586
587 /*
588  ***************************************************************************
589  * Display all graphs for current activity.
590  *
591  * IN:
592  * @g_nr        Number of graphs to display.
593  * @g_type      Type of graph (SVG_LINE_GRAPH, SVG_BAR_GRAPH).
594  * @title       Titles for each set of graphs.
595  * @g_title     Titles for each graph.
596  * @item_name   Item (network interface, etc.) name.
597  * @group       Indicate how graphs are grouped together to make sets.
598  * @spmin       Array containing min values for graphs.
599  * @spmax       Array containing max values for graphs.
600  * @out         Pointer on array of chars for each graph definition.
601  * @outsize     Size of array of chars for each graph definition.
602  * @svg_p       SVG specific parameters: Current graph number (.@graph_no)
603  *              and a pointer on a record header structure (.@record_hdr)
604  *              containing the first stats sample.
605  * @record_hdr  Pointer on record header of current stats sample.
606  ***************************************************************************
607  */
608 void draw_activity_graphs(int g_nr, int g_type, char *title[], char *g_title[], char *item_name,
609                           int group[], double *spmin, double *spmax, char **out, int *outsize,
610                           struct svg_parm *svg_p, struct record_header *record_hdr)
611 {
612         struct record_header stamp;
613         struct tm rectime;
614         char *out_p;
615         int i, j, dp, pos = 0;
616         long int k;
617         double lmax, xfactor, yfactor, ypos;
618         char cur_time[32];
619
620         /* Translate to proper position for current activity */
621         printf("<g id=\"g%d\" transform=\"translate(0,%d)\">\n",
622                svg_p->graph_no,
623                SVG_H_YSIZE + svg_p->graph_no * SVG_T_YSIZE);
624
625         /* For each set of graphs which are part of current activity */
626         for (i = 0; i < g_nr; i++) {
627
628                 /* Graph background */
629                 printf("<rect x=\"0\" y=\"%d\" height=\"%d\" width=\"%d\"/>\n",
630                        i * SVG_T_YSIZE,
631                        SVG_V_YSIZE, SVG_V_XSIZE);
632
633                 /* Graph title */
634                 printf("<text x=\"0\" y=\"%d\" style=\"fill: yellow; stroke: none\">%s",
635                        20 + i * SVG_T_YSIZE, title[i]);
636                 if (item_name) {
637                         printf(" [%s]", item_name);
638                 }
639                 printf("\n");
640                 printf("<tspan x=\"%d\" y=\"%d\" style=\"fill: yellow; stroke: none; font-size: 12px\">"
641                        "(Min, Max values)</tspan>\n</text>\n",
642                        5 + SVG_M_XSIZE + SVG_G_XSIZE,
643                        25 + i * SVG_T_YSIZE);
644
645                 /*
646                  * At least two samples are needed.
647                  * And a min and max value should have been found.
648                  */
649                 if ((record_hdr->ust_time == svg_p->record_hdr->ust_time) ||
650                     (*(spmin + i) == DBL_MAX) || (*(spmax + i) == -DBL_MIN)) {
651                         /* No data found */
652                         printf("<text x=\"0\" y=\"%d\" style=\"fill: red; stroke: none\">No data</text>\n",
653                                SVG_M_YSIZE + i * SVG_T_YSIZE);
654                         continue;
655                 }
656
657                 /* X and Y axis */
658                 printf("<polyline points=\"%d,%d %d,%d %d,%d\" stroke=\"white\" stroke-width=\"2\"/>\n",
659                        SVG_M_XSIZE, SVG_M_YSIZE + i * SVG_T_YSIZE,
660                        SVG_M_XSIZE, SVG_M_YSIZE + SVG_G_YSIZE + i * SVG_T_YSIZE,
661                        SVG_M_XSIZE + SVG_G_XSIZE, SVG_M_YSIZE + SVG_G_YSIZE + i * SVG_T_YSIZE);
662
663                 /* Caption */
664                 for (j = 0; j < group[i]; j++) {
665                         /* Set dp to TRUE (1) if current metric is based on integer values */
666                         dp = (g_title[pos + j][0] == '~');
667                         printf("<text x=\"%d\" y=\"%d\" style=\"fill: #%06x; stroke: none; font-size: 12px\">"
668                                "%s (%.*f, %.*f)</text>\n",
669                                5 + SVG_M_XSIZE + SVG_G_XSIZE, SVG_M_YSIZE + i * SVG_T_YSIZE + j * 15,
670                                svg_colors[(pos + j) & SVG_COLORS_IDX_MASK], g_title[pos + j] + dp,
671                                !dp * 2, *(spmin + pos + j), !dp * 2, *(spmax + pos + j));
672                 }
673
674                 /* Get global min and max value for current set of graphs */
675                 get_global_extrema(pos, group[i], spmin, spmax);
676
677                 /* Translate to proper position for current graph within current activity */
678                 printf("<g transform=\"translate(%d,%d)\">\n",
679                        SVG_M_XSIZE, SVG_M_YSIZE + SVG_G_YSIZE + i * SVG_T_YSIZE);
680
681                 /* Grid */
682                 if (g_type == SVG_LINE_GRAPH) {
683                         /* For line graphs */
684                         if (*(spmax + pos) == 0) {
685                                 /* If all values are zero then set current max value to 1 */
686                                 lmax = 1.0;
687                         }
688                         else {
689                                 lmax = *(spmax + pos);
690                         }
691                         /* Max value cannot be too small, else Y graduations will be meaningless */
692                         if (lmax < SVG_H_GRIDNR * 0.01) {
693                                 lmax = SVG_H_GRIDNR * 0.01;
694                         }
695                         ypos = ygrid(lmax, &dp);
696                 }
697                 else {
698                         /* For bar graphs (used for %values) */
699                         ypos = 25.0;    /* Draw lines at 25%, 50%, 75% and 100% */
700                         dp = 0;         /* No decimals */
701
702                         /* Max should be always 100% except for percentage values greater than 100% */
703                         if (*(spmax + pos) > 100.0) {
704                                 lmax = *(spmax + pos);
705                         }
706                         else {
707                                 lmax = 100.0;
708                         }
709                 }
710                 yfactor = (double) -SVG_G_YSIZE / lmax;
711                 j = 1;
712                 do {
713                         printf("<polyline points=\"0,%.2f %d,%.2f\" style=\"vector-effect: non-scaling-stroke; "
714                                "stroke: #202020\" transform=\"scale(1,%f)\"/>\n",
715                                ypos * j, SVG_G_XSIZE, ypos * j, yfactor);
716                         j++;
717                 }
718                 while (ypos * j <= lmax);
719                 j = 0;
720                 do {
721                         printf("<text x=\"0\" y=\"%ld\" style=\"fill: white; stroke: none; font-size: 12px; "
722                                "text-anchor: end\">%.*f.</text>\n",
723                                (long) (ypos * j * yfactor), dp, ypos * j);
724                         j++;
725                 }
726                 while (ypos * j <= lmax);
727
728                 k = xgrid(svg_p->record_hdr->ust_time, record_hdr->ust_time);
729                 xfactor = (double) SVG_G_XSIZE / (record_hdr->ust_time - svg_p->record_hdr->ust_time);
730                 stamp = *svg_p->record_hdr;
731                 for (j = 0; (j <= SVG_V_GRIDNR) && (stamp.ust_time <= record_hdr->ust_time); j++) {
732                         sa_get_record_timestamp_struct(flags, &stamp, &rectime, NULL);
733                         set_record_timestamp_string(flags, &stamp, NULL, cur_time, 32, &rectime);
734                         printf("<polyline points=\"%ld,0 %ld,%d\" style=\"vector-effect: non-scaling-stroke; "
735                                "stroke: #202020\" transform=\"scale(%f,1)\"/>\n",
736                                k * j, k * j, -SVG_G_YSIZE, xfactor);
737                         printf("<text x=\"%ld\" y=\"10\" style=\"fill: white; stroke: none; font-size: 12px; "
738                                "text-anchor: start\" transform=\"rotate(45,%ld,0)\">%s</text>\n",
739                                (long) (k * j * xfactor), (long) (k * j * xfactor), cur_time);
740                         stamp.ust_time += k;
741                 }
742                 if (!PRINT_LOCAL_TIME(flags)) {
743                         printf("<text x=\"-10\" y=\"30\" style=\"fill: yellow; stroke: none; font-size: 12px; "
744                                "text-anchor: end\">UTC</text>\n");
745                 }
746
747                 /* Draw current graphs set */
748                 for (j = 0; j < group[i]; j++) {
749                         out_p = *(out + pos + j);
750                         if (g_type == SVG_LINE_GRAPH) {
751                                 /* Line graphs */
752                                 printf("<path id=\"g%dp%d\" d=\"%s\" "
753                                        "style=\"vector-effect: non-scaling-stroke; "
754                                        "stroke: #%06x; stroke-width: 1; fill-opacity: 0\" "
755                                        "transform=\"scale(%f,%f)\"/>\n",
756                                        svg_p->graph_no, pos + j, out_p,
757                                        svg_colors[(pos + j) & SVG_COLORS_IDX_MASK],
758                                        xfactor,
759                                        yfactor);
760                         }
761                         else if (*out_p) {      /* Ignore flat bars */
762                                 /* Bar graphs */
763                                 printf("<g style=\"fill: #%06x; stroke: none\" transform=\"scale(%f,%f)\">\n",
764                                        svg_colors[(pos + j) & SVG_COLORS_IDX_MASK], xfactor, yfactor);
765                                 printf("%s\n", out_p);
766                                 printf("</g>\n");
767                         }
768                         free(out_p);
769                 }
770                 printf("</g>\n");
771                 pos += group[i];
772         }
773         printf("</g>\n");
774
775         /* Next graph */
776         (svg_p->graph_no) += g_nr;
777 }
778
779 /*
780  ***************************************************************************
781  * Display CPU statistics in SVG
782  *
783  * IN:
784  * @a           Activity structure with statistics.
785  * @curr        Index in array for current sample statistics.
786  * @action      Action expected from current function.
787  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
788  *              flag indicating that a restart record has been previously
789  *              found (.@restart) and a pointer on a record header structure
790  *              (.@record_hdr) containing the first stats sample.
791  * @itv         Interval of time in jiffies (only with F_MAIN action).
792  * @record_hdr  Pointer on record header of current stats sample.
793  ***************************************************************************
794  */
795 __print_funct_t svg_print_cpu_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
796                                     unsigned long long g_itv, struct record_header *record_hdr)
797 {
798         struct stats_cpu *scc, *scp;
799         int group1[] = {5};
800         int group2[] = {9};
801         char *title[] = {"CPU load"};
802         char *g_title1[] = {"%user", "%nice", "%system", "%iowait", "%steal", "%idle"};
803         char *g_title2[] = {"%usr", "%nice", "%sys", "%iowait", "%steal", "%irq", "%soft", "%guest", "%gnice", "%idle"};
804         static double *spmin, *spmax;
805         static char **out;
806         static int *outsize;
807         char item_name[8];
808         double offset, val;
809         int i, j, k, pos, cpu_offline;
810
811         if (action & F_BEGIN) {
812                 /*
813                  * Allocate arrays that will contain the graphs data
814                  * and the min/max values.
815                  */
816                 out = allocate_graph_lines(10 * a->nr, &outsize, &spmin, &spmax);
817         }
818
819         if (action & F_MAIN) {
820                 /* For each CPU */
821                 for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {
822
823                         scc = (struct stats_cpu *) ((char *) a->buf[curr]  + i * a->msize);
824                         scp = (struct stats_cpu *) ((char *) a->buf[!curr] + i * a->msize);
825
826                         /* Should current CPU (including CPU "all") be displayed? */
827                         if (!(a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))))
828                                 /* No */
829                                 continue;
830
831                         pos = i * 10;
832                         offset = 0.0;
833
834                         if (i) {        /* Don't test CPU "all" here */
835                                 /*
836                                  * If the CPU is offline then it is omited from /proc/stat:
837                                  * All the fields couldn't have been read and the sum of them is zero.
838                                  * (Remember that guest/guest_nice times are already included in
839                                  * user/nice modes.)
840                                  */
841                                 if ((scc->cpu_user    + scc->cpu_nice + scc->cpu_sys   +
842                                      scc->cpu_iowait  + scc->cpu_idle + scc->cpu_steal +
843                                      scc->cpu_hardirq + scc->cpu_softirq) == 0) {
844                                         /*
845                                          * Set current struct fields (which have been set to zero)
846                                          * to values from previous iteration. Hence their values won't
847                                          * jump from zero when the CPU comes back online.
848                                          */
849                                         *scc = *scp;
850
851                                         g_itv = 0;
852                                         cpu_offline = TRUE;
853                                 }
854                                 else {
855                                         /*
856                                          * Recalculate interval for current proc.
857                                          * If result is 0 then current CPU is a tickless one.
858                                          */
859                                         g_itv = get_per_cpu_interval(scc, scp);
860                                         cpu_offline = FALSE;
861                                 }
862
863                                 if (!g_itv) {   /* Current CPU is offline or tickless */
864
865                                         val = (cpu_offline ? 0.0        /* Offline CPU: %idle = 0% */
866                                                            : 100.0);    /* Tickless CPU: %idle = 100% */
867
868                                         if (DISPLAY_CPU_DEF(a->opt_flags)) {
869                                                 j  = 5; /* -u */
870                                         }
871                                         else {  /* DISPLAY_CPU_ALL(a->opt_flags) */
872                                                 j = 9;  /* -u ALL */
873                                         }
874
875                                         /* Check min/max values for %user, etc. */
876                                         for (k = 0; k < j; k++) {
877                                                 if (0.0 < *(spmin + pos + k)) {
878                                                         *(spmin + pos + k) = 0.0;
879                                                 }
880                                                 if (0.0 > *(spmax + pos + k)) {
881                                                         *(spmax + pos + k) = 0.0;
882                                                 }
883                                         }
884
885                                         /* %idle */
886                                         cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
887                                                   &offset, val,
888                                                   out + pos + j, outsize + pos + j, svg_p->dt,
889                                                   spmin + pos + j, spmax + pos + j);
890                                         continue;
891                                 }
892                         }
893
894                         if (DISPLAY_CPU_DEF(a->opt_flags)) {
895                                 /* %user */
896                                 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
897                                           &offset, ll_sp_value(scp->cpu_user, scc->cpu_user, g_itv),
898                                           out + pos, outsize + pos, svg_p->dt,
899                                           spmin + pos, spmax + pos);
900                         }
901                         else {
902                                 /* %usr */
903                                 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
904                                           &offset,
905                                           (scc->cpu_user - scc->cpu_guest) < (scp->cpu_user - scp->cpu_guest) ?
906                                            0.0 :
907                                            ll_sp_value(scp->cpu_user - scp->cpu_guest,
908                                                        scc->cpu_user - scc->cpu_guest, g_itv),
909                                           out + pos, outsize + pos, svg_p->dt,
910                                           spmin + pos, spmax + pos);
911                         }
912
913                         if (DISPLAY_CPU_DEF(a->opt_flags)) {
914                                 /* %nice */
915                                 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
916                                           &offset, ll_sp_value(scp->cpu_nice, scc->cpu_nice, g_itv),
917                                           out + pos + 1, outsize + pos + 1, svg_p->dt,
918                                           spmin + pos + 1, spmax + pos + 1);
919                         }
920                         else {
921                                 /* %nice */
922                                 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
923                                           &offset,
924                                           (scc->cpu_nice - scc->cpu_guest_nice) < (scp->cpu_nice - scp->cpu_guest_nice) ?
925                                            0.0 :
926                                            ll_sp_value(scp->cpu_nice - scp->cpu_guest_nice,
927                                                        scc->cpu_nice - scc->cpu_guest_nice, g_itv),
928                                           out + pos + 1, outsize + pos + 1, svg_p->dt,
929                                           spmin + pos + 1, spmax + pos + 1);
930                         }
931
932                         if (DISPLAY_CPU_DEF(a->opt_flags)) {
933                                 /* %system */
934                                 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
935                                           &offset,
936                                           ll_sp_value(scp->cpu_sys + scp->cpu_hardirq + scp->cpu_softirq,
937                                                       scc->cpu_sys + scc->cpu_hardirq + scc->cpu_softirq,
938                                                       g_itv),
939                                           out + pos + 2, outsize + pos + 2, svg_p->dt,
940                                           spmin + pos + 2, spmax + pos + 2);
941                         }
942                         else {
943                                 /* %sys */
944                                 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
945                                           &offset, ll_sp_value(scp->cpu_sys, scc->cpu_sys, g_itv),
946                                           out + pos + 2, outsize + pos + 2, svg_p->dt,
947                                           spmin + pos + 2, spmax + pos + 2);
948                         }
949
950                         /* %iowait */
951                         cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
952                                   &offset, ll_sp_value(scp->cpu_iowait, scc->cpu_iowait, g_itv),
953                                   out + pos + 3, outsize + pos + 3, svg_p->dt,
954                                   spmin + pos + 3, spmax + pos + 3);
955
956                         /* %steal */
957                         cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
958                                   &offset, ll_sp_value(scp->cpu_steal, scc->cpu_steal, g_itv),
959                                   out + pos + 4, outsize + pos + 4, svg_p->dt,
960                                   spmin + pos + 4, spmax + pos + 4);
961
962                         if (DISPLAY_CPU_ALL(a->opt_flags)) {
963                                 /* %irq */
964                                 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
965                                           &offset, ll_sp_value(scp->cpu_hardirq, scc->cpu_hardirq, g_itv),
966                                           out + pos + 5, outsize + pos + 5, svg_p->dt,
967                                           spmin + pos + 5, spmax + pos + 5);
968
969                                 /* %soft */
970                                 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
971                                           &offset, ll_sp_value(scp->cpu_softirq, scc->cpu_softirq, g_itv),
972                                           out + pos + 6, outsize + pos + 6, svg_p->dt,
973                                           spmin + pos + 6, spmax + pos + 6);
974
975                                 /* %guest */
976                                 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
977                                           &offset, ll_sp_value(scp->cpu_guest, scc->cpu_guest, g_itv),
978                                           out + pos + 7, outsize + pos + 7, svg_p->dt,
979                                           spmin + pos + 7, spmax + pos + 7);
980
981                                 /* %gnice */
982                                 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
983                                           &offset, ll_sp_value(scp->cpu_guest_nice, scc->cpu_guest_nice, g_itv),
984                                           out + pos + 8, outsize + pos + 8, svg_p->dt,
985                                           spmin + pos + 8, spmax + pos + 8);
986
987                                 j = 9;
988                         }
989                         else {
990                                 j = 5;
991                         }
992
993                         /* %idle */
994                         cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
995                                   &offset,
996                                   (scc->cpu_idle < scp->cpu_idle ? 0.0 :
997                                    ll_sp_value(scp->cpu_idle, scc->cpu_idle, g_itv)),
998                                   out + pos + j, outsize + pos + j, svg_p->dt,
999                                   spmin + pos + j, spmax + pos + j);
1000                 }
1001         }
1002
1003         if (action & F_END) {
1004                 for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {
1005
1006                         /* Should current CPU (including CPU "all") be displayed? */
1007                         if (!(a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))))
1008                                 /* No */
1009                                 continue;
1010
1011                         pos = i * 10;
1012                         if (!i) {
1013                                 /* This is CPU "all" */
1014                                 strcpy(item_name, "all");
1015                         }
1016                         else {
1017                                 sprintf(item_name, "%d", i - 1);
1018                         }
1019
1020                         if (DISPLAY_CPU_DEF(a->opt_flags)) {
1021                                 draw_activity_graphs(a->g_nr, SVG_BAR_GRAPH,
1022                                                      title, g_title1, item_name, group1,
1023                                                      spmin + pos, spmax + pos, out + pos, outsize + pos,
1024                                                      svg_p, record_hdr);
1025                         }
1026                         else {
1027                                 draw_activity_graphs(a->g_nr, SVG_BAR_GRAPH,
1028                                                      title, g_title2, item_name, group2,
1029                                                      spmin + pos, spmax + pos, out + pos, outsize + pos,
1030                                                      svg_p, record_hdr);
1031                         }
1032                 }
1033
1034                 /* Free remaining structures */
1035                 free_graphs(out, outsize, spmin, spmax);
1036         }
1037 }
1038
1039 /*
1040  ***************************************************************************
1041  * Display task creation and context switch statistics in SVG
1042  *
1043  * IN:
1044  * @a           Activity structure with statistics.
1045  * @curr        Index in array for current sample statistics.
1046  * @action      Action expected from current function.
1047  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1048  *              flag indicating that a restart record has been previously
1049  *              found (.@restart) and a pointer on a record header structure
1050  *              (.@record_hdr) containing the first stats sample.
1051  * @itv         Interval of time in jiffies (only with F_MAIN action).
1052  * @record_hdr  Pointer on record header of current stats sample.
1053  ***************************************************************************
1054  */
1055 __print_funct_t svg_print_pcsw_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1056                                      unsigned long long itv, struct record_header *record_hdr)
1057 {
1058         struct stats_pcsw
1059                 *spc = (struct stats_pcsw *) a->buf[curr],
1060                 *spp = (struct stats_pcsw *) a->buf[!curr];
1061         int group[] = {1, 1};
1062         char *title[] = {"Switching activity", "Task creation"};
1063         char *g_title[] = {"cswch/s",
1064                            "proc/s"};
1065         static double *spmin, *spmax;
1066         static char **out;
1067         static int *outsize;
1068
1069         if (action & F_BEGIN) {
1070                 /*
1071                  * Allocate arrays that will contain the graphs data
1072                  * and the min/max values.
1073                  */
1074                 out = allocate_graph_lines(2, &outsize, &spmin, &spmax);
1075         }
1076
1077         if (action & F_MAIN) {
1078                 /* Check for min/max values */
1079                 save_extrema(1, 1, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
1080                              itv, spmin, spmax);
1081                 /* cswch/s */
1082                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1083                          S_VALUE(spp->context_switch, spc->context_switch, itv),
1084                          out, outsize, svg_p->restart);
1085                 /* proc/s */
1086                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1087                          S_VALUE(spp->processes, spc->processes, itv),
1088                          out + 1, outsize + 1, svg_p->restart);
1089         }
1090
1091         if (action & F_END) {
1092                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1093                                      spmin, spmax, out, outsize, svg_p, record_hdr);
1094
1095                 /* Free remaining structures */
1096                 free_graphs(out, outsize, spmin, spmax);
1097         }
1098 }
1099
1100 /*
1101  ***************************************************************************
1102  * Display swap statistics in SVG
1103  *
1104  * IN:
1105  * @a           Activity structure with statistics.
1106  * @curr        Index in array for current sample statistics.
1107  * @action      Action expected from current function.
1108  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1109  *              flag indicating that a restart record has been previously
1110  *              found (.@restart) and a pointer on a record header structure
1111  *              (.@record_hdr) containing the first stats sample.
1112  * @itv         Interval of time in jiffies (only with F_MAIN action).
1113  * @record_hdr  Pointer on record header of current stats sample.
1114  ***************************************************************************
1115  */
1116 __print_funct_t svg_print_swap_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1117                                        unsigned long long itv, struct record_header *record_hdr)
1118 {
1119         struct stats_swap
1120                 *ssc = (struct stats_swap *) a->buf[curr],
1121                 *ssp = (struct stats_swap *) a->buf[!curr];
1122         int group[] = {2};
1123         char *title[] = {"Swap activity"};
1124         char *g_title[] = {"pswpin/s", "pswpout/s" };
1125         static double *spmin, *spmax;
1126         static char **out;
1127         static int *outsize;
1128
1129         if (action & F_BEGIN) {
1130                 /*
1131                  * Allocate arrays that will contain the graphs data
1132                  * and the min/max values.
1133                  */
1134                 out = allocate_graph_lines(2, &outsize, &spmin, &spmax);
1135         }
1136
1137         if (action & F_MAIN) {
1138                 /* Check for min/max values */
1139                 save_extrema(0, 2, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
1140                              itv, spmin, spmax);
1141                 /* pswpin/s */
1142                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1143                          S_VALUE(ssp->pswpin, ssc->pswpin, itv),
1144                          out, outsize, svg_p->restart);
1145                 /* pswpout/s */
1146                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1147                          S_VALUE(ssp->pswpout, ssc->pswpout, itv),
1148                          out + 1, outsize + 1, svg_p->restart);
1149         }
1150
1151         if (action & F_END) {
1152                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1153                                      spmin, spmax, out, outsize, svg_p, record_hdr);
1154
1155                 /* Free remaining structures */
1156                 free_graphs(out, outsize, spmin, spmax);
1157         }
1158 }
1159
1160 /*
1161  ***************************************************************************
1162  * Display paging statistics in SVG
1163  *
1164  * IN:
1165  * @a           Activity structure with statistics.
1166  * @curr        Index in array for current sample statistics.
1167  * @action      Action expected from current function.
1168  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1169  *              flag indicating that a restart record has been previously
1170  *              found (.@restart) and a pointer on a record header structure
1171  *              (.@record_hdr) containing the first stats sample.
1172  * @itv         Interval of time in jiffies (only with F_MAIN action).
1173  * @record_hdr  Pointer on record header of current stats sample.
1174  ***************************************************************************
1175  */
1176 __print_funct_t svg_print_paging_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1177                                        unsigned long long itv, struct record_header *record_hdr)
1178 {
1179         struct stats_paging
1180                 *spc = (struct stats_paging *) a->buf[curr],
1181                 *spp = (struct stats_paging *) a->buf[!curr];
1182         int group[] = {2, 2, 4};
1183         char *title[] = {"Paging activity (1)", "Paging activity (2)", "Paging activity (3)"};
1184         char *g_title[] = {"pgpgin/s", "pgpgout/s",
1185                            "fault/s", "majflt/s",
1186                            "pgfree/s", "pgscank/s", "pgscand/s", "pgsteal/s"};
1187         static double *spmin, *spmax;
1188         static char **out;
1189         static int *outsize;
1190
1191         if (action & F_BEGIN) {
1192                 /*
1193                  * Allocate arrays that will contain the graphs data
1194                  * and the min/max values.
1195                  */
1196                 out = allocate_graph_lines(8, &outsize, &spmin, &spmax);
1197         }
1198
1199         if (action & F_MAIN) {
1200                 /* Check for min/max values */
1201                 save_extrema(0, 8, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
1202                              itv, spmin, spmax);
1203                 /* pgpgin/s */
1204                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1205                          S_VALUE(spp->pgpgin, spc->pgpgin, itv),
1206                          out, outsize, svg_p->restart);
1207                 /* pgpgout/s */
1208                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1209                          S_VALUE(spp->pgpgout, spc->pgpgout, itv),
1210                          out + 1, outsize + 1, svg_p->restart);
1211                 /* fault/s */
1212                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1213                          S_VALUE(spp->pgfault, spc->pgfault, itv),
1214                          out + 2, outsize + 2, svg_p->restart);
1215                 /* majflt/s */
1216                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1217                          S_VALUE(spp->pgmajfault, spc->pgmajfault, itv),
1218                          out + 3, outsize + 3, svg_p->restart);
1219                 /* pgfree/s */
1220                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1221                          S_VALUE(spp->pgfree, spc->pgfree, itv),
1222                          out + 4, outsize + 4, svg_p->restart);
1223                 /* pgscank/s */
1224                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1225                          S_VALUE(spp->pgscan_kswapd, spc->pgscan_kswapd, itv),
1226                          out + 5, outsize + 5, svg_p->restart);
1227                 /* pgscand/s */
1228                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1229                          S_VALUE(spp->pgscan_direct, spc->pgscan_direct, itv),
1230                          out + 6, outsize + 6, svg_p->restart);
1231                 /* pgsteal/s */
1232                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1233                          S_VALUE(spp->pgsteal, spc->pgsteal, itv),
1234                          out + 7, outsize + 7, svg_p->restart);
1235         }
1236
1237         if (action & F_END) {
1238                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1239                                      spmin, spmax, out, outsize, svg_p, record_hdr);
1240
1241                 /* Free remaining structures */
1242                 free_graphs(out, outsize, spmin, spmax);
1243         }
1244 }
1245
1246 /*
1247  ***************************************************************************
1248  * Display memory statistics in SVG.
1249  *
1250  * IN:
1251  * @a           Activity structure with statistics.
1252  * @curr        Index in array for current sample statistics.
1253  * @action      Action expected from current function.
1254  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1255  *              flag indicating that a restart record has been previously
1256  *              found (.@restart) and a pointer on a record header structure
1257  *              (.@record_hdr) containing the first stats sample.
1258  * @itv         Interval of time in jiffies (only with F_MAIN action).
1259  * @record_hdr  Pointer on record header of current stats sample.
1260  ***************************************************************************
1261  */
1262 __print_funct_t svg_print_memory_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1263                                        unsigned long long itv, struct record_header *record_hdr)
1264 {
1265         struct stats_memory
1266                 *smc = (struct stats_memory *) a->buf[curr];
1267         int group1a[] = {2, 2};
1268         int group1b[] = {1, 1};
1269         int group1c[] = {4, 5};
1270         int group2a[] = {3};
1271         int group2b[] = {1, 1};
1272         char *title1a[] = {"Memory utilization (1)", "Memory utilization (2)"};
1273         char *title1b[] = {"Memory utilization (3)", "Memory utilization (4)"};
1274         char *title1c[] = {"Memory utilization (5)", "Memory utilization (6)"};
1275         char *title2a[] = {"Swap utilization (1)"};
1276         char *title2b[] = {"Swap utilization (2)", "Swap utilization (3)"};
1277         char *g_title1a[] = {"MBmemfree", "MBmemused",
1278                              "MBcached", "MBbuffers"};
1279         char *g_title1b[] = {"%memused", "%commit"};
1280         char *g_title1c[] = {"MBcommit", "MBactive", "MBinact", "MBdirty",
1281                              "MBanonpg", "MBslab", "MBkstack", "MBpgtbl", "MBvmused"};
1282         char *g_title2a[] = {"MBswpfree", "MBswpused", "MBswpcad"};
1283         char *g_title2b[] = {"%swpused", "%swpcad"};
1284         static double *spmin, *spmax;
1285         static char **out;
1286         static int *outsize;
1287         double tval;
1288         int i;
1289
1290         if (action & F_BEGIN) {
1291                 /*
1292                  * Allocate arrays that will contain the graphs data
1293                  * and the min/max values.
1294                  */
1295                 out = allocate_graph_lines(22, &outsize, &spmin, &spmax);
1296         }
1297
1298         if (action & F_MAIN) {
1299                 /* Check for min/max values */
1300                 save_extrema(0, 16, 0, (void *) a->buf[curr], NULL,
1301                              0, spmin, spmax);
1302                 /* Compute %memused min/max values */
1303                 tval = smc->tlmkb ? SP_VALUE(smc->frmkb, smc->tlmkb, smc->tlmkb) : 0.0;
1304                 if (tval > *(spmax + 16)) {
1305                         *(spmax + 16) = tval;
1306                 }
1307                 if (tval < *(spmin + 16)) {
1308                         *(spmin + 16) = tval;
1309                 }
1310                 /* Compute %commit min/max values */
1311                 tval = (smc->tlmkb + smc->tlskb) ?
1312                        SP_VALUE(0, smc->comkb, smc->tlmkb + smc->tlskb) : 0.0;
1313                 if (tval > *(spmax + 17)) {
1314                         *(spmax + 17) = tval;
1315                 }
1316                 if (tval < *(spmin + 17)) {
1317                         *(spmin + 17) = tval;
1318                 }
1319                 /* Compute %swpused min/max values */
1320                 tval = smc->tlskb ?
1321                        SP_VALUE(smc->frskb, smc->tlskb, smc->tlskb) : 0.0;
1322                 if (tval > *(spmax + 18)) {
1323                         *(spmax + 18) = tval;
1324                 }
1325                 if (tval < *(spmin + 18)) {
1326                         *(spmin + 18) = tval;
1327                 }
1328                 /* Compute %swpcad min/max values */
1329                 tval = (smc->tlskb - smc->frskb) ?
1330                        SP_VALUE(0, smc->caskb, smc->tlskb - smc->frskb) : 0.0;
1331                 if (tval > *(spmax + 19)) {
1332                         *(spmax + 19) = tval;
1333                 }
1334                 if (tval < *(spmin + 19)) {
1335                         *(spmin + 19) = tval;
1336                 }
1337                 /* Compute memused min/max values in MB */
1338                 tval = ((double) (smc->tlmkb - smc->frmkb)) / 1024;
1339                 if (tval > *(spmax + 20)) {
1340                         *(spmax + 20) = tval;
1341                 }
1342                 if (tval < *(spmin + 20)) {
1343                         *(spmin + 20) = tval;
1344                 }
1345                 /* Compute swpused min/max values in MB */
1346                 tval = ((double) (smc->tlskb - smc->frskb)) / 1024;
1347                 if (tval > *(spmax + 21)) {
1348                         *(spmax + 21) = tval;
1349                 }
1350                 if (tval < *(spmin + 21)) {
1351                         *(spmin + 21) = tval;
1352                 }
1353
1354                 /* MBmemfree */
1355                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1356                          ((double) smc->frmkb) / 1024,
1357                          out, outsize, svg_p->restart);
1358                 /* MBmemused */
1359                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1360                          ((double) (smc->tlmkb - smc->frmkb)) / 1024,
1361                          out + 1, outsize + 1, svg_p->restart);
1362                 /* MBcached */
1363                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1364                          ((double) smc->camkb) / 1024,
1365                           out + 2, outsize + 2, svg_p->restart);
1366                 /* MBbuffers */
1367                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1368                          ((double) smc->bufkb) / 1024,
1369                          out + 3, outsize + 3, svg_p->restart);
1370                 /* MBswpfree */
1371                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1372                          ((double) smc->frskb) / 1024,
1373                          out + 4, outsize + 4, svg_p->restart);
1374                 /* MBswpused */
1375                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1376                          ((double) (smc->tlskb - smc->frskb)) / 1024,
1377                          out + 5, outsize + 5, svg_p->restart);
1378                 /* MBswpcad */
1379                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1380                          ((double) smc->caskb) / 1024,
1381                          out + 6, outsize + 6, svg_p->restart);
1382                 /* MBcommit */
1383                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1384                          ((double) smc->comkb) / 1024,
1385                          out + 7, outsize + 7, svg_p->restart);
1386                 /* MBactive */
1387                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1388                          ((double) smc->activekb) / 1024,
1389                          out + 8, outsize + 8, svg_p->restart);
1390                 /* MBinact */
1391                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1392                          ((double) smc->inactkb) / 1024,
1393                          out + 9, outsize + 9, svg_p->restart);
1394                 /* MBdirty */
1395                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1396                          ((double) smc->dirtykb) / 1024,
1397                          out + 10, outsize + 10, svg_p->restart);
1398                 /* MBanonpg */
1399                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1400                          ((double) smc->anonpgkb) / 1024,
1401                          out + 11, outsize + 11, svg_p->restart);
1402                 /* MBslab */
1403                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1404                          ((double) smc->slabkb) / 1024,
1405                          out + 12, outsize + 12, svg_p->restart);
1406                 /* MBkstack */
1407                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1408                          ((double) smc->kstackkb) / 1024,
1409                          out + 13, outsize + 13, svg_p->restart);
1410                 /* MBpgtbl */
1411                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1412                          ((double) smc->pgtblkb) / 1024,
1413                          out + 14, outsize + 14, svg_p->restart);
1414                 /* MBvmused */
1415                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1416                          ((double) smc->vmusedkb) / 1024,
1417                          out + 15, outsize + 15, svg_p->restart);
1418                 /* %memused */
1419                 brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1420                          0.0,
1421                          smc->tlmkb ?
1422                          SP_VALUE(smc->frmkb, smc->tlmkb, smc->tlmkb) : 0.0,
1423                          out + 16, outsize + 16, svg_p->dt);
1424                 /* %commit */
1425                 brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1426                          0.0,
1427                          (smc->tlmkb + smc->tlskb) ?
1428                          SP_VALUE(0, smc->comkb, smc->tlmkb + smc->tlskb) : 0.0,
1429                          out + 17, outsize + 17, svg_p->dt);
1430                 /* %swpused */
1431                 brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1432                          0.0,
1433                          smc->tlskb ?
1434                          SP_VALUE(smc->frskb, smc->tlskb, smc->tlskb) : 0.0,
1435                          out + 18, outsize + 18, svg_p->dt);
1436                 /* %swpcad */
1437                 brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1438                          0.0,
1439                          (smc->tlskb - smc->frskb) ?
1440                          SP_VALUE(0, smc->caskb, smc->tlskb - smc->frskb) : 0.0,
1441                          out + 19, outsize + 19, svg_p->dt);
1442         }
1443
1444         if (action & F_END) {
1445
1446                 /* Conversion kB -> MB */
1447                 for (i = 0; i < 16; i++) {
1448                         *(spmin + i) /= 1024;
1449                         *(spmax + i) /= 1024;
1450                 }
1451
1452                 if (DISPLAY_MEM_AMT(a->opt_flags)) {
1453                         /* frmkb and tlmkb should be together because they will be drawn on the same view */
1454                         *(spmax + 3) = *(spmax + 1);
1455                         *(spmin + 3) = *(spmin + 1);
1456                         /* Move memused min/max values */
1457                         *(spmax + 1) = *(spmax + 20);
1458                         *(spmin + 1) = *(spmin + 20);
1459
1460                         draw_activity_graphs(2, SVG_LINE_GRAPH, title1a, g_title1a, NULL, group1a,
1461                                              spmin, spmax, out, outsize, svg_p, record_hdr);
1462                         draw_activity_graphs(2, SVG_BAR_GRAPH, title1b, g_title1b, NULL, group1b,
1463                                              spmin + 16, spmax + 16, out + 16, outsize + 16, svg_p, record_hdr);
1464                         draw_activity_graphs(DISPLAY_MEM_ALL(a->opt_flags) ? 2 : 1,
1465                                              SVG_LINE_GRAPH, title1c, g_title1c, NULL, group1c,
1466                                              spmin + 7, spmax + 7, out + 7, outsize + 7, svg_p, record_hdr);
1467                 }
1468
1469                 if (DISPLAY_SWAP(a->opt_flags)) {
1470                         /* Move swpused min/max values */
1471                         *(spmax + 5) = *(spmax + 21);
1472                         *(spmin + 5) = *(spmin + 21);
1473
1474                         draw_activity_graphs(1, SVG_LINE_GRAPH, title2a, g_title2a, NULL, group2a,
1475                                              spmin + 4, spmax + 4, out + 4, outsize + 4, svg_p, record_hdr);
1476                         draw_activity_graphs(2, SVG_BAR_GRAPH, title2b, g_title2b, NULL, group2b,
1477                                              spmin + 18, spmax + 18, out + 18, outsize + 18, svg_p, record_hdr);
1478                 }
1479
1480                 /* Free remaining structures */
1481                 free_graphs(out, outsize, spmin, spmax);
1482         }
1483 }
1484
1485 /*
1486  ***************************************************************************
1487  * Display queue and load statistics in SVG
1488  *
1489  * IN:
1490  * @a           Activity structure with statistics.
1491  * @curr        Index in array for current sample statistics.
1492  * @action      Action expected from current function.
1493  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1494  *              flag indicating that a restart record has been previously
1495  *              found (.@restart) and a pointer on a record header structure
1496  *              (.@record_hdr) containing the first stats sample.
1497  * @itv         Interval of time in jiffies (only with F_MAIN action).
1498  * @record_hdr  Pointer on record header of current stats sample.
1499  ***************************************************************************
1500  */
1501 __print_funct_t svg_print_queue_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1502                                       unsigned long long itv, struct record_header *record_hdr)
1503 {
1504         struct stats_queue
1505                 *sqc = (struct stats_queue *) a->buf[curr];
1506         int group[] = {2, 3, 1};
1507         char *title[] = {"Queue length", "Load average", "Task list"};
1508         char *g_title[] = {"~runq-sz", "~blocked",
1509                            "ldavg-1", "ldavg-5", "ldavg-15",
1510                            "~plist-sz"};
1511         static double *spmin, *spmax;
1512         static char **out;
1513         static int *outsize;
1514
1515         if (action & F_BEGIN) {
1516                 /*
1517                  * Allocate arrays that will contain the graphs data
1518                  * and the min/max values.
1519                  */
1520                 out = allocate_graph_lines(6, &outsize, &spmin, &spmax);
1521         }
1522
1523         if (action & F_MAIN) {
1524                 /* Check for min/max values */
1525                 save_extrema(0, 2, 4, (void *) a->buf[curr], NULL,
1526                              itv, spmin, spmax);
1527                 /* runq-sz */
1528                 lniappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1529                           (unsigned long) sqc->nr_running,
1530                           out, outsize, svg_p->restart);
1531                 /* blocked */
1532                 lniappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1533                           (unsigned long) sqc->procs_blocked,
1534                           out + 1, outsize + 1, svg_p->restart);
1535                 /* ldavg-1 */
1536                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1537                          (double) sqc->load_avg_1 / 100,
1538                          out + 2, outsize + 2, svg_p->restart);
1539                 /* ldavg-5 */
1540                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1541                          (double) sqc->load_avg_5 / 100,
1542                          out + 3, outsize + 3, svg_p->restart);
1543                 /* ldavg-15 */
1544                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1545                          (double) sqc->load_avg_15 / 100,
1546                          out + 4, outsize + 4, svg_p->restart);
1547                 /* plist-sz */
1548                 lniappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1549                           (unsigned long) sqc->nr_threads,
1550                           out + 5, outsize + 5, svg_p->restart);
1551         }
1552
1553         if (action & F_END) {
1554                 /* Fix min/max values for load average */
1555                 *(spmin + 2) /= 100; *(spmax + 2) /= 100;
1556                 *(spmin + 3) /= 100; *(spmax + 3) /= 100;
1557                 *(spmin + 4) /= 100; *(spmax + 4) /= 100;
1558
1559                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1560                                      spmin, spmax, out, outsize, svg_p, record_hdr);
1561
1562                 /* Free remaining structures */
1563                 free_graphs(out, outsize, spmin, spmax);
1564         }
1565 }
1566
1567 /*
1568  ***************************************************************************
1569  * Display network interfaces statistics in SVG
1570  *
1571  * IN:
1572  * @a           Activity structure with statistics.
1573  * @curr        Index in array for current sample statistics.
1574  * @action      Action expected from current function.
1575  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1576  *              flag indicating that a restart record has been previously
1577  *              found (.@restart) and a pointer on a record header structure
1578  *              (.@record_hdr) containing the first stats sample.
1579  * @itv         Interval of time in jiffies (only with F_MAIN action).
1580  * @record_hdr  Pointer on record header of current stats sample.
1581  ***************************************************************************
1582  */
1583 __print_funct_t svg_print_net_dev_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1584                                         unsigned long long itv, struct record_header *record_hdr)
1585 {
1586         struct stats_net_dev *sndc, *sndp;
1587         int group1[] = {2, 2, 3};
1588         int group2[] = {1};
1589         char *title1[] = {"Network statistics (1)", "Network statistics (2)",
1590                           "Network statistics (3)"};
1591         char *title2[] = {"Network statistics (4)"};
1592         char *g_title1[] = {"rxpck/s", "txpck/s",
1593                             "rxkB/s", "txkB/s",
1594                             "rxcmp/s", "txcmp/s", "rxmcst/s"};
1595         char *g_title2[] = {"%ifutil"};
1596         static double *spmin, *spmax;
1597         static char **out;
1598         static int *outsize;
1599         char *item_name;
1600         double rxkb, txkb, ifutil;
1601         int i, j, k, pos, restart, *unregistered;
1602
1603         if (action & F_BEGIN) {
1604                 /*
1605                  * Allocate arrays (#0..7) that will contain the graphs data
1606                  * and the min/max values.
1607                  * Also allocate one additional array (#8) for each interface:
1608                  * out + 8 will contain the interface name,
1609                  * outsize + 8 will contain a positive value (TRUE) if the interface
1610                  * has either still not been registered, or has been unregistered.
1611                  */
1612                 out = allocate_graph_lines(9 * a->nr, &outsize, &spmin, &spmax);
1613         }
1614
1615         if (action & F_MAIN) {
1616                 restart = svg_p->restart;
1617                 /*
1618                  * Mark previously registered interfaces as now
1619                  * possibly unregistered for all graphs.
1620                  */
1621                 for (k = 0; k < a->nr; k++) {
1622                         unregistered = outsize + k * 9 + 8;
1623                         if (*unregistered == FALSE) {
1624                                 *unregistered = MAYBE;
1625                         }
1626                 }
1627
1628                 /* For each network interfaces structure */
1629                 for (i = 0; i < a->nr; i++) {
1630                         sndc = (struct stats_net_dev *) ((char *) a->buf[curr] + i * a->msize);
1631                         if (!strcmp(sndc->interface, ""))
1632                                 /* Empty structure: Ignore it */
1633                                 continue;
1634
1635                         /* Look for corresponding graph */
1636                         for (k = 0; k < a->nr; k++) {
1637                                 item_name = *(out + k * 9 + 8);
1638                                 if (!strcmp(sndc->interface, item_name))
1639                                         /* Graph found! */
1640                                         break;
1641                         }
1642                         if (k == a->nr) {
1643                                 /* Graph not found: Look for first free entry */
1644                                 for (k = 0; k < a->nr; k++) {
1645                                         item_name = *(out + k * 9 + 8);
1646                                         if (!strcmp(item_name, ""))
1647                                                 break;
1648                                 }
1649                                 if (k == a->nr)
1650                                         /* No free graph entry: Graph for this item won't be drawn */
1651                                         continue;
1652                         }
1653
1654                         pos = k * 9;
1655                         unregistered = outsize + pos + 8;
1656
1657                         j = check_net_dev_reg(a, curr, !curr, i);
1658                         sndp = (struct stats_net_dev *) ((char *) a->buf[!curr] + j * a->msize);
1659
1660                         /*
1661                          * If current interface was marked as previously unregistered,
1662                          * then set restart variable to TRUE so that the graph will be
1663                          * discontinuous, and mark it as now registered.
1664                          */
1665                         if (*unregistered == TRUE) {
1666                                 restart = TRUE;
1667                         }
1668                         *unregistered = FALSE;
1669
1670                         if (!item_name[0]) {
1671                                 /* Save network interface name (if not already done) */
1672                                 strncpy(item_name, sndc->interface, CHUNKSIZE);
1673                                 item_name[CHUNKSIZE - 1] = '\0';
1674                         }
1675
1676                         /* Check for min/max values */
1677                         save_extrema(7, 0, 0, (void *) sndc, (void *) sndp,
1678                                      itv, spmin + pos, spmax + pos);
1679
1680                         rxkb = S_VALUE(sndp->rx_bytes, sndc->rx_bytes, itv);
1681                         txkb = S_VALUE(sndp->tx_bytes, sndc->tx_bytes, itv);
1682                         ifutil = compute_ifutil(sndc, rxkb, txkb);
1683                         if (ifutil < *(spmin + pos + 7)) {
1684                                 *(spmin + pos + 7) = ifutil;
1685                         }
1686                         if (ifutil > *(spmax + pos + 7)) {
1687                                 *(spmax + pos + 7) = ifutil;
1688                         }
1689
1690                         /* rxpck/s */
1691                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1692                                  S_VALUE(sndp->rx_packets, sndc->rx_packets, itv),
1693                                  out + pos, outsize + pos, restart);
1694
1695                         /* txpck/s */
1696                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1697                                  S_VALUE(sndp->tx_packets, sndc->tx_packets, itv),
1698                                  out + pos + 1, outsize + pos + 1, restart);
1699
1700                         /* rxkB/s */
1701                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1702                                  rxkb / 1024,
1703                                  out + pos + 2, outsize + pos + 2, restart);
1704
1705                         /* txkB/s */
1706                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1707                                  txkb / 1024,
1708                                  out + pos + 3, outsize + pos + 3, restart);
1709
1710                         /* rxcmp/s */
1711                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1712                                  S_VALUE(sndp->rx_compressed, sndc->rx_compressed, itv),
1713                                  out + pos + 4, outsize + pos + 4, restart);
1714
1715                         /* txcmp/s */
1716                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1717                                  S_VALUE(sndp->tx_compressed, sndc->tx_compressed, itv),
1718                                  out + pos + 5, outsize + pos + 5, restart);
1719
1720                         /* rxmcst/s */
1721                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1722                                  S_VALUE(sndp->multicast, sndc->multicast, itv),
1723                                  out + pos + 6, outsize + pos + 6, restart);
1724
1725                         /* %ifutil */
1726                         brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1727                                  0.0, ifutil,
1728                                  out + pos + 7, outsize + pos + 7, svg_p->dt);
1729                 }
1730
1731                 /* Mark interfaces not seen here as now unregistered */
1732                 for (k = 0; k < a->nr; k++) {
1733                         unregistered = outsize + k * 9 + 8;
1734                         if (*unregistered != FALSE) {
1735                                 *unregistered = TRUE;
1736                         }
1737                 }
1738         }
1739
1740         if (action & F_END) {
1741                 for (i = 0; i < a->nr; i++) {
1742                         /*
1743                          * Check if there is something to display.
1744                          * Don't test sndc->interface because maybe the network
1745                          * interface has been registered later.
1746                          */
1747                         pos = i * 9;
1748                         if (!**(out + pos))
1749                                 continue;
1750
1751                         /* Recalculate min and max values in kB, not in B */
1752                         *(spmin + pos + 2) /= 1024;
1753                         *(spmax + pos + 2) /= 1024;
1754                         *(spmin + pos + 3) /= 1024;
1755                         *(spmax + pos + 3) /= 1024;
1756
1757                         item_name = *(out + pos + 8);
1758                         draw_activity_graphs(a->g_nr - 1, SVG_LINE_GRAPH,
1759                                              title1, g_title1, item_name, group1,
1760                                              spmin + pos, spmax + pos, out + pos, outsize + pos,
1761                                              svg_p, record_hdr);
1762                         draw_activity_graphs(1, SVG_BAR_GRAPH,
1763                                              title2, g_title2, item_name, group2,
1764                                              spmin + pos + 7, spmax + pos + 7, out + pos + 7, outsize + pos + 7,
1765                                              svg_p, record_hdr);
1766                 }
1767
1768                 /* Free remaining structures */
1769                 free_graphs(out, outsize, spmin, spmax);
1770         }
1771 }
1772
1773 /*
1774  ***************************************************************************
1775  * Display CPU frequency statistics in SVG.
1776  *
1777  * IN:
1778  * @a           Activity structure with statistics.
1779  * @curr        Index in array for current sample statistics.
1780  * @action      Action expected from current function.
1781  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1782  *              flag indicating that a restart record has been previously
1783  *              found (.@restart) and a pointer on a record header structure
1784  *              (.@record_hdr) containing the first stats sample.
1785  * @itv         Interval of time in jiffies (only with F_MAIN action).
1786  * @record_hdr  Pointer on record header of current stats sample.
1787  ***************************************************************************
1788  */
1789 __print_funct_t svg_print_pwr_cpufreq_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1790                                             unsigned long long g_itv, struct record_header *record_hdr)
1791 {
1792         struct stats_pwr_cpufreq *spc, *spp;
1793         int group[] = {1};
1794         char *title[] = {"CPU frequency"};
1795         char *g_title[] = {"MHz"};
1796         static double *spmin, *spmax;
1797         static char **out;
1798         static int *outsize;
1799         char item_name[8];
1800         int i;
1801
1802         if (action & F_BEGIN) {
1803                 /*
1804                  * Allocate arrays that will contain the graphs data
1805                  * and the min/max values.
1806                  */
1807                 out = allocate_graph_lines(a->nr, &outsize, &spmin, &spmax);
1808         }
1809
1810         if (action & F_MAIN) {
1811                 /* For each CPU */
1812                 for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {
1813
1814                         spc = (struct stats_pwr_cpufreq *) ((char *) a->buf[curr]  + i * a->msize);
1815                         spp = (struct stats_pwr_cpufreq *) ((char *) a->buf[!curr]  + i * a->msize);
1816
1817                         /* Should current CPU (including CPU "all") be displayed? */
1818                         if (!(a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))))
1819                                 /* No */
1820                                 continue;
1821
1822                         /* MHz */
1823                         recappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1824                                   ((double) spp->cpufreq) / 100,
1825                                   ((double) spc->cpufreq) / 100,
1826                                   out + i, outsize + i, svg_p->restart, svg_p->dt,
1827                                   spmin + i, spmax + i);
1828                 }
1829         }
1830
1831         if (action & F_END) {
1832                 for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {
1833
1834                         /* Should current CPU (including CPU "all") be displayed? */
1835                         if (!(a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))))
1836                                 /* No */
1837                                 continue;
1838
1839                         if (!i) {
1840                                 /* This is CPU "all" */
1841                                 strcpy(item_name, "all");
1842                         }
1843                         else {
1844                                 sprintf(item_name, "%d", i - 1);
1845                         }
1846
1847                         draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH,
1848                                              title, g_title, item_name, group,
1849                                              spmin + i, spmax + i, out + i, outsize + i,
1850                                              svg_p, record_hdr);
1851                 }
1852
1853                 /* Free remaining structures */
1854                 free_graphs(out, outsize, spmin, spmax);
1855         }
1856 }
1857
1858 /*
1859  ***************************************************************************
1860  * Display fan statistics in SVG.
1861  *
1862  * IN:
1863  * @a           Activity structure with statistics.
1864  * @curr        Index in array for current sample statistics.
1865  * @action      Action expected from current function.
1866  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1867  *              flag indicating that a restart record has been previously
1868  *              found (.@restart) and a pointer on a record header structure
1869  *              (.@record_hdr) containing the first stats sample.
1870  * @itv         Interval of time in jiffies (only with F_MAIN action).
1871  * @record_hdr  Pointer on record header of current stats sample.
1872  ***************************************************************************
1873  */
1874 __print_funct_t svg_print_pwr_fan_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1875                                         unsigned long long g_itv, struct record_header *record_hdr)
1876 {
1877         struct stats_pwr_fan *spc, *spp;
1878         int group[] = {1};
1879         char *title[] = {"Fan speed"};
1880         char *g_title[] = {"~rpm"};
1881         static double *spmin, *spmax;
1882         static char **out;
1883         static int *outsize;
1884         char item_name[MAX_SENSORS_DEV_LEN + 8];
1885         int i;
1886
1887         if (action & F_BEGIN) {
1888                 /*
1889                  * Allocate arrays that will contain the graphs data
1890                  * and the min/max values.
1891                  */
1892                 out = allocate_graph_lines(a->nr, &outsize, &spmin, &spmax);
1893         }
1894
1895         if (action & F_MAIN) {
1896                 /* For each fan */
1897                 for (i = 0; i < a->nr; i++) {
1898
1899                         spc = (struct stats_pwr_fan *) ((char *) a->buf[curr]  + i * a->msize);
1900                         spp = (struct stats_pwr_fan *) ((char *) a->buf[!curr]  + i * a->msize);
1901
1902                         /* rpm */
1903                         recappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1904                                   (double) spp->rpm,
1905                                   (double) spc->rpm,
1906                                   out + i, outsize + i, svg_p->restart, svg_p->dt,
1907                                   spmin + i, spmax + i);
1908                 }
1909         }
1910
1911         if (action & F_END) {
1912                 for (i = 0; i < a->nr; i++) {
1913
1914                         spc = (struct stats_pwr_fan *) ((char *) a->buf[curr]  + i * a->msize);
1915
1916                         snprintf(item_name, MAX_SENSORS_DEV_LEN + 8, "%d: %s", i + 1, spc->device);
1917                         item_name[MAX_SENSORS_DEV_LEN + 7] = '\0';
1918
1919                         draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH,
1920                                              title, g_title, item_name, group,
1921                                              spmin + i, spmax + i, out + i, outsize + i,
1922                                              svg_p, record_hdr);
1923                 }
1924
1925                 /* Free remaining structures */
1926                 free_graphs(out, outsize, spmin, spmax);
1927         }
1928 }