]> granicus.if.org Git - sysstat/blob - svg_stats.c
Merge branch 'master' of https://github.com/stevekay/sysstat into stevekay-master
[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                              0x0000ff, 0xffbf00, 0x00ff00, 0x7030a0,
46                              0xffffbf, 0xffff00, 0xd60093, 0x00bfbf,
47                              0xcc3300, 0xbfbfbf, 0x666635, 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.
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                 val = S_VALUE(*llup, *lluc, itv);
91                 if (val < minv[m]) {
92                         minv[m] = val;
93                 }
94                 if (val > maxv[m]) {
95                         maxv[m] = val;
96                 }
97                 lluc = (unsigned long long *) ((char *) lluc + ULL_ALIGNMENT_WIDTH);
98                 llup = (unsigned long long *) ((char *) llup + ULL_ALIGNMENT_WIDTH);
99         }
100
101         /* Compare unsigned long fields */
102         luc = (unsigned long *) lluc;
103         lup = (unsigned long *) llup;
104         for (i = 0; i < lu_nr; i++, m++) {
105                 val = S_VALUE(*lup, *luc, itv);
106                 if (val < minv[m]) {
107                         minv[m] = val;
108                 }
109                 if (val > maxv[m]) {
110                         maxv[m] = val;
111                 }
112                 luc = (unsigned long *) ((char *) luc + UL_ALIGNMENT_WIDTH);
113                 lup = (unsigned long *) ((char *) lup + UL_ALIGNMENT_WIDTH);
114         }
115
116         /* Compare unsigned int fields */
117         uc = (unsigned int *) luc;
118         up = (unsigned int *) lup;
119         for (i = 0; i < u_nr; i++, m++) {
120                 val = S_VALUE(*up, *uc, itv);
121                 if (val < minv[m]) {
122                         minv[m] = val;
123                 }
124                 if (val > maxv[m]) {
125                         maxv[m] = val;
126                 }
127                 uc = (unsigned int *) ((char *) uc + U_ALIGNMENT_WIDTH);
128                 up = (unsigned int *) ((char *) up + U_ALIGNMENT_WIDTH);
129         }
130 }
131
132 /*
133  ***************************************************************************
134  * Find the min and max values of all the graphs that will be drawn in the
135  * same window. The graphs have their own min and max values in
136  * minv[pos...pos+n-1] and maxv[pos...pos+n-1]. 
137  *
138  * IN:
139  * @pos         Position in array for the first graph extrema value.
140  * @n           Number of graphs to scan.
141  * @minv        Array containing min values for graphs.
142  * @maxv        Array containing max values for graphs.
143  *
144  * OUT:
145  * @minv        minv[pos] is modified and contains the global min value found.
146  * @maxv        maxv[pos] is modified and contains the global max value found.
147  ***************************************************************************
148  */
149 void get_global_extrema(int pos, int n, double minv[], double maxv[])
150 {
151         int i;
152
153         for (i = 1; i < n; i++) {
154                 if (minv[pos + i] < minv[pos]) {
155                         minv[pos] = minv[pos + i];
156                 }
157                 if (maxv[pos + i] > maxv[pos]) {
158                         maxv[pos] = maxv[pos + i];
159                 }
160         }
161 }
162
163 /*
164  ***************************************************************************
165  * Allocate arrays used to save graphs data, min and max values.
166  * @n arrays of chars are allocated for @n graphs to draw. A pointer on this
167  * array is returned. This is equivalent to "char data[][n]" where each
168  * element is of indeterminate size and will contain the graph data (eg.
169  * << path d="M12,14 L13,16..." ... >>.
170  * The size of element data[i] is given by outsize[i].
171  * Also allocate an array to save min values (equivalent to "double spmin[n]")
172  * and an array for max values (equivalent to "double spmax[n]").
173  *
174  * IN:
175  * @n           Number of graphs to draw for current activity.
176  *
177  * OUT:
178  * @outsize     Array that will contain the sizes of each element in array
179  *              of chars. Equivalent to "int outsize[n]" with
180  *              outsize[n] = sizeof(data[][n]).
181  * @spmin       Array that will contain min values for current activity.
182  * @spmax       Array that will contain max values for current activity.
183  *
184  * RETURNS:
185  * Pointer on array of arrays of chars that will contain the graphs data.
186  *
187  * NB: @min and @max arrays contain values in the same order as the fields
188  * in the statistics structure.
189  ***************************************************************************
190  */
191 char **allocate_graph_lines(int n, int **outsize, double **spmin, double **spmax)
192 {
193         char **out;
194         char *out_p;
195         int i;
196
197         /*
198          * Allocate an array of pointers. Each of these pointers will
199          * be an array of chars.
200          */
201         if ((out = (char **) malloc(n * sizeof(char *))) == NULL) {
202                 perror("malloc");
203                 exit(4);
204         }
205         /* Allocate array that will contain the size of each array of chars */
206         if ((*outsize = (int *) malloc(n * sizeof(int))) == NULL) {
207                 perror("malloc");
208                 exit(4);
209         }
210         /* Allocate array that will contain the min value of each graph */
211         if ((*spmin = (double *) malloc(n * sizeof(double))) == NULL) {
212                 perror("malloc");
213                 exit(4);
214         }
215         /* Allocate array that will contain the max value of each graph */
216         if ((*spmax = (double *) malloc(n * sizeof(double))) == NULL) {
217                 perror("malloc");
218                 exit(4);
219         }
220         /* Allocate arrays of chars that will contain graphs data */
221         for (i = 0; i < n; i++) {
222                 if ((out_p = (char *) malloc(CHUNKSIZE * sizeof(char))) == NULL) {
223                         perror("malloc");
224                         exit(4);
225                 }
226                 *(out + i) = out_p;
227                 *out_p = '\0';                  /* Reset string so that it can be safely strncat()'d later */
228                 *(*outsize + i) = CHUNKSIZE;    /* Each array of chars has a default size of CHUNKSIZE */
229                 *(*spmin + i) = DBL_MAX;        /* Init min and max values */
230                 *(*spmax + i) = -DBL_MAX;
231         }
232
233         return out;
234 }
235
236 /*
237  ***************************************************************************
238  * Update graph definition by appending current X,Y coordinates.
239  *
240  * IN:
241  * @timetag     Timestamp in seconds since the epoch for current sample
242  *              stats. Will be used as X coordinate.
243  * @value       Value of current sample metric. Will be used as Y coordinate.
244  * @out         Pointer on array of chars for current graph definition.
245  * @outsize     Size of array of chars for current graph definition.
246  * @restart     Set to TRUE if a RESTART record has been read since the last
247  *              statistics sample.
248  * @dt          Interval of time in seconds between current and previous
249  *              sample.
250  * @g_type      Graph type: SVG_LINE_GRAPH or SVG_BAR_GRAPH (for %values).
251  *
252  * OUT:
253  * @out         Pointer on array of chars for current graph definition that
254  *              has been updated with the addition of current sample data.
255  * @outsize     Array that containing the (possibly new) sizes of each
256  *              element in array of chars.
257  ***************************************************************************
258  */
259 void lnappend(unsigned long timetag, double value, char **out, int *outsize, int restart,
260               unsigned long dt, int g_type)
261 {
262         char point[128];
263         char *out_p;
264         int len;
265
266         /* Prepare additional graph definition data */
267         if (g_type == SVG_LINE_GRAPH) {
268                 snprintf(point, 128, " %c%lu,%.2f", restart ? 'M' : 'L', timetag, value);
269         }
270         else {
271                 if (value == 0.0)
272                         /* Dont draw a flat rectangle! */
273                         return;
274                 snprintf(point, 128, "<rect x=\"%lu\" y=\"0\" height=\"%.2f\" width=\"%lu\"/>",
275                          timetag - dt, MINIMUM(value, 100.0), dt);
276         }
277         point[127] = '\0';
278         out_p = *out;
279         len = *outsize - strlen(out_p) - 1;
280         if (strlen(point) >= len) {
281                 /*
282                  * If current array of chars doesn't have enough space left
283                  * then reallocate it with CHUNKSIZE more bytes.
284                  */
285                 SREALLOC(out_p, char, *outsize + CHUNKSIZE);
286                 *out = out_p;
287                 *outsize += CHUNKSIZE;
288         }
289         strncat(out_p, point, len);
290 }
291
292 /*
293  ***************************************************************************
294  * Calculate the value on the Y axis between two horizontal lines that will
295  * make the graph background grid.
296  *
297  * IN:
298  * @lmax        Max value reached for this graph.
299  *
300  * OUT:
301  * @dp          Number of decimal places for Y graduations.
302  *
303  * RETURNS:
304  * Value between two horizontal lines.
305  ***************************************************************************
306  */
307 double ygrid(double lmax, int *dp)
308 {
309         char val[32];
310         int l, i, e = 1;
311         long n = 0;
312
313         *dp = 0;
314         if (lmax == 0) {
315                 lmax = 1;
316         }
317         n = (long) (lmax / SVG_H_GRIDNR);
318         if (!n) {
319                 *dp = 2;
320                 return (lmax / SVG_H_GRIDNR);
321         }
322         snprintf(val, 32, "%ld", n);
323         val[31] = '\0';
324         l = strlen(val);
325         if (l < 2)
326                 return n;
327         for (i = 1; i < l; i++) {
328                 e = e * 10;
329         }
330         return ((double) (((long) (n / e)) * e));
331 }
332
333 /*
334  ***************************************************************************
335  * Calculate the value on the X axis between two vertical lines that will
336  * make the graph background grid.
337  *
338  * IN:
339  * @timestart   First data timestamp (X coordinate of the first data point).
340  * @timeend     Last data timestamp (X coordinate of the last data point).
341  *
342  * RETURNS:
343  * Value between two vertical lines.
344  ***************************************************************************
345  */
346 long int xgrid(unsigned long timestart, unsigned long timeend)
347 {
348         return ((timeend - timestart) / SVG_V_GRIDNR);
349 }
350
351 /*
352  ***************************************************************************
353  * Free global graphs structures.
354  *
355  * IN:
356  * @out         Pointer on array of chars for each graph definition.
357  * @outsize     Size of array of chars for each graph definition.
358  * @spmin       Array containing min values for graphs.
359  * @spmax       Array containing max values for graphs.
360  ***************************************************************************
361  */
362 void free_graphs(char **out, int *outsize, double *spmin, double *spmax)
363 {
364         if (out) {
365                 free(out);
366         }
367         if (outsize) {
368                 free(outsize);
369         }
370         if (spmin) {
371                 free(spmin);
372         }
373         if (spmax) {
374                 free(spmax);
375         }
376 }
377
378 /*
379  ***************************************************************************
380  * Display all graphs for current activity.
381  *
382  * IN:
383  * @g_nr        Number of graphs to display.
384  * @g_type      Type of graph (SVG_LINE_GRAPH, SVG_BAR_GRAPH).
385  * @title       Titles for each set of graphs.
386  * @g_title     Titles for each graph.
387  * @item_name   Item (network interface, etc.) name.
388  * @group       Indicate how graphs are grouped together to make sets.
389  * @spmin       Array containing min values for graphs.
390  * @spmax       Array containing max values for graphs.
391  * @out         Pointer on array of chars for each graph definition.
392  * @outsize     Size of array of chars for each graph definition.
393  * @svg_p       SVG specific parameters: Current graph number (.@graph_no)
394  *              and a pointer on a record header structure (.@record_hdr)
395  *              containing the first stats sample.
396  * @record_hdr  Pointer on record header of current stats sample.
397  ***************************************************************************
398  */
399 void draw_activity_graphs(int g_nr, int g_type, char *title[], char *g_title[], char *item_name,
400                           int group[], double *spmin, double *spmax, char **out, int *outsize,
401                           struct svg_parm *svg_p, struct record_header *record_hdr)
402 {
403         struct record_header stamp;
404         struct tm rectime;
405         char *out_p;
406         int i, j, dp, pos = 0;
407         long int k;
408         double lmax, xfactor, yfactor, ypos;
409         char cur_time[32];
410
411         /* Translate to proper position for current activity */
412         printf("<g id=\"g%d\" transform=\"translate(0,%d)\">\n",
413                svg_p->graph_no,
414                SVG_H_YSIZE + svg_p->graph_no * SVG_T_YSIZE);
415
416         /* For each set of graphs which are part of current activity */
417         for (i = 0; i < g_nr; i++) {
418
419                 /* Graph background */
420                 printf("<rect x=\"0\" y=\"%d\" height=\"%d\" width=\"%d\"/>\n",
421                        i * SVG_T_YSIZE,
422                        SVG_V_YSIZE, SVG_V_XSIZE);
423
424                 /* Graph title */
425                 printf("<text x=\"0\" y=\"%d\" style=\"fill: yellow; stroke: none\">%s",
426                        20 + i * SVG_T_YSIZE, title[i]);
427                 if (item_name) {
428                         printf(" [%s]", item_name);
429                 }
430                 printf("\n");
431                 printf("<tspan x=\"%d\" y=\"%d\" style=\"fill: yellow; stroke: none; font-size: 12px\">"
432                        "(Min, Max values)</tspan>\n</text>\n",
433                        5 + SVG_M_XSIZE + SVG_G_XSIZE,
434                        25 + i * SVG_T_YSIZE);
435
436                 /*
437                  * At least two samples are needed.
438                  * And a min and max value should have been found.
439                  */
440                 if ((record_hdr->ust_time == svg_p->record_hdr->ust_time) ||
441                     (*(spmin + i) == DBL_MAX) || (*(spmax + i) == -DBL_MIN)) {
442                         /* No data found */
443                         printf("<text x=\"0\" y=\"%d\" style=\"fill: red; stroke: none\">No data</text>\n",
444                                SVG_M_YSIZE + i * SVG_T_YSIZE);
445                         continue;
446                 }
447
448                 /* X and Y axis */
449                 printf("<polyline points=\"%d,%d %d,%d %d,%d\" stroke=\"white\" stroke-width=\"2\"/>\n",
450                        SVG_M_XSIZE, SVG_M_YSIZE + i * SVG_T_YSIZE,
451                        SVG_M_XSIZE, SVG_M_YSIZE + SVG_G_YSIZE + i * SVG_T_YSIZE,
452                        SVG_M_XSIZE + SVG_G_XSIZE, SVG_M_YSIZE + SVG_G_YSIZE + i * SVG_T_YSIZE);
453
454                 /* Caption */
455                 for (j = 0; j < group[i]; j++) {
456                         printf("<text x=\"%d\" y=\"%d\" style=\"fill: #%06x; stroke: none; font-size: 12px\">"
457                                "%s (%.2f, %.2f)</text>\n",
458                                5 + SVG_M_XSIZE + SVG_G_XSIZE, SVG_M_YSIZE + i * SVG_T_YSIZE + j * 15,
459                                svg_colors[(pos + j) & SVG_COLORS_IDX_MASK], g_title[pos + j],
460                        *(spmin + pos + j), *(spmax + pos + j));
461                 }
462
463                 /* Get global min and max value for current set of graphs */
464                 get_global_extrema(pos, group[i], spmin, spmax);
465
466                 /* Translate to proper position for current graph within current activity */
467                 printf("<g transform=\"translate(%d,%d)\">\n",
468                        SVG_M_XSIZE, SVG_M_YSIZE + SVG_G_YSIZE + i * SVG_T_YSIZE);
469
470                 /* Grid */
471                 if (g_type == SVG_LINE_GRAPH) {
472                         /* For line graphs */
473                         if (*(spmax + pos) == 0) {
474                                 /* If all values are zero then set current max value to 1 */
475                                 lmax = 1.0;
476                         }
477                         else {
478                                 lmax = *(spmax + pos);
479                         }
480                         ypos = ygrid(*(spmax + pos), &dp);
481                 }
482                 else {
483                         /* For bar graphs (used for %values) */
484                         lmax = 100.0;   /* Max is always 100% */
485                         ypos = 25.0;    /* Draw lines at 25%, 50%, 75% and 100% */
486                 }
487                 yfactor = (double) -SVG_G_YSIZE / lmax;
488                 j = 1;
489                 do {
490                         printf("<polyline points=\"0,%.2f %d,%.2f\" style=\"vector-effect: non-scaling-stroke; "
491                                "stroke: #202020\" transform=\"scale(1,%f)\"/>\n",
492                                ypos * j, SVG_G_XSIZE, ypos * j, yfactor);
493                         j++;
494                 }
495                 while (ypos * j <= lmax);
496                 j = 0;
497                 do {
498                         printf("<text x=\"0\" y=\"%ld\" style=\"fill: white; stroke: none; font-size: 12px; "
499                                "text-anchor: end\">%.*f.</text>\n",
500                                (long) (ypos * j * yfactor), dp, ypos * j);
501                         j++;
502                 }
503                 while (ypos * j <= lmax);
504
505                 k = xgrid(svg_p->record_hdr->ust_time, record_hdr->ust_time);
506                 xfactor = (double) SVG_G_XSIZE / (record_hdr->ust_time - svg_p->record_hdr->ust_time);
507                 stamp = *svg_p->record_hdr;
508                 for (j = 1; j <= SVG_V_GRIDNR; j++) {
509                         printf("<polyline points=\"%ld,0 %ld,%d\" style=\"vector-effect: non-scaling-stroke; "
510                                "stroke: #202020\" transform=\"scale(%f,1)\"/>\n",
511                                k * j, k * j, -SVG_G_YSIZE, xfactor);
512                 }
513                 for (j = 0; j <= SVG_V_GRIDNR; j++) {
514                         sa_get_record_timestamp_struct(flags, &stamp, &rectime, NULL);
515                         set_record_timestamp_string(flags, &stamp, NULL, cur_time, 32, &rectime);
516                         printf("<text x=\"%ld\" y=\"10\" style=\"fill: white; stroke: none; font-size: 12px; "
517                                "text-anchor: start\" transform=\"rotate(45,%ld,0)\">%s</text>\n",
518                                (long) (k * j * xfactor), (long) (k * j * xfactor), cur_time);
519                         stamp.ust_time += k;
520                 }
521                 if (!PRINT_LOCAL_TIME(flags)) {
522                         printf("<text x=\"-10\" y=\"30\" style=\"fill: yellow; stroke: none; font-size: 12px; "
523                                "text-anchor: end\">UTC</text>\n");
524                 }
525
526                 /* Draw current graphs set */
527                 for (j = 0; j < group[i]; j++) {
528                         out_p = *(out + pos + j);
529                         if (g_type == SVG_LINE_GRAPH) {
530                                 /* Line graphs */
531                                 printf("<path id=\"g%dp%d\" d=\"%s\" "
532                                        "style=\"vector-effect: non-scaling-stroke; "
533                                        "stroke: #%06x; stroke-width: 1; fill-opacity: 0\" "
534                                        "transform=\"scale(%f,%f)\"/>\n",
535                                        svg_p->graph_no, pos + j, out_p,
536                                        svg_colors[(pos + j) & SVG_COLORS_IDX_MASK],
537                                        xfactor,
538                                        yfactor);
539                         }
540                         else {
541                                 /* Bar graphs */
542                                 printf("<g style=\"fill: #%06x; stroke: none\" transform=\"scale(%f,%f)\">\n",
543                                        svg_colors[(pos + j) & SVG_COLORS_IDX_MASK], xfactor, yfactor);
544                                 printf("%s\n", out_p);
545                                 printf("</g>\n");
546                         }
547                         free(out_p);
548                 }
549                 printf("</g>\n");
550                 pos += group[i];
551         }
552         printf("</g>\n");
553
554         /* Next graph */
555         (svg_p->graph_no) += g_nr;
556 }
557
558 /*
559  ***************************************************************************
560  * Display task creation and context switch statistics in SVG
561  *
562  * IN:
563  * @a           Activity structure with statistics.
564  * @curr        Index in array for current sample statistics.
565  * @action      Action expected from current function.
566  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
567  *              flag indicating that a restart record has been previously
568  *              found (.@restart) and a pointer on a record header structure
569  *              (.@record_hdr) containing the first stats sample.
570  * @itv         Interval of time in jiffies (only with F_MAIN action).
571  * @record_hdr  Pointer on record header of current stats sample.
572  ***************************************************************************
573  */
574 __print_funct_t svg_print_pcsw_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
575                                      unsigned long long itv, struct record_header *record_hdr)
576 {
577         struct stats_pcsw
578                 *spc = (struct stats_pcsw *) a->buf[curr],
579                 *spp = (struct stats_pcsw *) a->buf[!curr];
580         int group[] = {1, 1};
581         char *title[] = {"Switching activity", "Task creation"};
582         char *g_title[] = {"cswch/s",
583                            "proc/s"};
584         static double *spmin, *spmax;
585         static char **out;
586         static int *outsize;
587
588         if (action & F_BEGIN) {
589                 /*
590                  * Allocate arrays that will contain the graphs data
591                  * and the min/max values.
592                  */
593                 out = allocate_graph_lines(2, &outsize, &spmin, &spmax);
594         }
595
596         if (action & F_MAIN) {
597                 /* Check for min/max values */
598                 save_extrema(1, 1, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
599                              itv, spmin, spmax);
600                 /* cswch/s */
601                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
602                          S_VALUE(spp->context_switch, spc->context_switch, itv),
603                          out, outsize, svg_p->restart, svg_p->dt, SVG_LINE_GRAPH);
604                 /* proc/s */
605                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
606                          S_VALUE(spp->processes, spc->processes, itv),
607                          out + 1, outsize + 1, svg_p->restart, svg_p->dt, SVG_LINE_GRAPH);
608         }
609
610         if (action & F_END) {
611                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
612                                      spmin, spmax, out, outsize, svg_p, record_hdr);
613
614                 /* Free remaining structures */
615                 free_graphs(out, outsize, spmin, spmax);
616         }
617 }
618
619 /*
620  ***************************************************************************
621  * Display swap statistics in SVG
622  *
623  * IN:
624  * @a           Activity structure with statistics.
625  * @curr        Index in array for current sample statistics.
626  * @action      Action expected from current function.
627  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
628  *              flag indicating that a restart record has been previously
629  *              found (.@restart) and a pointer on a record header structure
630  *              (.@record_hdr) containing the first stats sample.
631  * @itv         Interval of time in jiffies (only with F_MAIN action).
632  * @record_hdr  Pointer on record header of current stats sample.
633  ***************************************************************************
634  */
635 __print_funct_t svg_print_swap_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
636                                        unsigned long long itv, struct record_header *record_hdr)
637 {
638         struct stats_swap
639                 *ssc = (struct stats_swap *) a->buf[curr],
640                 *ssp = (struct stats_swap *) a->buf[!curr];
641         int group[] = {2};
642         char *title[] = {"Swap activity"};
643         char *g_title[] = {"pswpin/s", "pswpout/s" };
644         static double *spmin, *spmax;
645         static char **out;
646         static int *outsize;
647
648         if (action & F_BEGIN) {
649                 /*
650                  * Allocate arrays that will contain the graphs data
651                  * and the min/max values.
652                  */
653                 out = allocate_graph_lines(2, &outsize, &spmin, &spmax);
654         }
655
656         if (action & F_MAIN) {
657                 /* Check for min/max values */
658                 save_extrema(0, 2, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
659                              itv, spmin, spmax);
660                 /* pswpin/s */
661                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
662                          S_VALUE(ssp->pswpin, ssc->pswpin, itv),
663                          out, outsize, svg_p->restart);
664                 /* pswpout/s */
665                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
666                          S_VALUE(ssp->pswpout, ssc->pswpout, itv),
667                          out + 1, outsize + 1, svg_p->restart);
668         }
669
670         if (action & F_END) {
671                 draw_activity_graphs(a, title, g_title, NULL, group, spmin, spmax,
672                                      out, outsize, svg_p, record_hdr);
673
674                 /* Free remaining structures */
675                 free_graphs(out, outsize, spmin, spmax);
676         }
677 }
678
679
680 /*
681  ***************************************************************************
682  * Display paging statistics in SVG
683  *
684  * IN:
685  * @a           Activity structure with statistics.
686  * @curr        Index in array for current sample statistics.
687  * @action      Action expected from current function.
688  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
689  *              flag indicating that a restart record has been previously
690  *              found (.@restart) and a pointer on a record header structure
691  *              (.@record_hdr) containing the first stats sample.
692  * @itv         Interval of time in jiffies (only with F_MAIN action).
693  * @record_hdr  Pointer on record header of current stats sample.
694  ***************************************************************************
695  */
696 __print_funct_t svg_print_paging_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
697                                        unsigned long long itv, struct record_header *record_hdr)
698 {
699         struct stats_paging
700                 *spc = (struct stats_paging *) a->buf[curr],
701                 *spp = (struct stats_paging *) a->buf[!curr];
702         int group[] = {2, 2, 4};
703         char *title[] = {"Paging activity (1)", "Paging activity (2)", "Paging activity (3)"};
704         char *g_title[] = {"pgpgin/s", "pgpgout/s",
705                            "fault/s", "majflt/s",
706                            "pgfree/s", "pgscank/s", "pgscand/s", "pgsteal/s"};
707         static double *spmin, *spmax;
708         static char **out;
709         static int *outsize;
710
711         if (action & F_BEGIN) {
712                 /*
713                  * Allocate arrays that will contain the graphs data
714                  * and the min/max values.
715                  */
716                 out = allocate_graph_lines(8, &outsize, &spmin, &spmax);
717         }
718
719         if (action & F_MAIN) {
720                 /* Check for min/max values */
721                 save_extrema(0, 8, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
722                              itv, spmin, spmax);
723                 /* pgpgin/s */
724                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
725                          S_VALUE(spp->pgpgin, spc->pgpgin, itv),
726                          out, outsize, svg_p->restart, svg_p->dt, SVG_LINE_GRAPH);
727                 /* pgpgout/s */
728                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
729                          S_VALUE(spp->pgpgout, spc->pgpgout, itv),
730                          out + 1, outsize + 1, svg_p->restart, svg_p->dt, SVG_LINE_GRAPH);
731                 /* fault/s */
732                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
733                          S_VALUE(spp->pgfault, spc->pgfault, itv),
734                          out + 2, outsize + 2, svg_p->restart, svg_p->dt, SVG_LINE_GRAPH);
735                 /* majflt/s */
736                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
737                          S_VALUE(spp->pgmajfault, spc->pgmajfault, itv),
738                          out + 3, outsize + 3, svg_p->restart, svg_p->dt, SVG_LINE_GRAPH);
739                 /* pgfree/s */
740                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
741                          S_VALUE(spp->pgfree, spc->pgfree, itv),
742                          out + 4, outsize + 4, svg_p->restart, svg_p->dt, SVG_LINE_GRAPH);
743                 /* pgscank/s */
744                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
745                          S_VALUE(spp->pgscan_kswapd, spc->pgscan_kswapd, itv),
746                          out + 5, outsize + 5, svg_p->restart, svg_p->dt, SVG_LINE_GRAPH);
747                 /* pgscand/s */
748                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
749                          S_VALUE(spp->pgscan_direct, spc->pgscan_direct, itv),
750                          out + 6, outsize + 6, svg_p->restart, svg_p->dt, SVG_LINE_GRAPH);
751                 /* pgsteal/s */
752                 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
753                          S_VALUE(spp->pgsteal, spc->pgsteal, itv),
754                          out + 7, outsize + 7, svg_p->restart, svg_p->dt, SVG_LINE_GRAPH);
755         }
756
757         if (action & F_END) {
758                 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
759                                      spmin, spmax, out, outsize, svg_p, record_hdr);
760
761                 /* Free remaining structures */
762                 free_graphs(out, outsize, spmin, spmax);
763         }
764 }
765
766 /*
767  ***************************************************************************
768  * Display network interfaces statistics in SVG
769  *
770  * IN:
771  * @a           Activity structure with statistics.
772  * @curr        Index in array for current sample statistics.
773  * @action      Action expected from current function.
774  * @svg_p       SVG specific parameters: Current graph number (.@graph_no),
775  *              flag indicating that a restart record has been previously
776  *              found (.@restart) and a pointer on a record header structure
777  *              (.@record_hdr) containing the first stats sample.
778  * @itv         Interval of time in jiffies (only with F_MAIN action).
779  * @record_hdr  Pointer on record header of current stats sample.
780  ***************************************************************************
781  */
782 __print_funct_t svg_print_net_dev_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
783                                         unsigned long long itv, struct record_header *record_hdr)
784 {
785         struct stats_net_dev *sndc, *sndp;
786         int group1[] = {2, 2, 3};
787         int group2[] = {1};
788         char *title1[] = {"Network statistics (1)", "Network statistics (2)",
789                           "Network statistics (3)"};
790         char *title2[] = {"Network statistics (4)"};
791         char *g_title1[] = {"rxpck/s", "txpck/s",
792                             "rxkB/s", "txkB/s",
793                             "rxcmp/s", "txcmp/s", "rxmcst/s"};
794         char *g_title2[] = {"%ifutil"};
795         static double *spmin, *spmax;
796         static char **out;
797         static int *outsize;
798         char *item_name;
799         double rxkb, txkb, ifutil;
800         int i, j, pos, restart, *unregistered;
801
802         if (action & F_BEGIN) {
803                 /*
804                  * Allocate arrays (#0..7) that will contain the graphs data
805                  * and the min/max values.
806                  * Also allocate one additional array (#8) for each interface:
807                  * out + 8 will contain the interface name,
808                  * outsize + 8 will contain a positive value (TRUE) if the interface
809                  * has either still not been registered, or has been unregistered.
810                  */
811                 out = allocate_graph_lines(9 * a->nr, &outsize, &spmin, &spmax);
812         }
813
814         if (action & F_MAIN) {
815                 restart = svg_p->restart;
816
817                 for (i = 0; i < a->nr; i++) {
818                         pos = i * 9;
819                         unregistered = outsize + pos + 8;
820                         item_name = *(out + pos + 8);
821                         sndc = (struct stats_net_dev *) ((char *) a->buf[curr] + i * a->msize);
822
823                         if (!strcmp(sndc->interface, "")) {
824                                 /*
825                                  * Current interface non existent. Maybe this
826                                  * interface will be dynamically registered later,
827                                  * or it has been unregistered. So mark it as unregistered.
828                                  */
829                                 *unregistered = TRUE;
830                                 continue;
831                         }
832
833                         j = check_net_dev_reg(a, curr, !curr, i);
834                         sndp = (struct stats_net_dev *) ((char *) a->buf[!curr] + j * a->msize);
835
836                         /*
837                          * Current interface was marked as previously unregistered.
838                          * So set restart variable to TRUE so that the graph will be
839                          * discontinuous, then mark current interface as now registered.
840                          */
841                         if (*unregistered == TRUE) {
842                                 restart = TRUE;
843                                 *unregistered = FALSE;
844                         }
845                         if (!**(out + pos + 7)) {
846                                 /* Save network interface name (if not already done) */
847                                 strncpy(item_name, sndc->interface, CHUNKSIZE);
848                                 item_name[CHUNKSIZE - 1] = '\0';
849                         }
850                         /* Check for min/max values */
851                         save_extrema(7, 0, 0, (void *) sndc, (void *) sndp,
852                                      itv, spmin + pos, spmax + pos);
853
854                         rxkb = S_VALUE(sndp->rx_bytes, sndc->rx_bytes, itv);
855                         txkb = S_VALUE(sndp->tx_bytes, sndc->tx_bytes, itv);
856                         ifutil = compute_ifutil(sndc, rxkb, txkb);
857                         if (ifutil < *(spmin + pos + 7)) {
858                                 *(spmin + pos + 7) = ifutil;
859                         }
860                         if (ifutil > *(spmax + pos + 7)) {
861                                 *(spmax + pos + 7) = ifutil;
862                         }
863
864                         /* rxpck/s */
865                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
866                                  S_VALUE(sndp->rx_packets, sndc->rx_packets, itv),
867                                  out + pos, outsize + pos, restart, svg_p->dt, SVG_LINE_GRAPH);
868
869                         /* txpck/s */
870                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
871                                  S_VALUE(sndp->tx_packets, sndc->tx_packets, itv),
872                                  out + pos + 1, outsize + pos + 1, restart, svg_p->dt, SVG_LINE_GRAPH);
873
874                         /* rxkB/s */
875                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
876                                  rxkb / 1024,
877                                  out + pos + 2, outsize + pos + 2, restart, svg_p->dt, SVG_LINE_GRAPH);
878
879                         /* txkB/s */
880                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
881                                  txkb / 1024,
882                                  out + pos + 3, outsize + pos + 3, restart, svg_p->dt, SVG_LINE_GRAPH);
883
884                         /* rxcmp/s */
885                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
886                                  S_VALUE(sndp->rx_compressed, sndc->rx_compressed, itv),
887                                  out + pos + 4, outsize + pos + 4, restart, svg_p->dt, SVG_LINE_GRAPH);
888
889                         /* txcmp/s */
890                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
891                                  S_VALUE(sndp->tx_compressed, sndc->tx_compressed, itv),
892                                  out + pos + 5, outsize + pos + 5, restart, svg_p->dt, SVG_LINE_GRAPH);
893
894                         /* rxmcst/s */
895                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
896                                  S_VALUE(sndp->multicast, sndc->multicast, itv),
897                                  out + pos + 6, outsize + pos + 6, restart, svg_p->dt, SVG_LINE_GRAPH);
898
899                         /* %ifutil */
900                         lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
901                                  ifutil,
902                                  out + pos + 7, outsize + pos + 7, restart, svg_p->dt, SVG_BAR_GRAPH);
903                 }
904         }
905
906         if (action & F_END) {
907                 for (i = 0; i < a->nr; i++) {
908                         /*
909                          * Check if there is something to display.
910                          * Don't test sndc->interface because maybe the network
911                          * interface has been registered later.
912                          */
913                         pos = i * 9;
914                         if (!**(out + pos))
915                                 continue;
916
917                         /* Recalculate min and max values in kB, not in B */
918                         *(spmin + pos + 2) /= 1024;
919                         *(spmax + pos + 2) /= 1024;
920                         *(spmin + pos + 3) /= 1024;
921                         *(spmax + pos + 3) /= 1024;
922
923                         item_name = *(out + pos + 8);
924                         draw_activity_graphs(a->g_nr - 1, SVG_LINE_GRAPH,
925                                              title1, g_title1, item_name, group1,
926                                              spmin + pos, spmax + pos, out + pos, outsize + pos,
927                                              svg_p, record_hdr);
928                         draw_activity_graphs(1, SVG_BAR_GRAPH,
929                                              title2, g_title2, item_name, group2,
930                                              spmin + pos + 7, spmax + pos + 7, out + pos + 7, outsize + pos + 7,
931                                              svg_p, record_hdr);
932                 }
933
934                 /* Free remaining structures */
935                 free_graphs(out, outsize, spmin, spmax);
936         }
937 }