2 * svg_stats.c: Funtions used by sadf to display statistics in SVG format.
3 * (C) 2016 by Sebastien GODARD (sysstat <at> orange.fr)
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. *
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 *
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 ***************************************************************************
31 #include "svg_stats.h"
36 #define _(string) gettext(string)
38 #define _(string) (string)
41 extern unsigned int flags;
42 extern unsigned int dm_major;
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
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.
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.
70 * @minv Array containg the possible new min values for current activity.
71 * @maxv Array containg the possible new max values for current activity.
73 * NB: @minv and @maxv arrays contain values in the same order as the fields
74 * in the statistics structure.
75 ***************************************************************************
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[])
80 unsigned long long *lluc, *llup;
81 unsigned long *luc, *lup;
82 unsigned int *uc, *up;
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);
97 lluc = (unsigned long long *) ((char *) lluc + ULL_ALIGNMENT_WIDTH);
98 llup = (unsigned long long *) ((char *) llup + ULL_ALIGNMENT_WIDTH);
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);
112 luc = (unsigned long *) ((char *) luc + UL_ALIGNMENT_WIDTH);
113 lup = (unsigned long *) ((char *) lup + UL_ALIGNMENT_WIDTH);
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);
127 uc = (unsigned int *) ((char *) uc + U_ALIGNMENT_WIDTH);
128 up = (unsigned int *) ((char *) up + U_ALIGNMENT_WIDTH);
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].
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.
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 ***************************************************************************
149 void get_global_extrema(int pos, int n, double minv[], double maxv[])
153 for (i = 1; i < n; i++) {
154 if (minv[pos + i] < minv[pos]) {
155 minv[pos] = minv[pos + i];
157 if (maxv[pos + i] > maxv[pos]) {
158 maxv[pos] = maxv[pos + i];
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]").
175 * @n Number of graphs to draw for current activity.
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.
185 * Pointer on array of arrays of chars that will contain the graphs data.
187 * NB: @min and @max arrays contain values in the same order as the fields
188 * in the statistics structure.
189 ***************************************************************************
191 char **allocate_graph_lines(int n, int **outsize, double **spmin, double **spmax)
198 * Allocate an array of pointers. Each of these pointers will
199 * be an array of chars.
201 if ((out = (char **) malloc(n * sizeof(char *))) == NULL) {
205 /* Allocate array that will contain the size of each array of chars */
206 if ((*outsize = (int *) malloc(n * sizeof(int))) == NULL) {
210 /* Allocate array that will contain the min value of each graph */
211 if ((*spmin = (double *) malloc(n * sizeof(double))) == NULL) {
215 /* Allocate array that will contain the max value of each graph */
216 if ((*spmax = (double *) malloc(n * sizeof(double))) == NULL) {
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) {
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;
237 ***************************************************************************
238 * Update graph definition by appending current X,Y coordinates.
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
248 * @dt Interval of time in seconds between current and previous
250 * @g_type Graph type: SVG_LINE_GRAPH or SVG_BAR_GRAPH (for %values).
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 ***************************************************************************
259 void lnappend(unsigned long timetag, double value, char **out, int *outsize, int restart,
260 unsigned long dt, int g_type)
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);
272 /* Dont draw a flat rectangle! */
274 snprintf(point, 128, "<rect x=\"%lu\" y=\"0\" height=\"%.2f\" width=\"%lu\"/>",
275 timetag - dt, MINIMUM(value, 100.0), dt);
279 len = *outsize - strlen(out_p) - 1;
280 if (strlen(point) >= len) {
282 * If current array of chars doesn't have enough space left
283 * then reallocate it with CHUNKSIZE more bytes.
285 SREALLOC(out_p, char, *outsize + CHUNKSIZE);
287 *outsize += CHUNKSIZE;
289 strncat(out_p, point, len);
293 ***************************************************************************
294 * Calculate the value on the Y axis between two horizontal lines that will
295 * make the graph background grid.
298 * @lmax Max value reached for this graph.
301 * @dp Number of decimal places for Y graduations.
304 * Value between two horizontal lines.
305 ***************************************************************************
307 double ygrid(double lmax, int *dp)
317 n = (long) (lmax / SVG_H_GRIDNR);
320 return (lmax / SVG_H_GRIDNR);
322 snprintf(val, 32, "%ld", n);
327 for (i = 1; i < l; i++) {
330 return ((double) (((long) (n / e)) * e));
334 ***************************************************************************
335 * Calculate the value on the X axis between two vertical lines that will
336 * make the graph background grid.
339 * @timestart First data timestamp (X coordinate of the first data point).
340 * @timeend Last data timestamp (X coordinate of the last data point).
343 * Value between two vertical lines.
344 ***************************************************************************
346 long int xgrid(unsigned long timestart, unsigned long timeend)
348 return ((timeend - timestart) / SVG_V_GRIDNR);
352 ***************************************************************************
353 * Free global graphs structures.
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 ***************************************************************************
362 void free_graphs(char **out, int *outsize, double *spmin, double *spmax)
379 ***************************************************************************
380 * Display all graphs for current activity.
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 ***************************************************************************
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)
403 struct record_header stamp;
406 int i, j, dp, pos = 0;
408 double lmax, xfactor, yfactor, ypos;
411 /* Translate to proper position for current activity */
412 printf("<g id=\"g%d\" transform=\"translate(0,%d)\">\n",
414 SVG_H_YSIZE + svg_p->graph_no * SVG_T_YSIZE);
416 /* For each set of graphs which are part of current activity */
417 for (i = 0; i < g_nr; i++) {
419 /* Graph background */
420 printf("<rect x=\"0\" y=\"%d\" height=\"%d\" width=\"%d\"/>\n",
422 SVG_V_YSIZE, SVG_V_XSIZE);
425 printf("<text x=\"0\" y=\"%d\" style=\"fill: yellow; stroke: none\">%s",
426 20 + i * SVG_T_YSIZE, title[i]);
428 printf(" [%s]", item_name);
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);
437 * At least two samples are needed.
438 * And a min and max value should have been found.
440 if ((record_hdr->ust_time == svg_p->record_hdr->ust_time) ||
441 (*(spmin + i) == DBL_MAX) || (*(spmax + i) == -DBL_MIN)) {
443 printf("<text x=\"0\" y=\"%d\" style=\"fill: red; stroke: none\">No data</text>\n",
444 SVG_M_YSIZE + i * SVG_T_YSIZE);
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);
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));
463 /* Get global min and max value for current set of graphs */
464 get_global_extrema(pos, group[i], spmin, spmax);
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);
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 */
478 lmax = *(spmax + pos);
480 ypos = ygrid(*(spmax + pos), &dp);
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% */
487 yfactor = (double) -SVG_G_YSIZE / lmax;
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);
495 while (ypos * j <= lmax);
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);
503 while (ypos * j <= lmax);
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);
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);
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");
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) {
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],
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);
555 (svg_p->graph_no) += g_nr;
559 ***************************************************************************
560 * Display task creation and context switch statistics in SVG
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 ***************************************************************************
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)
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",
584 static double *spmin, *spmax;
588 if (action & F_BEGIN) {
590 * Allocate arrays that will contain the graphs data
591 * and the min/max values.
593 out = allocate_graph_lines(2, &outsize, &spmin, &spmax);
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],
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);
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);
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);
614 /* Free remaining structures */
615 free_graphs(out, outsize, spmin, spmax);
620 ***************************************************************************
621 * Display swap statistics in SVG
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 ***************************************************************************
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)
639 *ssc = (struct stats_swap *) a->buf[curr],
640 *ssp = (struct stats_swap *) a->buf[!curr];
642 char *title[] = {"Swap activity"};
643 char *g_title[] = {"pswpin/s", "pswpout/s" };
644 static double *spmin, *spmax;
648 if (action & F_BEGIN) {
650 * Allocate arrays that will contain the graphs data
651 * and the min/max values.
653 out = allocate_graph_lines(2, &outsize, &spmin, &spmax);
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],
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);
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);
670 if (action & F_END) {
671 draw_activity_graphs(a, title, g_title, NULL, group, spmin, spmax,
672 out, outsize, svg_p, record_hdr);
674 /* Free remaining structures */
675 free_graphs(out, outsize, spmin, spmax);
681 ***************************************************************************
682 * Display paging statistics in SVG
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 ***************************************************************************
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)
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;
711 if (action & F_BEGIN) {
713 * Allocate arrays that will contain the graphs data
714 * and the min/max values.
716 out = allocate_graph_lines(8, &outsize, &spmin, &spmax);
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],
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);
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);
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);
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);
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);
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);
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);
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);
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);
761 /* Free remaining structures */
762 free_graphs(out, outsize, spmin, spmax);
767 ***************************************************************************
768 * Display network interfaces statistics in SVG
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 ***************************************************************************
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)
785 struct stats_net_dev *sndc, *sndp;
786 int group1[] = {2, 2, 3};
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",
793 "rxcmp/s", "txcmp/s", "rxmcst/s"};
794 char *g_title2[] = {"%ifutil"};
795 static double *spmin, *spmax;
799 double rxkb, txkb, ifutil;
800 int i, j, pos, restart, *unregistered;
802 if (action & F_BEGIN) {
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.
811 out = allocate_graph_lines(9 * a->nr, &outsize, &spmin, &spmax);
814 if (action & F_MAIN) {
815 restart = svg_p->restart;
817 for (i = 0; i < a->nr; i++) {
819 unregistered = outsize + pos + 8;
820 item_name = *(out + pos + 8);
821 sndc = (struct stats_net_dev *) ((char *) a->buf[curr] + i * a->msize);
823 if (!strcmp(sndc->interface, "")) {
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.
829 *unregistered = TRUE;
833 j = check_net_dev_reg(a, curr, !curr, i);
834 sndp = (struct stats_net_dev *) ((char *) a->buf[!curr] + j * a->msize);
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.
841 if (*unregistered == TRUE) {
843 *unregistered = FALSE;
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';
850 /* Check for min/max values */
851 save_extrema(7, 0, 0, (void *) sndc, (void *) sndp,
852 itv, spmin + pos, spmax + pos);
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;
860 if (ifutil > *(spmax + pos + 7)) {
861 *(spmax + pos + 7) = ifutil;
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);
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);
875 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
877 out + pos + 2, outsize + pos + 2, restart, svg_p->dt, SVG_LINE_GRAPH);
880 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
882 out + pos + 3, outsize + pos + 3, restart, svg_p->dt, SVG_LINE_GRAPH);
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);
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);
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);
900 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
902 out + pos + 7, outsize + pos + 7, restart, svg_p->dt, SVG_BAR_GRAPH);
906 if (action & F_END) {
907 for (i = 0; i < a->nr; i++) {
909 * Check if there is something to display.
910 * Don't test sndc->interface because maybe the network
911 * interface has been registered later.
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;
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,
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,
934 /* Free remaining structures */
935 free_graphs(out, outsize, spmin, spmax);