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 0xe85f00, 0x0000ff, 0x006020, 0x7030a0,
46 0xffff00, 0x666635, 0xd60093, 0x00bfbf,
47 0xcc3300, 0xbfbfbf, 0xffffbf, 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 (may be NULL).
65 * @itv Interval of time in jiffies.
66 * @minv Array containing min values already found for this activity.
67 * @maxv Array containing max values already found for this activity.
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++) {
91 val = S_VALUE(*llup, *lluc, itv);
95 * If no pointer on previous sample has been given
96 * then the value is not a per-second one.
106 lluc = (unsigned long long *) ((char *) lluc + ULL_ALIGNMENT_WIDTH);
108 llup = (unsigned long long *) ((char *) llup + ULL_ALIGNMENT_WIDTH);
112 /* Compare unsigned long fields */
113 luc = (unsigned long *) lluc;
114 lup = (unsigned long *) llup;
115 for (i = 0; i < lu_nr; i++, m++) {
117 val = S_VALUE(*lup, *luc, itv);
128 luc = (unsigned long *) ((char *) luc + UL_ALIGNMENT_WIDTH);
130 lup = (unsigned long *) ((char *) lup + UL_ALIGNMENT_WIDTH);
134 /* Compare unsigned int fields */
135 uc = (unsigned int *) luc;
136 up = (unsigned int *) lup;
137 for (i = 0; i < u_nr; i++, m++) {
139 val = S_VALUE(*up, *uc, itv);
150 uc = (unsigned int *) ((char *) uc + U_ALIGNMENT_WIDTH);
152 up = (unsigned int *) ((char *) up + U_ALIGNMENT_WIDTH);
158 ***************************************************************************
159 * Find the min and max values of all the graphs that will be drawn in the
160 * same window. The graphs have their own min and max values in
161 * minv[pos...pos+n-1] and maxv[pos...pos+n-1].
164 * @pos Position in array for the first graph extrema value.
165 * @n Number of graphs to scan.
166 * @minv Array containing min values for graphs.
167 * @maxv Array containing max values for graphs.
170 * @minv minv[pos] is modified and contains the global min value found.
171 * @maxv maxv[pos] is modified and contains the global max value found.
172 ***************************************************************************
174 void get_global_extrema(int pos, int n, double minv[], double maxv[])
178 for (i = 1; i < n; i++) {
179 if (minv[pos + i] < minv[pos]) {
180 minv[pos] = minv[pos + i];
182 if (maxv[pos + i] > maxv[pos]) {
183 maxv[pos] = maxv[pos + i];
189 ***************************************************************************
190 * Allocate arrays used to save graphs data, min and max values.
191 * @n arrays of chars are allocated for @n graphs to draw. A pointer on this
192 * array is returned. This is equivalent to "char data[][n]" where each
193 * element is of indeterminate size and will contain the graph data (eg.
194 * << path d="M12,14 L13,16..." ... >>.
195 * The size of element data[i] is given by outsize[i].
196 * Also allocate an array to save min values (equivalent to "double spmin[n]")
197 * and an array for max values (equivalent to "double spmax[n]").
200 * @n Number of graphs to draw for current activity.
203 * @outsize Array that will contain the sizes of each element in array
204 * of chars. Equivalent to "int outsize[n]" with
205 * outsize[n] = sizeof(data[][n]).
206 * @spmin Array that will contain min values for current activity.
207 * @spmax Array that will contain max values for current activity.
210 * Pointer on array of arrays of chars that will contain the graphs data.
212 * NB: @min and @max arrays contain values in the same order as the fields
213 * in the statistics structure.
214 ***************************************************************************
216 char **allocate_graph_lines(int n, int **outsize, double **spmin, double **spmax)
223 * Allocate an array of pointers. Each of these pointers will
224 * be an array of chars.
226 if ((out = (char **) malloc(n * sizeof(char *))) == NULL) {
230 /* Allocate array that will contain the size of each array of chars */
231 if ((*outsize = (int *) malloc(n * sizeof(int))) == NULL) {
235 /* Allocate array that will contain the min value of each graph */
236 if ((*spmin = (double *) malloc(n * sizeof(double))) == NULL) {
240 /* Allocate array that will contain the max value of each graph */
241 if ((*spmax = (double *) malloc(n * sizeof(double))) == NULL) {
245 /* Allocate arrays of chars that will contain graphs data */
246 for (i = 0; i < n; i++) {
247 if ((out_p = (char *) malloc(CHUNKSIZE * sizeof(char))) == NULL) {
252 *out_p = '\0'; /* Reset string so that it can be safely strncat()'d later */
253 *(*outsize + i) = CHUNKSIZE; /* Each array of chars has a default size of CHUNKSIZE */
254 *(*spmin + i) = DBL_MAX; /* Init min and max values */
255 *(*spmax + i) = -DBL_MAX;
262 ***************************************************************************
263 * Save SVG code for current graph.
266 * @data SVG code to append to current graph definition.
267 * @out Pointer on array of chars for current graph definition.
268 * @outsize Size of array of chars for current graph definition.
271 * @out Pointer on array of chars for current graph definition that
272 * has been updated with the addition of current sample data.
273 * @outsize Array that containing the (possibly new) sizes of each
274 * element in array of chars.
275 ***************************************************************************
277 void save_svg_data(char *data, char **out, int *outsize)
283 /* Determine space left in array */
284 len = *outsize - strlen(out_p) - 1;
285 if (strlen(data) >= len) {
287 * If current array of chars doesn't have enough space left
288 * then reallocate it with CHUNKSIZE more bytes.
290 SREALLOC(out_p, char, *outsize + CHUNKSIZE);
292 *outsize += CHUNKSIZE;
295 strncat(out_p, data, len);
299 ***************************************************************************
300 * Update line graph definition by appending current X,Y coordinates.
303 * @timetag Timestamp in seconds since the epoch for current sample
304 * stats. Will be used as X coordinate.
305 * @value Value of current sample metric. Will be used as Y coordinate.
306 * @out Pointer on array of chars for current graph definition.
307 * @outsize Size of array of chars for current graph definition.
308 * @restart Set to TRUE if a RESTART record has been read since the last
312 * @out Pointer on array of chars for current graph definition that
313 * has been updated with the addition of current sample data.
314 * @outsize Array that containing the (possibly new) sizes of each
315 * element in array of chars.
316 ***************************************************************************
318 void lnappend(unsigned long timetag, double value, char **out, int *outsize, int restart)
322 /* Prepare additional graph definition data */
323 snprintf(data, 128, " %c%lu,%.2f", restart ? 'M' : 'L', timetag, value);
326 save_svg_data(data, out, outsize);
330 ***************************************************************************
331 * Update line graph definition by appending current X,Y coordinates. Use
332 * (unsigned long) integer values here.
335 * @timetag Timestamp in seconds since the epoch for current sample
336 * stats. Will be used as X coordinate.
337 * @value Value of current sample metric. Will be used as Y coordinate.
338 * @out Pointer on array of chars for current graph definition.
339 * @outsize Size of array of chars for current graph definition.
340 * @restart Set to TRUE if a RESTART record has been read since the last
344 * @out Pointer on array of chars for current graph definition that
345 * has been updated with the addition of current sample data.
346 * @outsize Array that containing the (possibly new) sizes of each
347 * element in array of chars.
348 ***************************************************************************
350 void lniappend(unsigned long timetag, unsigned long value, char **out, int *outsize,
355 /* Prepare additional graph definition data */
356 snprintf(data, 128, " %c%lu,%lu", restart ? 'M' : 'L', timetag, value);
359 save_svg_data(data, out, outsize);
363 ***************************************************************************
364 * Update bar graph definition by adding a new rectangle.
367 * @timetag Timestamp in seconds since the epoch for current sample
368 * stats. Will be used as X coordinate.
369 * @value Value of current sample metric. Will be used as rectangle
371 * @offset Offset for Y coordinate.
372 * @out Pointer on array of chars for current graph definition.
373 * @outsize Size of array of chars for current graph definition.
374 * @dt Interval of time in seconds between current and previous
378 * @out Pointer on array of chars for current graph definition that
379 * has been updated with the addition of current sample data.
380 * @outsize Array that containing the (possibly new) sizes of each
381 * element in array of chars.
382 ***************************************************************************
384 void brappend(unsigned long timetag, double offset, double value, char **out, int *outsize,
389 /* Prepare additional graph definition data */
391 /* Dont draw a flat rectangle! */
394 snprintf(data, 128, "<rect x=\"%lu\" y=\"%.2f\" height=\"%.2f\" width=\"%lu\"/>",
395 timetag - dt, MINIMUM(offset, 100.0), MINIMUM(value, (100.0 - offset)), dt);
398 save_svg_data(data, out, outsize);
403 ***************************************************************************
404 * Update CPU graph and min/max values for each metric.
407 * @timetag Timestamp in seconds since the epoch for current sample
408 * stats. Will be used as X coordinate.
409 * @offset Offset for Y coordinate.
410 * @value Value of current CPU metric. Will be used as rectangle
412 * @out Pointer on array of chars for current graph definition.
413 * @outsize Size of array of chars for current graph definition.
414 * @dt Interval of time in seconds between current and previous
416 * @spmin Min value already found for this CPU metric.
417 * @spmax Max value already found for this CPU metric.
420 * @offset New offset value, to use to draw next rectangle
421 * @out Pointer on array of chars for current graph definition that
422 * has been updated with the addition of current sample data.
423 * @outsize Array that containing the (possibly new) sizes of each
424 * element in array of chars.
425 ***************************************************************************
427 void cpuappend(unsigned long timetag, double *offset, double value, char **out, int *outsize,
428 unsigned long dt, double *spmin, double *spmax)
430 /* Save min and max values */
431 if (value < *spmin) {
434 if (value > *spmax) {
437 /* Prepare additional graph definition data */
438 brappend(timetag, *offset, value, out, outsize, dt);
444 ***************************************************************************
445 * Update rectangular graph and min/max values.
448 * @timetag Timestamp in seconds since the epoch for current sample
449 * stats. Will be used as X coordinate.
450 * @p_value Metric value for previous sample
451 * @value Metric value for current sample.
452 * @out Pointer on array of chars for current graph definition.
453 * @outsize Size of array of chars for current graph definition.
454 * @restart Set to TRUE if a RESTART record has been read since the last
456 * @dt Interval of time in seconds between current and previous
458 * @spmin Min value already found for this metric.
459 * @spmax Max value already found for this metric.
462 * @out Pointer on array of chars for current graph definition that
463 * has been updated with the addition of current sample data.
464 * @outsize Array that containing the (possibly new) sizes of each
465 * element in array of chars.
466 * @spmin Min value for this metric.
467 * @spmax Max value for this metric.
468 ***************************************************************************
470 void recappend(unsigned long timetag, double p_value, double value, char **out, int *outsize,
471 int restart, unsigned long dt, double *spmin, double *spmax)
473 char data[128], data1[128], data2[128];
475 /* Save min and max values */
476 if (value < *spmin) {
479 if (value > *spmax) {
482 /* Prepare additional graph definition data */
484 snprintf(data1, 128, " M%lu,%.2f", timetag - dt, p_value);
487 if (p_value != value) {
488 snprintf(data2, 128, " L%lu,%.2f", timetag, value);
491 snprintf(data, 128, "%s L%lu,%.2f%s", restart ? data1 : "", timetag, p_value,
492 p_value != value ? data2 : "");
495 save_svg_data(data, out, outsize);
499 ***************************************************************************
500 * Calculate the value on the Y axis between two horizontal lines that will
501 * make the graph background grid.
504 * @lmax Max value reached for this graph.
507 * @dp Number of decimal places for Y graduations.
510 * Value between two horizontal lines.
511 ***************************************************************************
513 double ygrid(double lmax, int *dp)
523 n = (long) (lmax / SVG_H_GRIDNR);
526 return (lmax / SVG_H_GRIDNR);
528 snprintf(val, 32, "%ld", n);
533 for (i = 1; i < l; i++) {
536 return ((double) (((long) (n / e)) * e));
540 ***************************************************************************
541 * Calculate the value on the X axis between two vertical lines that will
542 * make the graph background grid.
545 * @timestart First data timestamp (X coordinate of the first data point).
546 * @timeend Last data timestamp (X coordinate of the last data point).
549 * Value between two vertical lines.
550 ***************************************************************************
552 long int xgrid(unsigned long timestart, unsigned long timeend)
554 if ((timeend - timestart) <= SVG_V_GRIDNR)
557 return ((timeend - timestart) / SVG_V_GRIDNR);
561 ***************************************************************************
562 * Free global graphs structures.
565 * @out Pointer on array of chars for each graph definition.
566 * @outsize Size of array of chars for each graph definition.
567 * @spmin Array containing min values for graphs.
568 * @spmax Array containing max values for graphs.
569 ***************************************************************************
571 void free_graphs(char **out, int *outsize, double *spmin, double *spmax)
588 ***************************************************************************
589 * Display all graphs for current activity.
592 * @g_nr Number of graphs to display.
593 * @g_type Type of graph (SVG_LINE_GRAPH, SVG_BAR_GRAPH).
594 * @title Titles for each set of graphs.
595 * @g_title Titles for each graph.
596 * @item_name Item (network interface, etc.) name.
597 * @group Indicate how graphs are grouped together to make sets.
598 * @spmin Array containing min values for graphs.
599 * @spmax Array containing max values for graphs.
600 * @out Pointer on array of chars for each graph definition.
601 * @outsize Size of array of chars for each graph definition.
602 * @svg_p SVG specific parameters: Current graph number (.@graph_no)
603 * and a pointer on a record header structure (.@record_hdr)
604 * containing the first stats sample.
605 * @record_hdr Pointer on record header of current stats sample.
606 ***************************************************************************
608 void draw_activity_graphs(int g_nr, int g_type, char *title[], char *g_title[], char *item_name,
609 int group[], double *spmin, double *spmax, char **out, int *outsize,
610 struct svg_parm *svg_p, struct record_header *record_hdr)
612 struct record_header stamp;
615 int i, j, dp, pos = 0;
617 double lmax, xfactor, yfactor, ypos;
620 /* Translate to proper position for current activity */
621 printf("<g id=\"g%d\" transform=\"translate(0,%d)\">\n",
623 SVG_H_YSIZE + svg_p->graph_no * SVG_T_YSIZE);
625 /* For each set of graphs which are part of current activity */
626 for (i = 0; i < g_nr; i++) {
628 /* Graph background */
629 printf("<rect x=\"0\" y=\"%d\" height=\"%d\" width=\"%d\"/>\n",
631 SVG_V_YSIZE, SVG_V_XSIZE);
634 printf("<text x=\"0\" y=\"%d\" style=\"fill: yellow; stroke: none\">%s",
635 20 + i * SVG_T_YSIZE, title[i]);
637 printf(" [%s]", item_name);
640 printf("<tspan x=\"%d\" y=\"%d\" style=\"fill: yellow; stroke: none; font-size: 12px\">"
641 "(Min, Max values)</tspan>\n</text>\n",
642 5 + SVG_M_XSIZE + SVG_G_XSIZE,
643 25 + i * SVG_T_YSIZE);
646 * At least two samples are needed.
647 * And a min and max value should have been found.
649 if ((record_hdr->ust_time == svg_p->record_hdr->ust_time) ||
650 (*(spmin + i) == DBL_MAX) || (*(spmax + i) == -DBL_MIN)) {
652 printf("<text x=\"0\" y=\"%d\" style=\"fill: red; stroke: none\">No data</text>\n",
653 SVG_M_YSIZE + i * SVG_T_YSIZE);
658 printf("<polyline points=\"%d,%d %d,%d %d,%d\" stroke=\"white\" stroke-width=\"2\"/>\n",
659 SVG_M_XSIZE, SVG_M_YSIZE + i * SVG_T_YSIZE,
660 SVG_M_XSIZE, SVG_M_YSIZE + SVG_G_YSIZE + i * SVG_T_YSIZE,
661 SVG_M_XSIZE + SVG_G_XSIZE, SVG_M_YSIZE + SVG_G_YSIZE + i * SVG_T_YSIZE);
664 for (j = 0; j < group[i]; j++) {
665 /* Set dp to TRUE (1) if current metric is based on integer values */
666 dp = (g_title[pos + j][0] == '~');
667 printf("<text x=\"%d\" y=\"%d\" style=\"fill: #%06x; stroke: none; font-size: 12px\">"
668 "%s (%.*f, %.*f)</text>\n",
669 5 + SVG_M_XSIZE + SVG_G_XSIZE, SVG_M_YSIZE + i * SVG_T_YSIZE + j * 15,
670 svg_colors[(pos + j) & SVG_COLORS_IDX_MASK], g_title[pos + j] + dp,
671 !dp * 2, *(spmin + pos + j), !dp * 2, *(spmax + pos + j));
674 /* Get global min and max value for current set of graphs */
675 get_global_extrema(pos, group[i], spmin, spmax);
677 /* Translate to proper position for current graph within current activity */
678 printf("<g transform=\"translate(%d,%d)\">\n",
679 SVG_M_XSIZE, SVG_M_YSIZE + SVG_G_YSIZE + i * SVG_T_YSIZE);
682 if (g_type == SVG_LINE_GRAPH) {
683 /* For line graphs */
684 if (*(spmax + pos) == 0) {
685 /* If all values are zero then set current max value to 1 */
689 lmax = *(spmax + pos);
691 /* Max value cannot be too small, else Y graduations will be meaningless */
692 if (lmax < SVG_H_GRIDNR * 0.01) {
693 lmax = SVG_H_GRIDNR * 0.01;
695 ypos = ygrid(lmax, &dp);
698 /* For bar graphs (used for %values) */
699 ypos = 25.0; /* Draw lines at 25%, 50%, 75% and 100% */
700 dp = 0; /* No decimals */
702 /* Max should be always 100% except for percentage values greater than 100% */
703 if (*(spmax + pos) > 100.0) {
704 lmax = *(spmax + pos);
710 yfactor = (double) -SVG_G_YSIZE / lmax;
713 printf("<polyline points=\"0,%.2f %d,%.2f\" style=\"vector-effect: non-scaling-stroke; "
714 "stroke: #202020\" transform=\"scale(1,%f)\"/>\n",
715 ypos * j, SVG_G_XSIZE, ypos * j, yfactor);
718 while (ypos * j <= lmax);
721 printf("<text x=\"0\" y=\"%ld\" style=\"fill: white; stroke: none; font-size: 12px; "
722 "text-anchor: end\">%.*f.</text>\n",
723 (long) (ypos * j * yfactor), dp, ypos * j);
726 while (ypos * j <= lmax);
728 k = xgrid(svg_p->record_hdr->ust_time, record_hdr->ust_time);
729 xfactor = (double) SVG_G_XSIZE / (record_hdr->ust_time - svg_p->record_hdr->ust_time);
730 stamp = *svg_p->record_hdr;
731 for (j = 0; (j <= SVG_V_GRIDNR) && (stamp.ust_time <= record_hdr->ust_time); j++) {
732 sa_get_record_timestamp_struct(flags, &stamp, &rectime, NULL);
733 set_record_timestamp_string(flags, &stamp, NULL, cur_time, 32, &rectime);
734 printf("<polyline points=\"%ld,0 %ld,%d\" style=\"vector-effect: non-scaling-stroke; "
735 "stroke: #202020\" transform=\"scale(%f,1)\"/>\n",
736 k * j, k * j, -SVG_G_YSIZE, xfactor);
737 printf("<text x=\"%ld\" y=\"10\" style=\"fill: white; stroke: none; font-size: 12px; "
738 "text-anchor: start\" transform=\"rotate(45,%ld,0)\">%s</text>\n",
739 (long) (k * j * xfactor), (long) (k * j * xfactor), cur_time);
742 if (!PRINT_LOCAL_TIME(flags)) {
743 printf("<text x=\"-10\" y=\"30\" style=\"fill: yellow; stroke: none; font-size: 12px; "
744 "text-anchor: end\">UTC</text>\n");
747 /* Draw current graphs set */
748 for (j = 0; j < group[i]; j++) {
749 out_p = *(out + pos + j);
750 if (g_type == SVG_LINE_GRAPH) {
752 printf("<path id=\"g%dp%d\" d=\"%s\" "
753 "style=\"vector-effect: non-scaling-stroke; "
754 "stroke: #%06x; stroke-width: 1; fill-opacity: 0\" "
755 "transform=\"scale(%f,%f)\"/>\n",
756 svg_p->graph_no, pos + j, out_p,
757 svg_colors[(pos + j) & SVG_COLORS_IDX_MASK],
761 else if (*out_p) { /* Ignore flat bars */
763 printf("<g style=\"fill: #%06x; stroke: none\" transform=\"scale(%f,%f)\">\n",
764 svg_colors[(pos + j) & SVG_COLORS_IDX_MASK], xfactor, yfactor);
765 printf("%s\n", out_p);
776 (svg_p->graph_no) += g_nr;
780 ***************************************************************************
781 * Display CPU statistics in SVG
784 * @a Activity structure with statistics.
785 * @curr Index in array for current sample statistics.
786 * @action Action expected from current function.
787 * @svg_p SVG specific parameters: Current graph number (.@graph_no),
788 * flag indicating that a restart record has been previously
789 * found (.@restart) and a pointer on a record header structure
790 * (.@record_hdr) containing the first stats sample.
791 * @itv Interval of time in jiffies (only with F_MAIN action).
792 * @record_hdr Pointer on record header of current stats sample.
793 ***************************************************************************
795 __print_funct_t svg_print_cpu_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
796 unsigned long long g_itv, struct record_header *record_hdr)
798 struct stats_cpu *scc, *scp;
801 char *title[] = {"CPU load"};
802 char *g_title1[] = {"%user", "%nice", "%system", "%iowait", "%steal", "%idle"};
803 char *g_title2[] = {"%usr", "%nice", "%sys", "%iowait", "%steal", "%irq", "%soft", "%guest", "%gnice", "%idle"};
804 static double *spmin, *spmax;
809 int i, j, k, pos, cpu_offline;
811 if (action & F_BEGIN) {
813 * Allocate arrays that will contain the graphs data
814 * and the min/max values.
816 out = allocate_graph_lines(10 * a->nr, &outsize, &spmin, &spmax);
819 if (action & F_MAIN) {
821 for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {
823 scc = (struct stats_cpu *) ((char *) a->buf[curr] + i * a->msize);
824 scp = (struct stats_cpu *) ((char *) a->buf[!curr] + i * a->msize);
826 /* Should current CPU (including CPU "all") be displayed? */
827 if (!(a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))))
834 if (i) { /* Don't test CPU "all" here */
836 * If the CPU is offline then it is omited from /proc/stat:
837 * All the fields couldn't have been read and the sum of them is zero.
838 * (Remember that guest/guest_nice times are already included in
841 if ((scc->cpu_user + scc->cpu_nice + scc->cpu_sys +
842 scc->cpu_iowait + scc->cpu_idle + scc->cpu_steal +
843 scc->cpu_hardirq + scc->cpu_softirq) == 0) {
845 * Set current struct fields (which have been set to zero)
846 * to values from previous iteration. Hence their values won't
847 * jump from zero when the CPU comes back online.
856 * Recalculate interval for current proc.
857 * If result is 0 then current CPU is a tickless one.
859 g_itv = get_per_cpu_interval(scc, scp);
863 if (!g_itv) { /* Current CPU is offline or tickless */
865 val = (cpu_offline ? 0.0 /* Offline CPU: %idle = 0% */
866 : 100.0); /* Tickless CPU: %idle = 100% */
868 if (DISPLAY_CPU_DEF(a->opt_flags)) {
871 else { /* DISPLAY_CPU_ALL(a->opt_flags) */
875 /* Check min/max values for %user, etc. */
876 for (k = 0; k < j; k++) {
877 if (0.0 < *(spmin + pos + k)) {
878 *(spmin + pos + k) = 0.0;
880 if (0.0 > *(spmax + pos + k)) {
881 *(spmax + pos + k) = 0.0;
886 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
888 out + pos + j, outsize + pos + j, svg_p->dt,
889 spmin + pos + j, spmax + pos + j);
894 if (DISPLAY_CPU_DEF(a->opt_flags)) {
896 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
897 &offset, ll_sp_value(scp->cpu_user, scc->cpu_user, g_itv),
898 out + pos, outsize + pos, svg_p->dt,
899 spmin + pos, spmax + pos);
903 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
905 (scc->cpu_user - scc->cpu_guest) < (scp->cpu_user - scp->cpu_guest) ?
907 ll_sp_value(scp->cpu_user - scp->cpu_guest,
908 scc->cpu_user - scc->cpu_guest, g_itv),
909 out + pos, outsize + pos, svg_p->dt,
910 spmin + pos, spmax + pos);
913 if (DISPLAY_CPU_DEF(a->opt_flags)) {
915 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
916 &offset, ll_sp_value(scp->cpu_nice, scc->cpu_nice, g_itv),
917 out + pos + 1, outsize + pos + 1, svg_p->dt,
918 spmin + pos + 1, spmax + pos + 1);
922 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
924 (scc->cpu_nice - scc->cpu_guest_nice) < (scp->cpu_nice - scp->cpu_guest_nice) ?
926 ll_sp_value(scp->cpu_nice - scp->cpu_guest_nice,
927 scc->cpu_nice - scc->cpu_guest_nice, g_itv),
928 out + pos + 1, outsize + pos + 1, svg_p->dt,
929 spmin + pos + 1, spmax + pos + 1);
932 if (DISPLAY_CPU_DEF(a->opt_flags)) {
934 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
936 ll_sp_value(scp->cpu_sys + scp->cpu_hardirq + scp->cpu_softirq,
937 scc->cpu_sys + scc->cpu_hardirq + scc->cpu_softirq,
939 out + pos + 2, outsize + pos + 2, svg_p->dt,
940 spmin + pos + 2, spmax + pos + 2);
944 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
945 &offset, ll_sp_value(scp->cpu_sys, scc->cpu_sys, g_itv),
946 out + pos + 2, outsize + pos + 2, svg_p->dt,
947 spmin + pos + 2, spmax + pos + 2);
951 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
952 &offset, ll_sp_value(scp->cpu_iowait, scc->cpu_iowait, g_itv),
953 out + pos + 3, outsize + pos + 3, svg_p->dt,
954 spmin + pos + 3, spmax + pos + 3);
957 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
958 &offset, ll_sp_value(scp->cpu_steal, scc->cpu_steal, g_itv),
959 out + pos + 4, outsize + pos + 4, svg_p->dt,
960 spmin + pos + 4, spmax + pos + 4);
962 if (DISPLAY_CPU_ALL(a->opt_flags)) {
964 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
965 &offset, ll_sp_value(scp->cpu_hardirq, scc->cpu_hardirq, g_itv),
966 out + pos + 5, outsize + pos + 5, svg_p->dt,
967 spmin + pos + 5, spmax + pos + 5);
970 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
971 &offset, ll_sp_value(scp->cpu_softirq, scc->cpu_softirq, g_itv),
972 out + pos + 6, outsize + pos + 6, svg_p->dt,
973 spmin + pos + 6, spmax + pos + 6);
976 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
977 &offset, ll_sp_value(scp->cpu_guest, scc->cpu_guest, g_itv),
978 out + pos + 7, outsize + pos + 7, svg_p->dt,
979 spmin + pos + 7, spmax + pos + 7);
982 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
983 &offset, ll_sp_value(scp->cpu_guest_nice, scc->cpu_guest_nice, g_itv),
984 out + pos + 8, outsize + pos + 8, svg_p->dt,
985 spmin + pos + 8, spmax + pos + 8);
994 cpuappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
996 (scc->cpu_idle < scp->cpu_idle ? 0.0 :
997 ll_sp_value(scp->cpu_idle, scc->cpu_idle, g_itv)),
998 out + pos + j, outsize + pos + j, svg_p->dt,
999 spmin + pos + j, spmax + pos + j);
1003 if (action & F_END) {
1004 for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {
1006 /* Should current CPU (including CPU "all") be displayed? */
1007 if (!(a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))))
1013 /* This is CPU "all" */
1014 strcpy(item_name, "all");
1017 sprintf(item_name, "%d", i - 1);
1020 if (DISPLAY_CPU_DEF(a->opt_flags)) {
1021 draw_activity_graphs(a->g_nr, SVG_BAR_GRAPH,
1022 title, g_title1, item_name, group1,
1023 spmin + pos, spmax + pos, out + pos, outsize + pos,
1027 draw_activity_graphs(a->g_nr, SVG_BAR_GRAPH,
1028 title, g_title2, item_name, group2,
1029 spmin + pos, spmax + pos, out + pos, outsize + pos,
1034 /* Free remaining structures */
1035 free_graphs(out, outsize, spmin, spmax);
1040 ***************************************************************************
1041 * Display task creation and context switch statistics in SVG
1044 * @a Activity structure with statistics.
1045 * @curr Index in array for current sample statistics.
1046 * @action Action expected from current function.
1047 * @svg_p SVG specific parameters: Current graph number (.@graph_no),
1048 * flag indicating that a restart record has been previously
1049 * found (.@restart) and a pointer on a record header structure
1050 * (.@record_hdr) containing the first stats sample.
1051 * @itv Interval of time in jiffies (only with F_MAIN action).
1052 * @record_hdr Pointer on record header of current stats sample.
1053 ***************************************************************************
1055 __print_funct_t svg_print_pcsw_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1056 unsigned long long itv, struct record_header *record_hdr)
1059 *spc = (struct stats_pcsw *) a->buf[curr],
1060 *spp = (struct stats_pcsw *) a->buf[!curr];
1061 int group[] = {1, 1};
1062 char *title[] = {"Switching activity", "Task creation"};
1063 char *g_title[] = {"cswch/s",
1065 static double *spmin, *spmax;
1067 static int *outsize;
1069 if (action & F_BEGIN) {
1071 * Allocate arrays that will contain the graphs data
1072 * and the min/max values.
1074 out = allocate_graph_lines(2, &outsize, &spmin, &spmax);
1077 if (action & F_MAIN) {
1078 /* Check for min/max values */
1079 save_extrema(1, 1, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
1082 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1083 S_VALUE(spp->context_switch, spc->context_switch, itv),
1084 out, outsize, svg_p->restart);
1086 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1087 S_VALUE(spp->processes, spc->processes, itv),
1088 out + 1, outsize + 1, svg_p->restart);
1091 if (action & F_END) {
1092 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1093 spmin, spmax, out, outsize, svg_p, record_hdr);
1095 /* Free remaining structures */
1096 free_graphs(out, outsize, spmin, spmax);
1101 ***************************************************************************
1102 * Display swap statistics in SVG
1105 * @a Activity structure with statistics.
1106 * @curr Index in array for current sample statistics.
1107 * @action Action expected from current function.
1108 * @svg_p SVG specific parameters: Current graph number (.@graph_no),
1109 * flag indicating that a restart record has been previously
1110 * found (.@restart) and a pointer on a record header structure
1111 * (.@record_hdr) containing the first stats sample.
1112 * @itv Interval of time in jiffies (only with F_MAIN action).
1113 * @record_hdr Pointer on record header of current stats sample.
1114 ***************************************************************************
1116 __print_funct_t svg_print_swap_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1117 unsigned long long itv, struct record_header *record_hdr)
1120 *ssc = (struct stats_swap *) a->buf[curr],
1121 *ssp = (struct stats_swap *) a->buf[!curr];
1123 char *title[] = {"Swap activity"};
1124 char *g_title[] = {"pswpin/s", "pswpout/s" };
1125 static double *spmin, *spmax;
1127 static int *outsize;
1129 if (action & F_BEGIN) {
1131 * Allocate arrays that will contain the graphs data
1132 * and the min/max values.
1134 out = allocate_graph_lines(2, &outsize, &spmin, &spmax);
1137 if (action & F_MAIN) {
1138 /* Check for min/max values */
1139 save_extrema(0, 2, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
1142 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1143 S_VALUE(ssp->pswpin, ssc->pswpin, itv),
1144 out, outsize, svg_p->restart);
1146 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1147 S_VALUE(ssp->pswpout, ssc->pswpout, itv),
1148 out + 1, outsize + 1, svg_p->restart);
1151 if (action & F_END) {
1152 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1153 spmin, spmax, out, outsize, svg_p, record_hdr);
1155 /* Free remaining structures */
1156 free_graphs(out, outsize, spmin, spmax);
1161 ***************************************************************************
1162 * Display paging statistics in SVG
1165 * @a Activity structure with statistics.
1166 * @curr Index in array for current sample statistics.
1167 * @action Action expected from current function.
1168 * @svg_p SVG specific parameters: Current graph number (.@graph_no),
1169 * flag indicating that a restart record has been previously
1170 * found (.@restart) and a pointer on a record header structure
1171 * (.@record_hdr) containing the first stats sample.
1172 * @itv Interval of time in jiffies (only with F_MAIN action).
1173 * @record_hdr Pointer on record header of current stats sample.
1174 ***************************************************************************
1176 __print_funct_t svg_print_paging_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1177 unsigned long long itv, struct record_header *record_hdr)
1180 *spc = (struct stats_paging *) a->buf[curr],
1181 *spp = (struct stats_paging *) a->buf[!curr];
1182 int group[] = {2, 2, 4};
1183 char *title[] = {"Paging activity (1)", "Paging activity (2)", "Paging activity (3)"};
1184 char *g_title[] = {"pgpgin/s", "pgpgout/s",
1185 "fault/s", "majflt/s",
1186 "pgfree/s", "pgscank/s", "pgscand/s", "pgsteal/s"};
1187 static double *spmin, *spmax;
1189 static int *outsize;
1191 if (action & F_BEGIN) {
1193 * Allocate arrays that will contain the graphs data
1194 * and the min/max values.
1196 out = allocate_graph_lines(8, &outsize, &spmin, &spmax);
1199 if (action & F_MAIN) {
1200 /* Check for min/max values */
1201 save_extrema(0, 8, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
1204 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1205 S_VALUE(spp->pgpgin, spc->pgpgin, itv),
1206 out, outsize, svg_p->restart);
1208 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1209 S_VALUE(spp->pgpgout, spc->pgpgout, itv),
1210 out + 1, outsize + 1, svg_p->restart);
1212 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1213 S_VALUE(spp->pgfault, spc->pgfault, itv),
1214 out + 2, outsize + 2, svg_p->restart);
1216 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1217 S_VALUE(spp->pgmajfault, spc->pgmajfault, itv),
1218 out + 3, outsize + 3, svg_p->restart);
1220 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1221 S_VALUE(spp->pgfree, spc->pgfree, itv),
1222 out + 4, outsize + 4, svg_p->restart);
1224 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1225 S_VALUE(spp->pgscan_kswapd, spc->pgscan_kswapd, itv),
1226 out + 5, outsize + 5, svg_p->restart);
1228 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1229 S_VALUE(spp->pgscan_direct, spc->pgscan_direct, itv),
1230 out + 6, outsize + 6, svg_p->restart);
1232 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1233 S_VALUE(spp->pgsteal, spc->pgsteal, itv),
1234 out + 7, outsize + 7, svg_p->restart);
1237 if (action & F_END) {
1238 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1239 spmin, spmax, out, outsize, svg_p, record_hdr);
1241 /* Free remaining structures */
1242 free_graphs(out, outsize, spmin, spmax);
1247 ***************************************************************************
1248 * Display I/O and transfer rate statistics in SVG.
1251 * @a Activity structure with statistics.
1252 * @curr Index in array for current sample statistics.
1253 * @action Action expected from current function.
1254 * @svg_p SVG specific parameters: Current graph number (.@graph_no),
1255 * flag indicating that a restart record has been previously
1256 * found (.@restart) and a pointer on a record header structure
1257 * (.@record_hdr) containing the first stats sample.
1258 * @itv Interval of time in jiffies (only with F_MAIN action).
1259 * @record_hdr Pointer on record header of current stats sample.
1260 ***************************************************************************
1262 __print_funct_t svg_print_io_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1263 unsigned long long itv, struct record_header *record_hdr)
1266 *sic = (struct stats_io *) a->buf[curr],
1267 *sip = (struct stats_io *) a->buf[!curr];
1268 int group[] = {3, 2};
1269 char *title[] = {"I/O and transfer rate statistics (1)", "I/O and transfer rate statistics (2)"};
1270 char *g_title[] = {"tps", "rtps", "wtps",
1271 "bread/s", "bwrtn/s"};
1272 static double *spmin, *spmax;
1274 static int *outsize;
1276 if (action & F_BEGIN) {
1278 * Allocate arrays that will contain the graphs data
1279 * and the min/max values.
1281 out = allocate_graph_lines(5, &outsize, &spmin, &spmax);
1284 if (action & F_MAIN) {
1285 /* Check for min/max values */
1286 save_extrema(0, 5, 0, (void *) a->buf[curr], (void *) a->buf[!curr],
1290 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1291 S_VALUE(sip->dk_drive, sic->dk_drive, itv),
1292 out, outsize, svg_p->restart);
1294 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1295 S_VALUE(sip->dk_drive_rio, sic->dk_drive_rio, itv),
1296 out + 1, outsize + 1, svg_p->restart);
1298 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1299 S_VALUE(sip->dk_drive_wio, sic->dk_drive_wio, itv),
1300 out + 2, outsize + 2, svg_p->restart);
1302 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1303 S_VALUE(sip->dk_drive_rblk, sic->dk_drive_rblk, itv),
1304 out + 3, outsize + 3, svg_p->restart);
1306 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1307 S_VALUE(sip->dk_drive_wblk, sic->dk_drive_wblk, itv),
1308 out + 4, outsize + 4, svg_p->restart);
1311 if (action & F_END) {
1312 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1313 spmin, spmax, out, outsize, svg_p, record_hdr);
1315 /* Free remaining structures */
1316 free_graphs(out, outsize, spmin, spmax);
1321 ***************************************************************************
1322 * Display memory statistics in SVG.
1325 * @a Activity structure with statistics.
1326 * @curr Index in array for current sample statistics.
1327 * @action Action expected from current function.
1328 * @svg_p SVG specific parameters: Current graph number (.@graph_no),
1329 * flag indicating that a restart record has been previously
1330 * found (.@restart) and a pointer on a record header structure
1331 * (.@record_hdr) containing the first stats sample.
1332 * @itv Interval of time in jiffies (only with F_MAIN action).
1333 * @record_hdr Pointer on record header of current stats sample.
1334 ***************************************************************************
1336 __print_funct_t svg_print_memory_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1337 unsigned long long itv, struct record_header *record_hdr)
1340 *smc = (struct stats_memory *) a->buf[curr];
1341 int group1a[] = {2, 2};
1342 int group1b[] = {1, 1};
1343 int group1c[] = {4, 5};
1344 int group2a[] = {3};
1345 int group2b[] = {1, 1};
1346 char *title1a[] = {"Memory utilization (1)", "Memory utilization (2)"};
1347 char *title1b[] = {"Memory utilization (3)", "Memory utilization (4)"};
1348 char *title1c[] = {"Memory utilization (5)", "Memory utilization (6)"};
1349 char *title2a[] = {"Swap utilization (1)"};
1350 char *title2b[] = {"Swap utilization (2)", "Swap utilization (3)"};
1351 char *g_title1a[] = {"MBmemfree", "MBmemused",
1352 "MBcached", "MBbuffers"};
1353 char *g_title1b[] = {"%memused", "%commit"};
1354 char *g_title1c[] = {"MBcommit", "MBactive", "MBinact", "MBdirty",
1355 "MBanonpg", "MBslab", "MBkstack", "MBpgtbl", "MBvmused"};
1356 char *g_title2a[] = {"MBswpfree", "MBswpused", "MBswpcad"};
1357 char *g_title2b[] = {"%swpused", "%swpcad"};
1358 static double *spmin, *spmax;
1360 static int *outsize;
1364 if (action & F_BEGIN) {
1366 * Allocate arrays that will contain the graphs data
1367 * and the min/max values.
1369 out = allocate_graph_lines(22, &outsize, &spmin, &spmax);
1372 if (action & F_MAIN) {
1373 /* Check for min/max values */
1374 save_extrema(0, 16, 0, (void *) a->buf[curr], NULL,
1376 /* Compute %memused min/max values */
1377 tval = smc->tlmkb ? SP_VALUE(smc->frmkb, smc->tlmkb, smc->tlmkb) : 0.0;
1378 if (tval > *(spmax + 16)) {
1379 *(spmax + 16) = tval;
1381 if (tval < *(spmin + 16)) {
1382 *(spmin + 16) = tval;
1384 /* Compute %commit min/max values */
1385 tval = (smc->tlmkb + smc->tlskb) ?
1386 SP_VALUE(0, smc->comkb, smc->tlmkb + smc->tlskb) : 0.0;
1387 if (tval > *(spmax + 17)) {
1388 *(spmax + 17) = tval;
1390 if (tval < *(spmin + 17)) {
1391 *(spmin + 17) = tval;
1393 /* Compute %swpused min/max values */
1395 SP_VALUE(smc->frskb, smc->tlskb, smc->tlskb) : 0.0;
1396 if (tval > *(spmax + 18)) {
1397 *(spmax + 18) = tval;
1399 if (tval < *(spmin + 18)) {
1400 *(spmin + 18) = tval;
1402 /* Compute %swpcad min/max values */
1403 tval = (smc->tlskb - smc->frskb) ?
1404 SP_VALUE(0, smc->caskb, smc->tlskb - smc->frskb) : 0.0;
1405 if (tval > *(spmax + 19)) {
1406 *(spmax + 19) = tval;
1408 if (tval < *(spmin + 19)) {
1409 *(spmin + 19) = tval;
1411 /* Compute memused min/max values in MB */
1412 tval = ((double) (smc->tlmkb - smc->frmkb)) / 1024;
1413 if (tval > *(spmax + 20)) {
1414 *(spmax + 20) = tval;
1416 if (tval < *(spmin + 20)) {
1417 *(spmin + 20) = tval;
1419 /* Compute swpused min/max values in MB */
1420 tval = ((double) (smc->tlskb - smc->frskb)) / 1024;
1421 if (tval > *(spmax + 21)) {
1422 *(spmax + 21) = tval;
1424 if (tval < *(spmin + 21)) {
1425 *(spmin + 21) = tval;
1429 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1430 ((double) smc->frmkb) / 1024,
1431 out, outsize, svg_p->restart);
1433 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1434 ((double) (smc->tlmkb - smc->frmkb)) / 1024,
1435 out + 1, outsize + 1, svg_p->restart);
1437 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1438 ((double) smc->camkb) / 1024,
1439 out + 2, outsize + 2, svg_p->restart);
1441 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1442 ((double) smc->bufkb) / 1024,
1443 out + 3, outsize + 3, svg_p->restart);
1445 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1446 ((double) smc->frskb) / 1024,
1447 out + 4, outsize + 4, svg_p->restart);
1449 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1450 ((double) (smc->tlskb - smc->frskb)) / 1024,
1451 out + 5, outsize + 5, svg_p->restart);
1453 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1454 ((double) smc->caskb) / 1024,
1455 out + 6, outsize + 6, svg_p->restart);
1457 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1458 ((double) smc->comkb) / 1024,
1459 out + 7, outsize + 7, svg_p->restart);
1461 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1462 ((double) smc->activekb) / 1024,
1463 out + 8, outsize + 8, svg_p->restart);
1465 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1466 ((double) smc->inactkb) / 1024,
1467 out + 9, outsize + 9, svg_p->restart);
1469 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1470 ((double) smc->dirtykb) / 1024,
1471 out + 10, outsize + 10, svg_p->restart);
1473 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1474 ((double) smc->anonpgkb) / 1024,
1475 out + 11, outsize + 11, svg_p->restart);
1477 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1478 ((double) smc->slabkb) / 1024,
1479 out + 12, outsize + 12, svg_p->restart);
1481 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1482 ((double) smc->kstackkb) / 1024,
1483 out + 13, outsize + 13, svg_p->restart);
1485 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1486 ((double) smc->pgtblkb) / 1024,
1487 out + 14, outsize + 14, svg_p->restart);
1489 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1490 ((double) smc->vmusedkb) / 1024,
1491 out + 15, outsize + 15, svg_p->restart);
1493 brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1496 SP_VALUE(smc->frmkb, smc->tlmkb, smc->tlmkb) : 0.0,
1497 out + 16, outsize + 16, svg_p->dt);
1499 brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1501 (smc->tlmkb + smc->tlskb) ?
1502 SP_VALUE(0, smc->comkb, smc->tlmkb + smc->tlskb) : 0.0,
1503 out + 17, outsize + 17, svg_p->dt);
1505 brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1508 SP_VALUE(smc->frskb, smc->tlskb, smc->tlskb) : 0.0,
1509 out + 18, outsize + 18, svg_p->dt);
1511 brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1513 (smc->tlskb - smc->frskb) ?
1514 SP_VALUE(0, smc->caskb, smc->tlskb - smc->frskb) : 0.0,
1515 out + 19, outsize + 19, svg_p->dt);
1518 if (action & F_END) {
1520 /* Conversion kB -> MB */
1521 for (i = 0; i < 16; i++) {
1522 *(spmin + i) /= 1024;
1523 *(spmax + i) /= 1024;
1526 if (DISPLAY_MEM_AMT(a->opt_flags)) {
1527 /* frmkb and tlmkb should be together because they will be drawn on the same view */
1528 *(spmax + 3) = *(spmax + 1);
1529 *(spmin + 3) = *(spmin + 1);
1530 /* Move memused min/max values */
1531 *(spmax + 1) = *(spmax + 20);
1532 *(spmin + 1) = *(spmin + 20);
1534 draw_activity_graphs(2, SVG_LINE_GRAPH, title1a, g_title1a, NULL, group1a,
1535 spmin, spmax, out, outsize, svg_p, record_hdr);
1536 draw_activity_graphs(2, SVG_BAR_GRAPH, title1b, g_title1b, NULL, group1b,
1537 spmin + 16, spmax + 16, out + 16, outsize + 16, svg_p, record_hdr);
1538 draw_activity_graphs(DISPLAY_MEM_ALL(a->opt_flags) ? 2 : 1,
1539 SVG_LINE_GRAPH, title1c, g_title1c, NULL, group1c,
1540 spmin + 7, spmax + 7, out + 7, outsize + 7, svg_p, record_hdr);
1543 if (DISPLAY_SWAP(a->opt_flags)) {
1544 /* Move swpused min/max values */
1545 *(spmax + 5) = *(spmax + 21);
1546 *(spmin + 5) = *(spmin + 21);
1548 draw_activity_graphs(1, SVG_LINE_GRAPH, title2a, g_title2a, NULL, group2a,
1549 spmin + 4, spmax + 4, out + 4, outsize + 4, svg_p, record_hdr);
1550 draw_activity_graphs(2, SVG_BAR_GRAPH, title2b, g_title2b, NULL, group2b,
1551 spmin + 18, spmax + 18, out + 18, outsize + 18, svg_p, record_hdr);
1554 /* Free remaining structures */
1555 free_graphs(out, outsize, spmin, spmax);
1560 ***************************************************************************
1561 * Display queue and load statistics in SVG
1564 * @a Activity structure with statistics.
1565 * @curr Index in array for current sample statistics.
1566 * @action Action expected from current function.
1567 * @svg_p SVG specific parameters: Current graph number (.@graph_no),
1568 * flag indicating that a restart record has been previously
1569 * found (.@restart) and a pointer on a record header structure
1570 * (.@record_hdr) containing the first stats sample.
1571 * @itv Interval of time in jiffies (only with F_MAIN action).
1572 * @record_hdr Pointer on record header of current stats sample.
1573 ***************************************************************************
1575 __print_funct_t svg_print_queue_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1576 unsigned long long itv, struct record_header *record_hdr)
1579 *sqc = (struct stats_queue *) a->buf[curr];
1580 int group[] = {2, 3, 1};
1581 char *title[] = {"Queue length", "Load average", "Task list"};
1582 char *g_title[] = {"~runq-sz", "~blocked",
1583 "ldavg-1", "ldavg-5", "ldavg-15",
1585 static double *spmin, *spmax;
1587 static int *outsize;
1589 if (action & F_BEGIN) {
1591 * Allocate arrays that will contain the graphs data
1592 * and the min/max values.
1594 out = allocate_graph_lines(6, &outsize, &spmin, &spmax);
1597 if (action & F_MAIN) {
1598 /* Check for min/max values */
1599 save_extrema(0, 2, 4, (void *) a->buf[curr], NULL,
1602 lniappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1603 (unsigned long) sqc->nr_running,
1604 out, outsize, svg_p->restart);
1606 lniappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1607 (unsigned long) sqc->procs_blocked,
1608 out + 1, outsize + 1, svg_p->restart);
1610 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1611 (double) sqc->load_avg_1 / 100,
1612 out + 2, outsize + 2, svg_p->restart);
1614 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1615 (double) sqc->load_avg_5 / 100,
1616 out + 3, outsize + 3, svg_p->restart);
1618 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1619 (double) sqc->load_avg_15 / 100,
1620 out + 4, outsize + 4, svg_p->restart);
1622 lniappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1623 (unsigned long) sqc->nr_threads,
1624 out + 5, outsize + 5, svg_p->restart);
1627 if (action & F_END) {
1628 /* Fix min/max values for load average */
1629 *(spmin + 2) /= 100; *(spmax + 2) /= 100;
1630 *(spmin + 3) /= 100; *(spmax + 3) /= 100;
1631 *(spmin + 4) /= 100; *(spmax + 4) /= 100;
1633 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH, title, g_title, NULL, group,
1634 spmin, spmax, out, outsize, svg_p, record_hdr);
1636 /* Free remaining structures */
1637 free_graphs(out, outsize, spmin, spmax);
1642 ***************************************************************************
1643 * Display network interfaces statistics in SVG
1646 * @a Activity structure with statistics.
1647 * @curr Index in array for current sample statistics.
1648 * @action Action expected from current function.
1649 * @svg_p SVG specific parameters: Current graph number (.@graph_no),
1650 * flag indicating that a restart record has been previously
1651 * found (.@restart) and a pointer on a record header structure
1652 * (.@record_hdr) containing the first stats sample.
1653 * @itv Interval of time in jiffies (only with F_MAIN action).
1654 * @record_hdr Pointer on record header of current stats sample.
1655 ***************************************************************************
1657 __print_funct_t svg_print_net_dev_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1658 unsigned long long itv, struct record_header *record_hdr)
1660 struct stats_net_dev *sndc, *sndp;
1661 int group1[] = {2, 2, 3};
1663 char *title1[] = {"Network statistics (1)", "Network statistics (2)",
1664 "Network statistics (3)"};
1665 char *title2[] = {"Network statistics (4)"};
1666 char *g_title1[] = {"rxpck/s", "txpck/s",
1668 "rxcmp/s", "txcmp/s", "rxmcst/s"};
1669 char *g_title2[] = {"%ifutil"};
1670 static double *spmin, *spmax;
1672 static int *outsize;
1674 double rxkb, txkb, ifutil;
1675 int i, j, k, pos, restart, *unregistered;
1677 if (action & F_BEGIN) {
1679 * Allocate arrays (#0..7) that will contain the graphs data
1680 * and the min/max values.
1681 * Also allocate one additional array (#8) for each interface:
1682 * out + 8 will contain the interface name,
1683 * outsize + 8 will contain a positive value (TRUE) if the interface
1684 * has either still not been registered, or has been unregistered.
1686 out = allocate_graph_lines(9 * a->nr, &outsize, &spmin, &spmax);
1689 if (action & F_MAIN) {
1690 restart = svg_p->restart;
1692 * Mark previously registered interfaces as now
1693 * possibly unregistered for all graphs.
1695 for (k = 0; k < a->nr; k++) {
1696 unregistered = outsize + k * 9 + 8;
1697 if (*unregistered == FALSE) {
1698 *unregistered = MAYBE;
1702 /* For each network interfaces structure */
1703 for (i = 0; i < a->nr; i++) {
1704 sndc = (struct stats_net_dev *) ((char *) a->buf[curr] + i * a->msize);
1705 if (!strcmp(sndc->interface, ""))
1706 /* Empty structure: Ignore it */
1709 /* Look for corresponding graph */
1710 for (k = 0; k < a->nr; k++) {
1711 item_name = *(out + k * 9 + 8);
1712 if (!strcmp(sndc->interface, item_name))
1717 /* Graph not found: Look for first free entry */
1718 for (k = 0; k < a->nr; k++) {
1719 item_name = *(out + k * 9 + 8);
1720 if (!strcmp(item_name, ""))
1724 /* No free graph entry: Graph for this item won't be drawn */
1729 unregistered = outsize + pos + 8;
1731 j = check_net_dev_reg(a, curr, !curr, i);
1732 sndp = (struct stats_net_dev *) ((char *) a->buf[!curr] + j * a->msize);
1735 * If current interface was marked as previously unregistered,
1736 * then set restart variable to TRUE so that the graph will be
1737 * discontinuous, and mark it as now registered.
1739 if (*unregistered == TRUE) {
1742 *unregistered = FALSE;
1744 if (!item_name[0]) {
1745 /* Save network interface name (if not already done) */
1746 strncpy(item_name, sndc->interface, CHUNKSIZE);
1747 item_name[CHUNKSIZE - 1] = '\0';
1750 /* Check for min/max values */
1751 save_extrema(7, 0, 0, (void *) sndc, (void *) sndp,
1752 itv, spmin + pos, spmax + pos);
1754 rxkb = S_VALUE(sndp->rx_bytes, sndc->rx_bytes, itv);
1755 txkb = S_VALUE(sndp->tx_bytes, sndc->tx_bytes, itv);
1756 ifutil = compute_ifutil(sndc, rxkb, txkb);
1757 if (ifutil < *(spmin + pos + 7)) {
1758 *(spmin + pos + 7) = ifutil;
1760 if (ifutil > *(spmax + pos + 7)) {
1761 *(spmax + pos + 7) = ifutil;
1765 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1766 S_VALUE(sndp->rx_packets, sndc->rx_packets, itv),
1767 out + pos, outsize + pos, restart);
1770 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1771 S_VALUE(sndp->tx_packets, sndc->tx_packets, itv),
1772 out + pos + 1, outsize + pos + 1, restart);
1775 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1777 out + pos + 2, outsize + pos + 2, restart);
1780 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1782 out + pos + 3, outsize + pos + 3, restart);
1785 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1786 S_VALUE(sndp->rx_compressed, sndc->rx_compressed, itv),
1787 out + pos + 4, outsize + pos + 4, restart);
1790 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1791 S_VALUE(sndp->tx_compressed, sndc->tx_compressed, itv),
1792 out + pos + 5, outsize + pos + 5, restart);
1795 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1796 S_VALUE(sndp->multicast, sndc->multicast, itv),
1797 out + pos + 6, outsize + pos + 6, restart);
1800 brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1802 out + pos + 7, outsize + pos + 7, svg_p->dt);
1805 /* Mark interfaces not seen here as now unregistered */
1806 for (k = 0; k < a->nr; k++) {
1807 unregistered = outsize + k * 9 + 8;
1808 if (*unregistered != FALSE) {
1809 *unregistered = TRUE;
1814 if (action & F_END) {
1815 for (i = 0; i < a->nr; i++) {
1817 * Check if there is something to display.
1818 * Don't test sndc->interface because maybe the network
1819 * interface has been registered later.
1825 /* Recalculate min and max values in kB, not in B */
1826 *(spmin + pos + 2) /= 1024;
1827 *(spmax + pos + 2) /= 1024;
1828 *(spmin + pos + 3) /= 1024;
1829 *(spmax + pos + 3) /= 1024;
1831 item_name = *(out + pos + 8);
1832 draw_activity_graphs(a->g_nr - 1, SVG_LINE_GRAPH,
1833 title1, g_title1, item_name, group1,
1834 spmin + pos, spmax + pos, out + pos, outsize + pos,
1836 draw_activity_graphs(1, SVG_BAR_GRAPH,
1837 title2, g_title2, item_name, group2,
1838 spmin + pos + 7, spmax + pos + 7, out + pos + 7, outsize + pos + 7,
1842 /* Free remaining structures */
1843 free_graphs(out, outsize, spmin, spmax);
1848 ***************************************************************************
1849 * Display CPU frequency statistics in SVG.
1852 * @a Activity structure with statistics.
1853 * @curr Index in array for current sample statistics.
1854 * @action Action expected from current function.
1855 * @svg_p SVG specific parameters: Current graph number (.@graph_no),
1856 * flag indicating that a restart record has been previously
1857 * found (.@restart) and a pointer on a record header structure
1858 * (.@record_hdr) containing the first stats sample.
1859 * @itv Interval of time in jiffies (only with F_MAIN action).
1860 * @record_hdr Pointer on record header of current stats sample.
1861 ***************************************************************************
1863 __print_funct_t svg_print_pwr_cpufreq_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1864 unsigned long long g_itv, struct record_header *record_hdr)
1866 struct stats_pwr_cpufreq *spc, *spp;
1868 char *title[] = {"CPU frequency"};
1869 char *g_title[] = {"MHz"};
1870 static double *spmin, *spmax;
1872 static int *outsize;
1876 if (action & F_BEGIN) {
1878 * Allocate arrays that will contain the graphs data
1879 * and the min/max values.
1881 out = allocate_graph_lines(a->nr, &outsize, &spmin, &spmax);
1884 if (action & F_MAIN) {
1886 for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {
1888 spc = (struct stats_pwr_cpufreq *) ((char *) a->buf[curr] + i * a->msize);
1889 spp = (struct stats_pwr_cpufreq *) ((char *) a->buf[!curr] + i * a->msize);
1891 /* Should current CPU (including CPU "all") be displayed? */
1892 if (!(a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))))
1897 recappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1898 ((double) spp->cpufreq) / 100,
1899 ((double) spc->cpufreq) / 100,
1900 out + i, outsize + i, svg_p->restart, svg_p->dt,
1901 spmin + i, spmax + i);
1905 if (action & F_END) {
1906 for (i = 0; (i < a->nr) && (i < a->bitmap->b_size + 1); i++) {
1908 /* Should current CPU (including CPU "all") be displayed? */
1909 if (!(a->bitmap->b_array[i >> 3] & (1 << (i & 0x07))))
1914 /* This is CPU "all" */
1915 strcpy(item_name, "all");
1918 sprintf(item_name, "%d", i - 1);
1921 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH,
1922 title, g_title, item_name, group,
1923 spmin + i, spmax + i, out + i, outsize + i,
1927 /* Free remaining structures */
1928 free_graphs(out, outsize, spmin, spmax);
1933 ***************************************************************************
1934 * Display fan statistics in SVG.
1937 * @a Activity structure with statistics.
1938 * @curr Index in array for current sample statistics.
1939 * @action Action expected from current function.
1940 * @svg_p SVG specific parameters: Current graph number (.@graph_no),
1941 * flag indicating that a restart record has been previously
1942 * found (.@restart) and a pointer on a record header structure
1943 * (.@record_hdr) containing the first stats sample.
1944 * @itv Interval of time in jiffies (only with F_MAIN action).
1945 * @record_hdr Pointer on record header of current stats sample.
1946 ***************************************************************************
1948 __print_funct_t svg_print_pwr_fan_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
1949 unsigned long long g_itv, struct record_header *record_hdr)
1951 struct stats_pwr_fan *spc, *spp;
1953 char *title[] = {"Fan speed"};
1954 char *g_title[] = {"~rpm"};
1955 static double *spmin, *spmax;
1957 static int *outsize;
1958 char item_name[MAX_SENSORS_DEV_LEN + 8];
1961 if (action & F_BEGIN) {
1963 * Allocate arrays that will contain the graphs data
1964 * and the min/max values.
1966 out = allocate_graph_lines(a->nr, &outsize, &spmin, &spmax);
1969 if (action & F_MAIN) {
1971 for (i = 0; i < a->nr; i++) {
1973 spc = (struct stats_pwr_fan *) ((char *) a->buf[curr] + i * a->msize);
1974 spp = (struct stats_pwr_fan *) ((char *) a->buf[!curr] + i * a->msize);
1977 recappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
1980 out + i, outsize + i, svg_p->restart, svg_p->dt,
1981 spmin + i, spmax + i);
1985 if (action & F_END) {
1986 for (i = 0; i < a->nr; i++) {
1988 spc = (struct stats_pwr_fan *) ((char *) a->buf[curr] + i * a->msize);
1990 snprintf(item_name, MAX_SENSORS_DEV_LEN + 8, "%d: %s", i + 1, spc->device);
1991 item_name[MAX_SENSORS_DEV_LEN + 7] = '\0';
1993 draw_activity_graphs(a->g_nr, SVG_LINE_GRAPH,
1994 title, g_title, item_name, group,
1995 spmin + i, spmax + i, out + i, outsize + i,
1999 /* Free remaining structures */
2000 free_graphs(out, outsize, spmin, spmax);
2005 ***************************************************************************
2006 * Display temperature statistics in SVG.
2009 * @a Activity structure with statistics.
2010 * @curr Index in array for current sample statistics.
2011 * @action Action expected from current function.
2012 * @svg_p SVG specific parameters: Current graph number (.@graph_no),
2013 * flag indicating that a restart record has been previously
2014 * found (.@restart) and a pointer on a record header structure
2015 * (.@record_hdr) containing the first stats sample.
2016 * @itv Interval of time in jiffies (only with F_MAIN action).
2017 * @record_hdr Pointer on record header of current stats sample.
2018 ***************************************************************************
2020 __print_funct_t svg_print_pwr_temp_stats(struct activity *a, int curr, int action, struct svg_parm *svg_p,
2021 unsigned long long g_itv, struct record_header *record_hdr)
2023 struct stats_pwr_temp *spc;
2025 char *title[] = {"Device temperature"};
2026 char *g1_title[] = {"~degC"};
2027 char *g2_title[] = {"%temp"};
2028 static double *spmin, *spmax;
2030 static int *outsize;
2031 char item_name[MAX_SENSORS_DEV_LEN + 8];
2035 if (action & F_BEGIN) {
2037 * Allocate arrays that will contain the graphs data
2038 * and the min/max values.
2040 out = allocate_graph_lines(2 * a->nr, &outsize, &spmin, &spmax);
2043 if (action & F_MAIN) {
2044 /* For each temperature sensor */
2045 for (i = 0; i < a->nr; i++) {
2047 spc = (struct stats_pwr_temp *) ((char *) a->buf[curr] + i * a->msize);
2049 /* Look for min/max values */
2050 if (spc->temp < *(spmin + 2 * i)) {
2051 *(spmin + 2 * i) = spc->temp;
2053 if (spc->temp > *(spmax + 2 * i)) {
2054 *(spmax + 2 * i) = spc->temp;
2056 tval = (spc->temp_max - spc->temp_min) ?
2057 (spc->temp - spc->temp_min) / (spc->temp_max - spc->temp_min) * 100 :
2059 if (tval < *(spmin + 2 * i + 1)) {
2060 *(spmin + 2 * i + 1) = tval;
2062 if (tval > *(spmax + 2 * i + 1)) {
2063 *(spmax + 2 * i + 1) = tval;
2067 lnappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
2069 out + 2 * i, outsize + 2 * i, svg_p->restart);
2071 brappend(record_hdr->ust_time - svg_p->record_hdr->ust_time,
2073 out + 2 * i + 1, outsize + 2 * i + 1, svg_p->dt);
2077 if (action & F_END) {
2078 for (i = 0; i < a->nr; i++) {
2080 spc = (struct stats_pwr_temp *) ((char *) a->buf[curr] + i * a->msize);
2082 snprintf(item_name, MAX_SENSORS_DEV_LEN + 8, "%d: %s", i + 1, spc->device);
2083 item_name[MAX_SENSORS_DEV_LEN + 7] = '\0';
2085 draw_activity_graphs(1, SVG_LINE_GRAPH,
2086 title, g1_title, item_name, group,
2087 spmin + 2 * i, spmax + 2 * i, out + 2 * i, outsize + 2 * i,
2089 draw_activity_graphs(1, SVG_BAR_GRAPH,
2090 title, g2_title, item_name, group,
2091 spmin + 2 * i + 1, spmax + 2 * i + 1,
2092 out + 2 * i + 1, outsize + 2 * i + 1,
2096 /* Free remaining structures */
2097 free_graphs(out, outsize, spmin, spmax);