]> granicus.if.org Git - sysstat/blob - svg_stats.c
b6110b9b3d8b2c8c8505a2e353a34da1816872ae
[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 I/O and transfer rate 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_io_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_io
1266                 *sic = (struct stats_io *) a->buf[curr],
1267                 *sip = (struct stats_io *) a->buf[!curr];
1268         int group[] = {3, 2};
1269         char *title[] = {"I/O and transfer rate statistics (1)", "I/O and transfer rate statistics (2)"};
1270         char *g_title[] = {"tps", "rtps", "wtps",
1271                            "bread/s", "bwrtn/s"};
1272         static double *spmin, *spmax;
1273         static char **out;
1274         static int *outsize;
1275
1276         if (action & F_BEGIN) {
1277                 /*
1278                  * Allocate arrays that will contain the graphs data
1279                  * and the min/max values.
1280                  */
1281                 out = allocate_graph_lines(5, &outsize, &spmin, &spmax);
1282         }
1283
1284         if (action & F_MAIN) {
1285                 /* Check for min/max values */
1286                 save_extrema(0, 5, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
1287                              itv, spmin, spmax);
1288
1289                 /* tps */
1290                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1291                          S_VALUE(sip->dk_drive, sic->dk_drive, itv),
1292                          out, outsize, svg_p->restart);
1293                 /* rtps */
1294                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1295                          S_VALUE(sip->dk_drive_rio,  sic->dk_drive_rio, itv),
1296                          out + 1, outsize + 1, svg_p->restart);
1297                 /* wtps */
1298                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1299                          S_VALUE(sip->dk_drive_wio,  sic->dk_drive_wio, itv),
1300                          out + 2, outsize + 2, svg_p->restart);
1301                 /* bread/s */
1302                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1303                          S_VALUE(sip->dk_drive_rblk, sic->dk_drive_rblk, itv),
1304                          out + 3, outsize + 3, svg_p->restart);
1305                 /* bwrtn/s */
1306                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1307                          S_VALUE(sip->dk_drive_wblk, sic->dk_drive_wblk, itv),
1308                          out + 4, outsize + 4, svg_p->restart);
1309         }
1310
1311         if (action & F_END) {
1312                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1313                                      spmin, spmax, out, outsize, svg_p, record_hdr);
1314
1315                 /* Free remaining structures */
1316                 free_graphs(out, outsize, spmin, spmax);
1317         }
1318 }
1319
1320 /*
1321  ***************************************************************************
1322  * Display memory statistics in SVG.
1323  *
1324  * IN:
1325  * @a           Activity structure with statistics.
1326  * @curr        Index in array for current sample statistics.
1327  * @action      Action expected from current function.
1328  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1329  *              flag indicating that a restart record has been previously
1330  *              found (.@restart) and a pointer on a record header structure
1331  *              (.@record_hdr) containing the first stats sample.
1332  * @itv         Interval of time in jiffies (only with F_MAIN action).
1333  * @record_hdr  Pointer on record header of current stats sample.
1334  ***************************************************************************
1335  */
1336 __print_funct_t svg_print_memory_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1337                                        unsigned long long itv, struct record_header *record_hdr)
1338 {
1339         struct stats_memory
1340                 *smc = (struct stats_memory *) a->buf[curr];
1341         int group1a[] = {2, 2};
1342         int group1b[] = {1, 1};
1343         int group1c[] = {4, 5};
1344         int group2a[] = {3};
1345         int group2b[] = {1, 1};
1346         char *title1a[] = {"Memory utilization (1)", "Memory utilization (2)"};
1347         char *title1b[] = {"Memory utilization (3)", "Memory utilization (4)"};
1348         char *title1c[] = {"Memory utilization (5)", "Memory utilization (6)"};
1349         char *title2a[] = {"Swap utilization (1)"};
1350         char *title2b[] = {"Swap utilization (2)", "Swap utilization (3)"};
1351         char *g_title1a[] = {"MBmemfree", "MBmemused",
1352                              "MBcached", "MBbuffers"};
1353         char *g_title1b[] = {"%memused", "%commit"};
1354         char *g_title1c[] = {"MBcommit", "MBactive", "MBinact", "MBdirty",
1355                              "MBanonpg", "MBslab", "MBkstack", "MBpgtbl", "MBvmused"};
1356         char *g_title2a[] = {"MBswpfree", "MBswpused", "MBswpcad"};
1357         char *g_title2b[] = {"%swpused", "%swpcad"};
1358         static double *spmin, *spmax;
1359         static char **out;
1360         static int *outsize;
1361         double tval;
1362         int i;
1363
1364         if (action & F_BEGIN) {
1365                 /*
1366                  * Allocate arrays that will contain the graphs data
1367                  * and the min/max values.
1368                  */
1369                 out = allocate_graph_lines(22, &outsize, &spmin, &spmax);
1370         }
1371
1372         if (action & F_MAIN) {
1373                 /* Check for min/max values */
1374                 save_extrema(0, 16, 0, (void *) a->buf[curr], NULL,
1375                              0, spmin, spmax);
1376                 /* Compute %memused min/max values */
1377                 tval = smc->tlmkb ? SP_VALUE(smc->frmkb, smc->tlmkb, smc->tlmkb) : 0.0;
1378                 if (tval > *(spmax + 16)) {
1379                         *(spmax + 16) = tval;
1380                 }
1381                 if (tval < *(spmin + 16)) {
1382                         *(spmin + 16) = tval;
1383                 }
1384                 /* Compute %commit min/max values */
1385                 tval = (smc->tlmkb + smc->tlskb) ?
1386                        SP_VALUE(0, smc->comkb, smc->tlmkb + smc->tlskb) : 0.0;
1387                 if (tval > *(spmax + 17)) {
1388                         *(spmax + 17) = tval;
1389                 }
1390                 if (tval < *(spmin + 17)) {
1391                         *(spmin + 17) = tval;
1392                 }
1393                 /* Compute %swpused min/max values */
1394                 tval = smc->tlskb ?
1395                        SP_VALUE(smc->frskb, smc->tlskb, smc->tlskb) : 0.0;
1396                 if (tval > *(spmax + 18)) {
1397                         *(spmax + 18) = tval;
1398                 }
1399                 if (tval < *(spmin + 18)) {
1400                         *(spmin + 18) = tval;
1401                 }
1402                 /* Compute %swpcad min/max values */
1403                 tval = (smc->tlskb - smc->frskb) ?
1404                        SP_VALUE(0, smc->caskb, smc->tlskb - smc->frskb) : 0.0;
1405                 if (tval > *(spmax + 19)) {
1406                         *(spmax + 19) = tval;
1407                 }
1408                 if (tval < *(spmin + 19)) {
1409                         *(spmin + 19) = tval;
1410                 }
1411                 /* Compute memused min/max values in MB */
1412                 tval = ((double) (smc->tlmkb - smc->frmkb)) / 1024;
1413                 if (tval > *(spmax + 20)) {
1414                         *(spmax + 20) = tval;
1415                 }
1416                 if (tval < *(spmin + 20)) {
1417                         *(spmin + 20) = tval;
1418                 }
1419                 /* Compute swpused min/max values in MB */
1420                 tval = ((double) (smc->tlskb - smc->frskb)) / 1024;
1421                 if (tval > *(spmax + 21)) {
1422                         *(spmax + 21) = tval;
1423                 }
1424                 if (tval < *(spmin + 21)) {
1425                         *(spmin + 21) = tval;
1426                 }
1427
1428                 /* MBmemfree */
1429                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1430                          ((double) smc->frmkb) / 1024,
1431                          out, outsize, svg_p->restart);
1432                 /* MBmemused */
1433                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1434                          ((double) (smc->tlmkb - smc->frmkb)) / 1024,
1435                          out + 1, outsize + 1, svg_p->restart);
1436                 /* MBcached */
1437                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1438                          ((double) smc->camkb) / 1024,
1439                           out + 2, outsize + 2, svg_p->restart);
1440                 /* MBbuffers */
1441                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1442                          ((double) smc->bufkb) / 1024,
1443                          out + 3, outsize + 3, svg_p->restart);
1444                 /* MBswpfree */
1445                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1446                          ((double) smc->frskb) / 1024,
1447                          out + 4, outsize + 4, svg_p->restart);
1448                 /* MBswpused */
1449                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1450                          ((double) (smc->tlskb - smc->frskb)) / 1024,
1451                          out + 5, outsize + 5, svg_p->restart);
1452                 /* MBswpcad */
1453                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1454                          ((double) smc->caskb) / 1024,
1455                          out + 6, outsize + 6, svg_p->restart);
1456                 /* MBcommit */
1457                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1458                          ((double) smc->comkb) / 1024,
1459                          out + 7, outsize + 7, svg_p->restart);
1460                 /* MBactive */
1461                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1462                          ((double) smc->activekb) / 1024,
1463                          out + 8, outsize + 8, svg_p->restart);
1464                 /* MBinact */
1465                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1466                          ((double) smc->inactkb) / 1024,
1467                          out + 9, outsize + 9, svg_p->restart);
1468                 /* MBdirty */
1469                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1470                          ((double) smc->dirtykb) / 1024,
1471                          out + 10, outsize + 10, svg_p->restart);
1472                 /* MBanonpg */
1473                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1474                          ((double) smc->anonpgkb) / 1024,
1475                          out + 11, outsize + 11, svg_p->restart);
1476                 /* MBslab */
1477                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1478                          ((double) smc->slabkb) / 1024,
1479                          out + 12, outsize + 12, svg_p->restart);
1480                 /* MBkstack */
1481                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1482                          ((double) smc->kstackkb) / 1024,
1483                          out + 13, outsize + 13, svg_p->restart);
1484                 /* MBpgtbl */
1485                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1486                          ((double) smc->pgtblkb) / 1024,
1487                          out + 14, outsize + 14, svg_p->restart);
1488                 /* MBvmused */
1489                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1490                          ((double) smc->vmusedkb) / 1024,
1491                          out + 15, outsize + 15, svg_p->restart);
1492                 /* %memused */
1493                 brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1494                          0.0,
1495                          smc->tlmkb ?
1496                          SP_VALUE(smc->frmkb, smc->tlmkb, smc->tlmkb) : 0.0,
1497                          out + 16, outsize + 16, svg_p->dt);
1498                 /* %commit */
1499                 brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1500                          0.0,
1501                          (smc->tlmkb + smc->tlskb) ?
1502                          SP_VALUE(0, smc->comkb, smc->tlmkb + smc->tlskb) : 0.0,
1503                          out + 17, outsize + 17, svg_p->dt);
1504                 /* %swpused */
1505                 brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1506                          0.0,
1507                          smc->tlskb ?
1508                          SP_VALUE(smc->frskb, smc->tlskb, smc->tlskb) : 0.0,
1509                          out + 18, outsize + 18, svg_p->dt);
1510                 /* %swpcad */
1511                 brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1512                          0.0,
1513                          (smc->tlskb - smc->frskb) ?
1514                          SP_VALUE(0, smc->caskb, smc->tlskb - smc->frskb) : 0.0,
1515                          out + 19, outsize + 19, svg_p->dt);
1516         }
1517
1518         if (action & F_END) {
1519
1520                 /* Conversion kB -> MB */
1521                 for (i = 0; i < 16; i++) {
1522                         *(spmin + i) /= 1024;
1523                         *(spmax + i) /= 1024;
1524                 }
1525
1526                 if (DISPLAY_MEM_AMT(a->opt_flags)) {
1527                         /* frmkb and tlmkb should be together because they will be drawn on the same view */
1528                         *(spmax + 3) = *(spmax + 1);
1529                         *(spmin + 3) = *(spmin + 1);
1530                         /* Move memused min/max values */
1531                         *(spmax + 1) = *(spmax + 20);
1532                         *(spmin + 1) = *(spmin + 20);
1533
1534                         draw_activity_graphs(2, SVG_LINE_GRAPH, title1a, g_title1a, NULL, group1a,
1535                                              spmin, spmax, out, outsize, svg_p, record_hdr);
1536                         draw_activity_graphs(2, SVG_BAR_GRAPH, title1b, g_title1b, NULL, group1b,
1537                                              spmin + 16, spmax + 16, out + 16, outsize + 16, svg_p, record_hdr);
1538                         draw_activity_graphs(DISPLAY_MEM_ALL(a->opt_flags) ? 2 : 1,
1539                                              SVG_LINE_GRAPH, title1c, g_title1c, NULL, group1c,
1540                                              spmin + 7, spmax + 7, out + 7, outsize + 7, svg_p, record_hdr);
1541                 }
1542
1543                 if (DISPLAY_SWAP(a->opt_flags)) {
1544                         /* Move swpused min/max values */
1545                         *(spmax + 5) = *(spmax + 21);
1546                         *(spmin + 5) = *(spmin + 21);
1547
1548                         draw_activity_graphs(1, SVG_LINE_GRAPH, title2a, g_title2a, NULL, group2a,
1549                                              spmin + 4, spmax + 4, out + 4, outsize + 4, svg_p, record_hdr);
1550                         draw_activity_graphs(2, SVG_BAR_GRAPH, title2b, g_title2b, NULL, group2b,
1551                                              spmin + 18, spmax + 18, out + 18, outsize + 18, svg_p, record_hdr);
1552                 }
1553
1554                 /* Free remaining structures */
1555                 free_graphs(out, outsize, spmin, spmax);
1556         }
1557 }
1558
1559 /*
1560  ***************************************************************************
1561  * Display queue and load statistics in SVG
1562  *
1563  * IN:
1564  * @a           Activity structure with statistics.
1565  * @curr        Index in array for current sample statistics.
1566  * @action      Action expected from current function.
1567  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1568  *              flag indicating that a restart record has been previously
1569  *              found (.@restart) and a pointer on a record header structure
1570  *              (.@record_hdr) containing the first stats sample.
1571  * @itv         Interval of time in jiffies (only with F_MAIN action).
1572  * @record_hdr  Pointer on record header of current stats sample.
1573  ***************************************************************************
1574  */
1575 __print_funct_t svg_print_queue_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1576                                       unsigned long long itv, struct record_header *record_hdr)
1577 {
1578         struct stats_queue
1579                 *sqc = (struct stats_queue *) a->buf[curr];
1580         int group[] = {2, 3, 1};
1581         char *title[] = {"Queue length", "Load average", "Task list"};
1582         char *g_title[] = {"~runq-sz", "~blocked",
1583                            "ldavg-1", "ldavg-5", "ldavg-15",
1584                            "~plist-sz"};
1585         static double *spmin, *spmax;
1586         static char **out;
1587         static int *outsize;
1588
1589         if (action & F_BEGIN) {
1590                 /*
1591                  * Allocate arrays that will contain the graphs data
1592                  * and the min/max values.
1593                  */
1594                 out = allocate_graph_lines(6, &outsize, &spmin, &spmax);
1595         }
1596
1597         if (action & F_MAIN) {
1598                 /* Check for min/max values */
1599                 save_extrema(0, 2, 4, (void *) a->buf[curr], NULL,
1600                              itv, spmin, spmax);
1601                 /* runq-sz */
1602                 lniappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1603                           (unsigned long) sqc->nr_running,
1604                           out, outsize, svg_p->restart);
1605                 /* blocked */
1606                 lniappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1607                           (unsigned long) sqc->procs_blocked,
1608                           out + 1, outsize + 1, svg_p->restart);
1609                 /* ldavg-1 */
1610                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1611                          (double) sqc->load_avg_1 / 100,
1612                          out + 2, outsize + 2, svg_p->restart);
1613                 /* ldavg-5 */
1614                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1615                          (double) sqc->load_avg_5 / 100,
1616                          out + 3, outsize + 3, svg_p->restart);
1617                 /* ldavg-15 */
1618                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1619                          (double) sqc->load_avg_15 / 100,
1620                          out + 4, outsize + 4, svg_p->restart);
1621                 /* plist-sz */
1622                 lniappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1623                           (unsigned long) sqc->nr_threads,
1624                           out + 5, outsize + 5, svg_p->restart);
1625         }
1626
1627         if (action & F_END) {
1628                 /* Fix min/max values for load average */
1629                 *(spmin + 2) /= 100; *(spmax + 2) /= 100;
1630                 *(spmin + 3) /= 100; *(spmax + 3) /= 100;
1631                 *(spmin + 4) /= 100; *(spmax + 4) /= 100;
1632
1633                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1634                                      spmin, spmax, out, outsize, svg_p, record_hdr);
1635
1636                 /* Free remaining structures */
1637                 free_graphs(out, outsize, spmin, spmax);
1638         }
1639 }
1640
1641 /*
1642  ***************************************************************************
1643  * Display network interfaces statistics in SVG
1644  *
1645  * IN:
1646  * @a           Activity structure with statistics.
1647  * @curr        Index in array for current sample statistics.
1648  * @action      Action expected from current function.
1649  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1650  *              flag indicating that a restart record has been previously
1651  *              found (.@restart) and a pointer on a record header structure
1652  *              (.@record_hdr) containing the first stats sample.
1653  * @itv         Interval of time in jiffies (only with F_MAIN action).
1654  * @record_hdr  Pointer on record header of current stats sample.
1655  ***************************************************************************
1656  */
1657 __print_funct_t svg_print_net_dev_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1658                                         unsigned long long itv, struct record_header *record_hdr)
1659 {
1660         struct stats_net_dev *sndc, *sndp;
1661         int group1[] = {2, 2, 3};
1662         int group2[] = {1};
1663         char *title1[] = {"Network statistics (1)", "Network statistics (2)",
1664                           "Network statistics (3)"};
1665         char *title2[] = {"Network statistics (4)"};
1666         char *g_title1[] = {"rxpck/s", "txpck/s",
1667                             "rxkB/s", "txkB/s",
1668                             "rxcmp/s", "txcmp/s", "rxmcst/s"};
1669         char *g_title2[] = {"%ifutil"};
1670         static double *spmin, *spmax;
1671         static char **out;
1672         static int *outsize;
1673         char *item_name;
1674         double rxkb, txkb, ifutil;
1675         int i, j, k, pos, restart, *unregistered;
1676
1677         if (action & F_BEGIN) {
1678                 /*
1679                  * Allocate arrays (#0..7) that will contain the graphs data
1680                  * and the min/max values.
1681                  * Also allocate one additional array (#8) for each interface:
1682                  * out + 8 will contain the interface name,
1683                  * outsize + 8 will contain a positive value (TRUE) if the interface
1684                  * has either still not been registered, or has been unregistered.
1685                  */
1686                 out = allocate_graph_lines(9 * a->nr, &outsize, &spmin, &spmax);
1687         }
1688
1689         if (action & F_MAIN) {
1690                 restart = svg_p->restart;
1691                 /*
1692                  * Mark previously registered interfaces as now
1693                  * possibly unregistered for all graphs.
1694                  */
1695                 for (k = 0; k < a->nr; k++) {
1696                         unregistered = outsize + k * 9 + 8;
1697                         if (*unregistered == FALSE) {
1698                                 *unregistered = MAYBE;
1699                         }
1700                 }
1701
1702                 /* For each network interfaces structure */
1703                 for (i = 0; i < a->nr; i++) {
1704                         sndc = (struct stats_net_dev *) ((char *) a->buf[curr] + i * a->msize);
1705                         if (!strcmp(sndc->interface, ""))
1706                                 /* Empty structure: Ignore it */
1707                                 continue;
1708
1709                         /* Look for corresponding graph */
1710                         for (k = 0; k < a->nr; k++) {
1711                                 item_name = *(out + k * 9 + 8);
1712                                 if (!strcmp(sndc->interface, item_name))
1713                                         /* Graph found! */
1714                                         break;
1715                         }
1716                         if (k == a->nr) {
1717                                 /* Graph not found: Look for first free entry */
1718                                 for (k = 0; k < a->nr; k++) {
1719                                         item_name = *(out + k * 9 + 8);
1720                                         if (!strcmp(item_name, ""))
1721                                                 break;
1722                                 }
1723                                 if (k == a->nr)
1724                                         /* No free graph entry: Graph for this item won't be drawn */
1725                                         continue;
1726                         }
1727
1728                         pos = k * 9;
1729                         unregistered = outsize + pos + 8;
1730
1731                         j = check_net_dev_reg(a, curr, !curr, i);
1732                         sndp = (struct stats_net_dev *) ((char *) a->buf[!curr] + j * a->msize);
1733
1734                         /*
1735                          * If current interface was marked as previously unregistered,
1736                          * then set restart variable to TRUE so that the graph will be
1737                          * discontinuous, and mark it as now registered.
1738                          */
1739                         if (*unregistered == TRUE) {
1740                                 restart = TRUE;
1741                         }
1742                         *unregistered = FALSE;
1743
1744                         if (!item_name[0]) {
1745                                 /* Save network interface name (if not already done) */
1746                                 strncpy(item_name, sndc->interface, CHUNKSIZE);
1747                                 item_name[CHUNKSIZE - 1] = '\0';
1748                         }
1749
1750                         /* Check for min/max values */
1751                         save_extrema(7, 0, 0, (void *) sndc, (void *) sndp,
1752                                      itv, spmin + pos, spmax + pos);
1753
1754                         rxkb = S_VALUE(sndp->rx_bytes, sndc->rx_bytes, itv);
1755                         txkb = S_VALUE(sndp->tx_bytes, sndc->tx_bytes, itv);
1756                         ifutil = compute_ifutil(sndc, rxkb, txkb);
1757                         if (ifutil < *(spmin + pos + 7)) {
1758                                 *(spmin + pos + 7) = ifutil;
1759                         }
1760                         if (ifutil > *(spmax + pos + 7)) {
1761                                 *(spmax + pos + 7) = ifutil;
1762                         }
1763
1764                         /* rxpck/s */
1765                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1766                                  S_VALUE(sndp->rx_packets, sndc->rx_packets, itv),
1767                                  out + pos, outsize + pos, restart);
1768
1769                         /* txpck/s */
1770                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1771                                  S_VALUE(sndp->tx_packets, sndc->tx_packets, itv),
1772                                  out + pos + 1, outsize + pos + 1, restart);
1773
1774                         /* rxkB/s */
1775                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1776                                  rxkb / 1024,
1777                                  out + pos + 2, outsize + pos + 2, restart);
1778
1779                         /* txkB/s */
1780                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1781                                  txkb / 1024,
1782                                  out + pos + 3, outsize + pos + 3, restart);
1783
1784                         /* rxcmp/s */
1785                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1786                                  S_VALUE(sndp->rx_compressed, sndc->rx_compressed, itv),
1787                                  out + pos + 4, outsize + pos + 4, restart);
1788
1789                         /* txcmp/s */
1790                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1791                                  S_VALUE(sndp->tx_compressed, sndc->tx_compressed, itv),
1792                                  out + pos + 5, outsize + pos + 5, restart);
1793
1794                         /* rxmcst/s */
1795                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1796                                  S_VALUE(sndp->multicast, sndc->multicast, itv),
1797                                  out + pos + 6, outsize + pos + 6, restart);
1798
1799                         /* %ifutil */
1800                         brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1801                                  0.0, ifutil,
1802                                  out + pos + 7, outsize + pos + 7, svg_p->dt);
1803                 }
1804
1805                 /* Mark interfaces not seen here as now unregistered */
1806                 for (k = 0; k < a->nr; k++) {
1807                         unregistered = outsize + k * 9 + 8;
1808                         if (*unregistered != FALSE) {
1809                                 *unregistered = TRUE;
1810                         }
1811                 }
1812         }
1813
1814         if (action & F_END) {
1815                 for (i = 0; i < a->nr; i++) {
1816                         /*
1817                          * Check if there is something to display.
1818                          * Don't test sndc->interface because maybe the network
1819                          * interface has been registered later.
1820                          */
1821                         pos = i * 9;
1822                         if (!**(out + pos))
1823                                 continue;
1824
1825                         /* Recalculate min and max values in kB, not in B */
1826                         *(spmin + pos + 2) /= 1024;
1827                         *(spmax + pos + 2) /= 1024;
1828                         *(spmin + pos + 3) /= 1024;
1829                         *(spmax + pos + 3) /= 1024;
1830
1831                         item_name = *(out + pos + 8);
1832                         draw_activity_graphs(a->g_nr - 1, SVG_LINE_GRAPH,
1833                                              title1, g_title1, item_name, group1,
1834                                              spmin + pos, spmax + pos, out + pos, outsize + pos,
1835                                              svg_p, record_hdr);
1836                         draw_activity_graphs(1, SVG_BAR_GRAPH,
1837                                              title2, g_title2, item_name, group2,
1838                                              spmin + pos + 7, spmax + pos + 7, out + pos + 7, outsize + pos + 7,
1839                                              svg_p, record_hdr);
1840                 }
1841
1842                 /* Free remaining structures */
1843                 free_graphs(out, outsize, spmin, spmax);
1844         }
1845 }
1846
1847 /*
1848  ***************************************************************************
1849  * Display CPU frequency statistics in SVG.
1850  *
1851  * IN:
1852  * @a           Activity structure with statistics.
1853  * @curr        Index in array for current sample statistics.
1854  * @action      Action expected from current function.
1855  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1856  *              flag indicating that a restart record has been previously
1857  *              found (.@restart) and a pointer on a record header structure
1858  *              (.@record_hdr) containing the first stats sample.
1859  * @itv         Interval of time in jiffies (only with F_MAIN action).
1860  * @record_hdr  Pointer on record header of current stats sample.
1861  ***************************************************************************
1862  */
1863 __print_funct_t svg_print_pwr_cpufreq_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1864                                             unsigned long long g_itv, struct record_header *record_hdr)
1865 {
1866         struct stats_pwr_cpufreq *spc, *spp;
1867         int group[] = {1};
1868         char *title[] = {"CPU frequency"};
1869         char *g_title[] = {"MHz"};
1870         static double *spmin, *spmax;
1871         static char **out;
1872         static int *outsize;
1873         char item_name[8];
1874         int i;
1875
1876         if (action & F_BEGIN) {
1877                 /*
1878                  * Allocate arrays that will contain the graphs data
1879                  * and the min/max values.
1880                  */
1881                 out = allocate_graph_lines(a->nr, &outsize, &spmin, &spmax);
1882         }
1883
1884         if (action & F_MAIN) {
1885                 /* For each CPU */
1886                 for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {
1887
1888                         spc = (struct stats_pwr_cpufreq *) ((char *) a->buf[curr]  + i * a->msize);
1889                         spp = (struct stats_pwr_cpufreq *) ((char *) a->buf[!curr]  + i * a->msize);
1890
1891                         /* Should current CPU (including CPU "all") be displayed? */
1892                         if (!(a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))))
1893                                 /* No */
1894                                 continue;
1895
1896                         /* MHz */
1897                         recappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1898                                   ((double) spp->cpufreq) / 100,
1899                                   ((double) spc->cpufreq) / 100,
1900                                   out + i, outsize + i, svg_p->restart, svg_p->dt,
1901                                   spmin + i, spmax + i);
1902                 }
1903         }
1904
1905         if (action & F_END) {
1906                 for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {
1907
1908                         /* Should current CPU (including CPU "all") be displayed? */
1909                         if (!(a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))))
1910                                 /* No */
1911                                 continue;
1912
1913                         if (!i) {
1914                                 /* This is CPU "all" */
1915                                 strcpy(item_name, "all");
1916                         }
1917                         else {
1918                                 sprintf(item_name, "%d", i - 1);
1919                         }
1920
1921                         draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH,
1922                                              title, g_title, item_name, group,
1923                                              spmin + i, spmax + i, out + i, outsize + i,
1924                                              svg_p, record_hdr);
1925                 }
1926
1927                 /* Free remaining structures */
1928                 free_graphs(out, outsize, spmin, spmax);
1929         }
1930 }
1931
1932 /*
1933  ***************************************************************************
1934  * Display fan statistics in SVG.
1935  *
1936  * IN:
1937  * @a           Activity structure with statistics.
1938  * @curr        Index in array for current sample statistics.
1939  * @action      Action expected from current function.
1940  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
1941  *              flag indicating that a restart record has been previously
1942  *              found (.@restart) and a pointer on a record header structure
1943  *              (.@record_hdr) containing the first stats sample.
1944  * @itv         Interval of time in jiffies (only with F_MAIN action).
1945  * @record_hdr  Pointer on record header of current stats sample.
1946  ***************************************************************************
1947  */
1948 __print_funct_t svg_print_pwr_fan_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1949                                         unsigned long long g_itv, struct record_header *record_hdr)
1950 {
1951         struct stats_pwr_fan *spc, *spp;
1952         int group[] = {1};
1953         char *title[] = {"Fan speed"};
1954         char *g_title[] = {"~rpm"};
1955         static double *spmin, *spmax;
1956         static char **out;
1957         static int *outsize;
1958         char item_name[MAX_SENSORS_DEV_LEN + 8];
1959         int i;
1960
1961         if (action & F_BEGIN) {
1962                 /*
1963                  * Allocate arrays that will contain the graphs data
1964                  * and the min/max values.
1965                  */
1966                 out = allocate_graph_lines(a->nr, &outsize, &spmin, &spmax);
1967         }
1968
1969         if (action & F_MAIN) {
1970                 /* For each fan */
1971                 for (i = 0; i < a->nr; i++) {
1972
1973                         spc = (struct stats_pwr_fan *) ((char *) a->buf[curr]  + i * a->msize);
1974                         spp = (struct stats_pwr_fan *) ((char *) a->buf[!curr]  + i * a->msize);
1975
1976                         /* rpm */
1977                         recappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1978                                   (double) spp->rpm,
1979                                   (double) spc->rpm,
1980                                   out + i, outsize + i, svg_p->restart, svg_p->dt,
1981                                   spmin + i, spmax + i);
1982                 }
1983         }
1984
1985         if (action & F_END) {
1986                 for (i = 0; i < a->nr; i++) {
1987
1988                         spc = (struct stats_pwr_fan *) ((char *) a->buf[curr]  + i * a->msize);
1989
1990                         snprintf(item_name, MAX_SENSORS_DEV_LEN + 8, "%d: %s", i + 1, spc->device);
1991                         item_name[MAX_SENSORS_DEV_LEN + 7] = '\0';
1992
1993                         draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH,
1994                                              title, g_title, item_name, group,
1995                                              spmin + i, spmax + i, out + i, outsize + i,
1996                                              svg_p, record_hdr);
1997                 }
1998
1999                 /* Free remaining structures */
2000                 free_graphs(out, outsize, spmin, spmax);
2001         }
2002 }
2003
2004 /*
2005  ***************************************************************************
2006  * Display temperature statistics in SVG.
2007  *
2008  * IN:
2009  * @a           Activity structure with statistics.
2010  * @curr        Index in array for current sample statistics.
2011  * @action      Action expected from current function.
2012  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
2013  *              flag indicating that a restart record has been previously
2014  *              found (.@restart) and a pointer on a record header structure
2015  *              (.@record_hdr) containing the first stats sample.
2016  * @itv         Interval of time in jiffies (only with F_MAIN action).
2017  * @record_hdr  Pointer on record header of current stats sample.
2018  ***************************************************************************
2019  */
2020 __print_funct_t svg_print_pwr_temp_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
2021                                          unsigned long long g_itv, struct record_header *record_hdr)
2022 {
2023         struct stats_pwr_temp *spc;
2024         int group[] = {1};
2025         char *title[] = {"Device temperature"};
2026         char *g1_title[] = {"~degC"};
2027         char *g2_title[] = {"%temp"};
2028         static double *spmin, *spmax;
2029         static char **out;
2030         static int *outsize;
2031         char item_name[MAX_SENSORS_DEV_LEN + 8];
2032         int i;
2033         double tval;
2034
2035         if (action & F_BEGIN) {
2036                 /*
2037                  * Allocate arrays that will contain the graphs data
2038                  * and the min/max values.
2039                  */
2040                 out = allocate_graph_lines(2 * a->nr, &outsize, &spmin, &spmax);
2041         }
2042
2043         if (action & F_MAIN) {
2044                 /* For each temperature  sensor */
2045                 for (i = 0; i < a->nr; i++) {
2046
2047                         spc = (struct stats_pwr_temp *) ((char *) a->buf[curr]  + i * a->msize);
2048
2049                         /* Look for min/max values */
2050                         if (spc->temp < *(spmin + 2 * i)) {
2051                                 *(spmin + 2 * i) = spc->temp;
2052                         }
2053                         if (spc->temp > *(spmax + 2 * i)) {
2054                                 *(spmax + 2 * i) = spc->temp;
2055                         }
2056                         tval = (spc->temp_max - spc->temp_min) ?
2057                                (spc->temp - spc->temp_min) / (spc->temp_max - spc->temp_min) * 100 :
2058                                0.0;
2059                         if (tval < *(spmin + 2 * i + 1)) {
2060                                 *(spmin + 2 * i + 1) = tval;
2061                         }
2062                         if (tval > *(spmax + 2 * i + 1)) {
2063                                 *(spmax + 2 * i + 1) = tval;
2064                         }
2065
2066                         /* degC */
2067                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
2068                                  (double) spc->temp,
2069                                  out + 2 * i, outsize + 2 * i, svg_p->restart);
2070                         /* %temp */
2071                         brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
2072                                  0.0, tval,
2073                                  out + 2 * i + 1, outsize + 2 * i + 1, svg_p->dt);
2074                 }
2075         }
2076
2077         if (action & F_END) {
2078                 for (i = 0; i < a->nr; i++) {
2079
2080                         spc = (struct stats_pwr_temp *) ((char *) a->buf[curr]  + i * a->msize);
2081
2082                         snprintf(item_name, MAX_SENSORS_DEV_LEN + 8, "%d: %s", i + 1, spc->device);
2083                         item_name[MAX_SENSORS_DEV_LEN + 7] = '\0';
2084
2085                         draw_activity_graphs(1, SVG_LINE_GRAPH,
2086                                              title, g1_title, item_name, group,
2087                                              spmin + 2 * i, spmax + 2 * i, out + 2 * i, outsize + 2 * i,
2088                                              svg_p, record_hdr);
2089                         draw_activity_graphs(1, SVG_BAR_GRAPH,
2090                                              title, g2_title, item_name, group,
2091                                              spmin + 2 * i + 1, spmax + 2 * i + 1,
2092                                              out + 2 * i + 1, outsize + 2 * i + 1,
2093                                              svg_p, record_hdr);
2094                 }
2095
2096                 /* Free remaining structures */
2097                 free_graphs(out, outsize, spmin, spmax);
2098         }
2099 }