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
250 * @out Pointer on array of chars for current graph definition that
251 * has been updated with the addition of current sample data.
252 * @outsize Array that containing the (possibly new) sizes of each
253 * element in array of chars.
254 ***************************************************************************
256 void lnappend(unsigned long timetag, double value, char **out, int *outsize, int restart)
262 /* Prepare additional graph definition data */
263 snprintf(point, 128, " %c%lu,%.2f", restart ? 'M' : 'L', timetag, value);
266 len = *outsize - strlen(out_p) - 1;
267 if (strlen(point) >= len) {
269 * If current array of chars doesn't have enough space left
270 * then reallocate it with CHUNKSIZE more bytes.
272 SREALLOC(out_p, char, *outsize + CHUNKSIZE);
274 *outsize += CHUNKSIZE;
276 strncat(out_p, point, len);
280 ***************************************************************************
281 * Calculate the value on the Y axis between two horizontal lines that will
282 * make the graph background grid.
285 * @lmax Max value reached for this graph.
288 * @dp Number of decimal places for Y graduations.
291 * Value between two horizontal lines.
292 ***************************************************************************
294 double ygrid(double lmax, int *dp)
304 n = (long) (lmax / SVG_H_GRIDNR);
307 return (lmax / SVG_H_GRIDNR);
309 snprintf(val, 32, "%ld", n);
314 for (i = 1; i < l; i++) {
317 return ((double) (((long) (n / e)) * e));
321 ***************************************************************************
322 * Calculate the value on the X axis between two vertical lines that will
323 * make the graph background grid.
326 * @timestart First data timestamp (X coordinate of the first data point).
327 * @timeend Last data timestamp (X coordinate of the last data point).
330 * Value between two vertical lines.
331 ***************************************************************************
333 long int xgrid(unsigned long timestart, unsigned long timeend)
335 return ((timeend - timestart) / SVG_V_GRIDNR);
339 ***************************************************************************
340 * Free global graphs structures.
343 * @out Pointer on array of chars for each graph definition.
344 * @outsize Size of array of chars for each graph definition.
345 * @spmin Array containing min values for graphs.
346 * @spmax Array containing max values for graphs.
347 ***************************************************************************
349 void free_graphs(char **out, int *outsize, double *spmin, double *spmax)
366 ***************************************************************************
367 * Display all graphs for current activity.
370 * @a Activity structure containing the number of graphs to draw
371 * for current activity.
372 * @title Titles for each set of graphs.
373 * @g_title Titles for each graph.
374 * @item_name Item (network interface, etc.) name.
375 * @group Indicate how graphs are grouped together to make sets.
376 * @spmin Array containing min values for graphs.
377 * @spmax Array containing max values for graphs.
378 * @out Pointer on array of chars for each graph definition.
379 * @outsize Size of array of chars for each graph definition.
380 * @svg_p SVG specific parameters: Current graph number (.@graph_no)
381 * and a pointer on a record header structure (.@record_hdr)
382 * containing the first stats sample.
383 * @record_hdr Pointer on record header of current stats sample.
384 ***************************************************************************
386 void draw_activity_graphs(struct activity *a, char *title[], char *g_title[], char *item_name,
387 int group[], double *spmin, double *spmax, char **out, int *outsize,
388 struct svg_parm *svg_p, struct record_header *record_hdr)
390 struct record_header stamp;
393 int i, j, dp, pos = 0;
395 double lmax, xfactor, yfactor, ypos;
398 /* Translate to proper position for current activity */
399 printf("<g id=\"g%d\" transform=\"translate(0,%d)\">\n",
401 SVG_H_YSIZE + svg_p->graph_no * SVG_T_YSIZE);
403 /* For each graphs set which are part of current activity */
404 for (i = 0; i < a->g_nr; i++) {
406 /* Graph background */
407 printf("<rect x=\"0\" y=\"%d\" height=\"%d\" width=\"%d\"/>\n",
409 SVG_V_YSIZE, SVG_V_XSIZE);
412 printf("<text x=\"0\" y=\"%d\" style=\"fill: yellow; stroke: none\">%s",
413 20 + i * SVG_T_YSIZE, title[i]);
415 printf(" [%s]", item_name);
418 printf("<tspan x=\"%d\" y=\"%d\" style=\"fill: yellow; stroke: none; font-size: 12px\">"
419 "(Min, Max values)</tspan>\n</text>\n",
420 5 + SVG_M_XSIZE + SVG_G_XSIZE,
421 25 + i * SVG_T_YSIZE);
424 * At least two samples are needed.
425 * And a min and max value should have been found.
427 if ((record_hdr->ust_time == svg_p->record_hdr->ust_time) ||
428 (*(spmin + i) == DBL_MAX) || (*(spmax + i) == -DBL_MIN)) {
430 printf("<text x=\"0\" y=\"%d\" style=\"fill: red; stroke: none\">No data</text>\n",
431 SVG_M_YSIZE + i * SVG_T_YSIZE);
436 printf("<polyline points=\"%d,%d %d,%d %d,%d\" stroke=\"white\" stroke-width=\"2\"/>\n",
437 SVG_M_XSIZE, SVG_M_YSIZE + i * SVG_T_YSIZE,
438 SVG_M_XSIZE, SVG_M_YSIZE + SVG_G_YSIZE + i * SVG_T_YSIZE,
439 SVG_M_XSIZE + SVG_G_XSIZE, SVG_M_YSIZE + SVG_G_YSIZE + i * SVG_T_YSIZE);
442 for (j = 0; j < group[i]; j++) {
443 printf("<text x=\"%d\" y=\"%d\" style=\"fill: #%06x; stroke: none; font-size: 12px\">"
444 "%s (%.2f, %.2f)</text>\n",
445 5 + SVG_M_XSIZE + SVG_G_XSIZE, SVG_M_YSIZE + i * SVG_T_YSIZE + j * 15,
446 svg_colors[(pos + j) & SVG_COLORS_IDX_MASK], g_title[pos + j],
447 *(spmin + pos + j), *(spmax + pos + j));
450 /* Get global min and max value for current graphs set */
451 get_global_extrema(pos, group[i], spmin, spmax);
453 /* Translate to proper position for current graph within current activity */
454 printf("<g transform=\"translate(%d,%d)\">\n",
455 SVG_M_XSIZE, SVG_M_YSIZE + SVG_G_YSIZE + i * SVG_T_YSIZE);
458 if (*(spmax + pos) == 0) {
459 /* If all values are zero then set current max value to 1 */
463 lmax = *(spmax + pos);
465 ypos = ygrid(*(spmax + pos), &dp);
466 yfactor = (double) -SVG_G_YSIZE / lmax;
469 printf("<polyline points=\"0,%.2f %d,%.2f\" style=\"vector-effect: non-scaling-stroke; "
470 "stroke: #202020\" transform=\"scale(1,%f)\"/>\n",
471 ypos * j, SVG_G_XSIZE, ypos * j, yfactor);
474 while (ypos * j <= lmax);
477 printf("<text x=\"0\" y=\"%ld\" style=\"fill: white; stroke: none; font-size: 12px; "
478 "text-anchor: end\">%.*f.</text>\n",
479 (long) (ypos * j * yfactor), dp, ypos * j);
482 while (ypos * j <= lmax);
484 k = xgrid(svg_p->record_hdr->ust_time, record_hdr->ust_time);
485 xfactor = (double) SVG_G_XSIZE / (record_hdr->ust_time - svg_p->record_hdr->ust_time);
486 stamp = *svg_p->record_hdr;
487 for (j = 1; j <= SVG_V_GRIDNR; j++) {
488 printf("<polyline points=\"%ld,0 %ld,%d\" style=\"vector-effect: non-scaling-stroke; "
489 "stroke: #202020\" transform=\"scale(%f,1)\"/>\n",
490 k * j, k * j, -SVG_G_YSIZE, xfactor);
492 for (j = 0; j <= SVG_V_GRIDNR; j++) {
493 sa_get_record_timestamp_struct(flags, &stamp, &rectime, NULL);
494 set_record_timestamp_string(flags, &stamp, NULL, cur_time, 32, &rectime);
495 printf("<text x=\"%ld\" y=\"10\" style=\"fill: white; stroke: none; font-size: 12px; "
496 "text-anchor: start\" transform=\"rotate(45,%ld,0)\">%s</text>\n",
497 (long) (k * j * xfactor), (long) (k * j * xfactor), cur_time);
500 if (!PRINT_LOCAL_TIME(flags)) {
501 printf("<text x=\"-10\" y=\"30\" style=\"fill: yellow; stroke: none; font-size: 12px; "
502 "text-anchor: end\">UTC</text>\n");
505 /* Draw current graphs set */
506 for (j = 0; j < group[i]; j++) {
507 out_p = *(out + pos + j);
508 printf("<path id=\"g%dp%d\" d=\"%s\" style=\"vector-effect: non-scaling-stroke; "
509 "stroke: #%06x; stroke-width: 1; fill-opacity: 0\" "
510 "transform=\"scale(%f,%f)\"/>\n",
511 svg_p->graph_no, pos + j, out_p,
512 svg_colors[(pos + j) & SVG_COLORS_IDX_MASK],
523 (svg_p->graph_no) += a->g_nr;
527 ***************************************************************************
528 * Display task creation and context switch statistics in SVG
531 * @a Activity structure with statistics.
532 * @curr Index in array for current sample statistics.
533 * @action Action expected from current function.
534 * @svg_p SVG specific parameters: Current graph number (.@graph_no),
535 * flag indicating that a restart record has been previously
536 * found (.@restart) and a pointer on a record header structure
537 * (.@record_hdr) containing the first stats sample.
538 * @itv Interval of time in jiffies (only with F_MAIN action).
539 * @record_hdr Pointer on record header of current stats sample.
540 ***************************************************************************
542 __print_funct_t svg_print_pcsw_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
543 unsigned long long itv, struct record_header *record_hdr)
546 *spc = (struct stats_pcsw *) a->buf[curr],
547 *spp = (struct stats_pcsw *) a->buf[!curr];
548 int group[] = {1, 1};
549 char *title[] = {"Switching activity", "Task creation"};
550 char *g_title[] = {"cswch/s", "proc/s"};
551 static double *spmin, *spmax;
555 if (action & F_BEGIN) {
557 * Allocate arrays that will contain the graphs data
558 * and the min/max values.
560 out = allocate_graph_lines(2, &outsize, &spmin, &spmax);
563 if (action & F_MAIN) {
564 /* Check for min/max values */
565 save_extrema(1, 1, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
568 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
569 S_VALUE(spp->context_switch, spc->context_switch, itv),
570 out, outsize, svg_p->restart);
572 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
573 S_VALUE(spp->processes, spc->processes, itv),
574 out + 1, outsize + 1, svg_p->restart);
577 if (action & F_END) {
578 draw_activity_graphs(a, title, g_title, NULL, group, spmin, spmax,
579 out, outsize, svg_p, record_hdr);
581 /* Free remaining structures */
582 free_graphs(out, outsize, spmin, spmax);
587 ***************************************************************************
588 * Display swap statistics in SVG
591 * @a Activity structure with statistics.
592 * @curr Index in array for current sample statistics.
593 * @action Action expected from current function.
594 * @svg_p SVG specific parameters: Current graph number (.@graph_no),
595 * flag indicating that a restart record has been previously
596 * found (.@restart) and a pointer on a record header structure
597 * (.@record_hdr) containing the first stats sample.
598 * @itv Interval of time in jiffies (only with F_MAIN action).
599 * @record_hdr Pointer on record header of current stats sample.
600 ***************************************************************************
602 __print_funct_t svg_print_swap_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
603 unsigned long long itv, struct record_header *record_hdr)
606 *ssc = (struct stats_swap *) a->buf[curr],
607 *ssp = (struct stats_swap *) a->buf[!curr];
609 char *title[] = {"Swap activity"};
610 char *g_title[] = {"pswpin/s", "pswpout/s" };
611 static double *spmin, *spmax;
615 if (action & F_BEGIN) {
617 * Allocate arrays that will contain the graphs data
618 * and the min/max values.
620 out = allocate_graph_lines(2, &outsize, &spmin, &spmax);
623 if (action & F_MAIN) {
624 /* Check for min/max values */
625 save_extrema(0, 2, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
628 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
629 S_VALUE(ssp->pswpin, ssc->pswpin, itv),
630 out, outsize, svg_p->restart);
632 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
633 S_VALUE(ssp->pswpout, ssc->pswpout, itv),
634 out + 1, outsize + 1, svg_p->restart);
637 if (action & F_END) {
638 draw_activity_graphs(a, title, g_title, NULL, group, spmin, spmax,
639 out, outsize, svg_p, record_hdr);
641 /* Free remaining structures */
642 free_graphs(out, outsize, spmin, spmax);
648 ***************************************************************************
649 * Display paging statistics in SVG
652 * @a Activity structure with statistics.
653 * @curr Index in array for current sample statistics.
654 * @action Action expected from current function.
655 * @svg_p SVG specific parameters: Current graph number (.@graph_no),
656 * flag indicating that a restart record has been previously
657 * found (.@restart) and a pointer on a record header structure
658 * (.@record_hdr) containing the first stats sample.
659 * @itv Interval of time in jiffies (only with F_MAIN action).
660 * @record_hdr Pointer on record header of current stats sample.
661 ***************************************************************************
663 __print_funct_t svg_print_paging_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
664 unsigned long long itv, struct record_header *record_hdr)
667 *spc = (struct stats_paging *) a->buf[curr],
668 *spp = (struct stats_paging *) a->buf[!curr];
669 int group[] = {2, 2, 4};
670 char *title[] = {"Paging activity (1)", "Paging activity (2)", "Paging activity (3)"};
671 char *g_title[] = {"pgpgin/s", "pgpgout/s", "fault/s", "majflt/s",
672 "pgfree/s", "pgscank/s", "pgscand/s", "pgsteal/s"};
673 static double *spmin, *spmax;
677 if (action & F_BEGIN) {
679 * Allocate arrays that will contain the graphs data
680 * and the min/max values.
682 out = allocate_graph_lines(8, &outsize, &spmin, &spmax);
685 if (action & F_MAIN) {
686 /* Check for min/max values */
687 save_extrema(0, 8, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
690 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
691 S_VALUE(spp->pgpgin, spc->pgpgin, itv),
692 out, outsize, svg_p->restart);
694 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
695 S_VALUE(spp->pgpgout, spc->pgpgout, itv),
696 out + 1, outsize + 1, svg_p->restart);
698 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
699 S_VALUE(spp->pgfault, spc->pgfault, itv),
700 out + 2, outsize + 2, svg_p->restart);
702 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
703 S_VALUE(spp->pgmajfault, spc->pgmajfault, itv),
704 out + 3, outsize + 3, svg_p->restart);
706 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
707 S_VALUE(spp->pgfree, spc->pgfree, itv),
708 out + 4, outsize + 4, svg_p->restart);
710 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
711 S_VALUE(spp->pgscan_kswapd, spc->pgscan_kswapd, itv),
712 out + 5, outsize + 5, svg_p->restart);
714 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
715 S_VALUE(spp->pgscan_direct, spc->pgscan_direct, itv),
716 out + 6, outsize + 6, svg_p->restart);
718 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
719 S_VALUE(spp->pgsteal, spc->pgsteal, itv),
720 out + 7, outsize + 7, svg_p->restart);
723 if (action & F_END) {
724 draw_activity_graphs(a, title, g_title, NULL, group, spmin, spmax,
725 out, outsize, svg_p, record_hdr);
727 /* Free remaining structures */
728 free_graphs(out, outsize, spmin, spmax);
733 ***************************************************************************
734 * Display network interfaces statistics in SVG
737 * @a Activity structure with statistics.
738 * @curr Index in array for current sample statistics.
739 * @action Action expected from current function.
740 * @svg_p SVG specific parameters: Current graph number (.@graph_no),
741 * flag indicating that a restart record has been previously
742 * found (.@restart) and a pointer on a record header structure
743 * (.@record_hdr) containing the first stats sample.
744 * @itv Interval of time in jiffies (only with F_MAIN action).
745 * @record_hdr Pointer on record header of current stats sample.
746 ***************************************************************************
748 __print_funct_t svg_print_net_dev_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
749 unsigned long long itv, struct record_header *record_hdr)
751 struct stats_net_dev *sndc, *sndp;
752 int group[] = {2, 2, 3};
753 char *title[] = {"Network statistics (1)", "Network statistics (2)", "Network statistics (3)"};
754 char *g_title[] = {"rxpck/s", "txpck/s",
756 "rxcmp/s", "txcmp/s", "rxmcst/s"};
757 static double *spmin, *spmax;
761 int i, j, pos, restart, *unregistered;
763 if (action & F_BEGIN) {
765 * Allocate arrays (#0..6) that will contain the graphs data
766 * and the min/max values.
767 * Also allocate one additional array (#7) for each interface:
768 * out + 7 will contain the interface name,
769 * outsize + 7 will contain a positive value (TRUE) if the interface
770 * has either still not been registered, or has been unregistered.
772 out = allocate_graph_lines(8 * a->nr, &outsize, &spmin, &spmax);
775 if (action & F_MAIN) {
776 restart = svg_p->restart;
778 for (i = 0; i < a->nr; i++) {
780 unregistered = outsize + pos + 7;
781 item_name = *(out + pos + 7);
782 sndc = (struct stats_net_dev *) ((char *) a->buf[curr] + i * a->msize);
784 if (!strcmp(sndc->interface, "")) {
786 * Current interface non existent. Maybe this
787 * interface will be dynamically registered later,
788 * or it has been unregistered. So mark it as unregistered.
790 *unregistered = TRUE;
794 j = check_net_dev_reg(a, curr, !curr, i);
795 sndp = (struct stats_net_dev *) ((char *) a->buf[!curr] + j * a->msize);
798 * Current interface was marked as previously unregistered.
799 * So set restart variable to TRUE so that the graph will be
800 * discontinuous, then mark current interface as now registered.
802 if (*unregistered == TRUE) {
804 *unregistered = FALSE;
806 if (!**(out + pos + 7)) {
807 /* Save network interface name (if not already done) */
808 strncpy(item_name, sndc->interface, CHUNKSIZE);
809 item_name[CHUNKSIZE - 1] = '\0';
811 /* Check for min/max values */
812 save_extrema(7, 0, 0, (void *) sndc, (void *) sndp,
813 itv, spmin + pos, spmax + pos);
816 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
817 S_VALUE(sndp->rx_packets, sndc->rx_packets, itv),
818 out + pos, outsize + pos, restart);
821 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
822 S_VALUE(sndp->tx_packets, sndc->tx_packets, itv),
823 out + pos + 1, outsize + pos + 1, restart);
826 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
827 S_VALUE(sndp->rx_bytes, sndc->rx_bytes, itv) / 1024,
828 out + pos + 2, outsize + pos + 2, restart);
831 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
832 S_VALUE(sndp->tx_bytes, sndc->tx_bytes, itv) / 1024,
833 out + pos + 3, outsize + pos + 3, restart);
836 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
837 S_VALUE(sndp->rx_compressed, sndc->rx_compressed, itv),
838 out + pos + 4, outsize + pos + 4, restart);
841 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
842 S_VALUE(sndp->tx_compressed, sndc->tx_compressed, itv),
843 out + pos + 5, outsize + pos + 5, restart);
846 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
847 S_VALUE(sndp->multicast, sndc->multicast, itv),
848 out + pos + 6, outsize + pos + 6, restart);
852 if (action & F_END) {
853 for (i = 0; i < a->nr; i++) {
855 * Check if there is something to display.
856 * Don't test sndc->interface because maybe the network
857 * interface has been registered later.
863 /* Recalculate min and max values in kB, not in B */
864 *(spmin + pos + 2) /= 1024;
865 *(spmax + pos + 2) /= 1024;
866 *(spmin + pos + 3) /= 1024;
867 *(spmax + pos + 3) /= 1024;
869 item_name = *(out + pos + 7);
870 draw_activity_graphs(a, title, g_title, item_name, group,
871 spmin + pos, spmax + pos, out + pos, outsize + pos,
875 /* Free remaining structures */
876 free_graphs(out, outsize, spmin, spmax);