xref: /dragonfly/usr.bin/kcollect/gnuplot.c (revision 9afa2da7b79948a540ad9d6cfe6b0c1871b2e21a)
1 /*
2  * Copyright (c) 2017 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include "kcollect.h"
33 
34 void
start_gnuplot(int ac __unused,char ** av __unused,const char * plotfile)35 start_gnuplot(int ac __unused, char **av __unused, const char *plotfile)
36 {
37           OutFP = popen("gnuplot", "w");
38           if (OutFP == NULL) {
39                     fprintf(stderr, "can't find gnuplot\n");
40                     exit(1);
41           }
42 
43           /*
44            * If plotfile is specified allow .jpg or .JPG or .png or .PNG
45            */
46           if (plotfile) {
47                     const char *ext;
48 
49                     if ((ext = strrchr(plotfile, '.')) == NULL) {
50                               ext = "";
51                     } else {
52                               ++ext;
53                     }
54                     if (strcmp(ext, "jpg") == 0 ||
55                         strcmp(ext, "JPG") == 0) {
56                               fprintf(OutFP, "set terminal jpeg size %d,%d\n",
57                                         OutputWidth, OutputHeight);
58                     } else if (strcmp(ext, "png") == 0 ||
59                                  strcmp(ext, "PNG") == 0) {
60                               fprintf(OutFP, "set terminal png size %d,%d\n",
61                                         OutputWidth, OutputHeight);
62                     } else {
63                               fprintf(stderr, "plotfile must be .jpg or .png\n");
64                               exit(1);
65                     }
66                     fprintf(OutFP, "set output \"%s\"\n", plotfile);
67           } else {
68                     fprintf(OutFP, "set terminal x11 persist size %d,%d\n",
69                               OutputWidth, OutputHeight);
70           }
71 }
72 
73 void
dump_gnuplot(kcollect_t * ary,size_t count)74 dump_gnuplot(kcollect_t *ary, size_t count)
75 {
76           int plot1[] = { KCOLLECT_MEMFRE, KCOLLECT_MEMCAC,
77                               KCOLLECT_MEMINA, KCOLLECT_MEMACT,
78                               KCOLLECT_MEMWIR, KCOLLECT_LOAD };
79           int plot2[] = { KCOLLECT_IDLEPCT, KCOLLECT_INTRPCT,
80                               KCOLLECT_SYSTPCT, KCOLLECT_USERPCT,
81                               KCOLLECT_SWAPPCT,
82                               KCOLLECT_VMFAULT, -1/*KCOLLECT_SYSCALLS*/,
83                               KCOLLECT_NLOOKUP };
84           const char *id1[] = {
85                               "free", "cache",
86                               "inact", "active",
87                               "wired", "load" };
88           const char *id2[] = {
89                               "idle", "intr", "system", "user",
90                               "swap",
91                               "faults", "syscalls", "nlookups" };
92           struct tm *tmv;
93           char buf[64];
94           uint64_t value;
95           time_t t;
96           double dv;
97           double smoothed_dv;
98           int i;
99           int j;
100           int jj;
101           int k;
102 
103           /*
104            * NOTE: be sure to reset any fields adjusted by the second plot,
105            *         in case we are streaming plots with -f.
106            */
107           fprintf(OutFP, "set xdata time\n");
108           fprintf(OutFP, "set timefmt \"%%d-%%b-%%Y %%H:%%M:%%S\"\n");
109           fprintf(OutFP, "set style fill solid 1.0\n");
110           fprintf(OutFP, "set multiplot layout 2,1\n");
111           fprintf(OutFP, "set key outside\n");
112           fprintf(OutFP, "set lmargin 10\n");
113           fprintf(OutFP, "set rmargin 25\n");
114           fprintf(OutFP, "set xtics rotate\n");
115           fprintf(OutFP, "set format x '%%H:%%M'\n");
116 
117           fprintf(OutFP, "set ylabel \"GB\"\n");
118 
119           fprintf(OutFP, "set yrange [0:%d]\n",
120                     (int)((KCOLLECT_GETSCALE(ary[0].data[KCOLLECT_MEMFRE]) +
121                            999999) / 1000000000));
122           fprintf(OutFP, "set autoscale y2\n");
123           fprintf(OutFP, "set y2label \"Load\"\n");
124           fprintf(OutFP, "set ytics nomirror\n");
125           fprintf(OutFP, "set y2tics nomirror\n");
126 
127           fprintf(OutFP,
128                     "plot "
129                     "\"-\" using 1:3 title \"%s\" with boxes lw 1, "
130                     "\"-\" using 1:3 title \"%s\" with boxes lw 1, "
131                     "\"-\" using 1:3 title \"%s\" with boxes lw 1, "
132                     "\"-\" using 1:3 title \"%s\" with boxes lw 1, "
133                     "\"-\" using 1:3 title \"%s\" with boxes lw 1, "
134                     "\"-\" using 1:3 axes x1y2 title \"%s\" with lines lw 1\n",
135                     id1[0], id1[1], id1[2], id1[3], id1[4], id1[5]);
136 
137           for (jj = 0; jj < (int)(sizeof(plot1) / sizeof(plot1[0])); ++jj) {
138                     j = plot1[jj];
139 
140                     smoothed_dv = 0.0;
141                     for (i = count - 1; i >= 2; --i) {
142                               /*
143                                * Timestamp
144                                */
145                               t = ary[i].realtime.tv_sec;
146                               if (t < 1000)
147                                         continue;
148                               if (UseGMT)
149                                         tmv = gmtime(&t);
150                               else
151                                         tmv = localtime(&t);
152                               strftime(buf, sizeof(buf), "%d-%b-%Y %H:%M:%S", tmv);
153                               value = ary[i].data[j];
154                               if (jj <= 4) {
155                                         for (k = jj + 1; k <= 4; ++k)
156                                                   value += ary[i].data[plot1[k]];
157                                         dv = (double)value / 1e9;
158                               } else {
159                                         dv = (double)value / 100.0;
160                               }
161                               if (SmoothOpt) {
162                                         if (i == (int)(count - 1)) {
163                                                   smoothed_dv = dv;
164                                         } else if (smoothed_dv < dv) {
165                                                   smoothed_dv =
166                                                    (smoothed_dv * 5.0 + 5 * dv) /
167                                                    10.0;
168                                         } else {
169                                                   smoothed_dv =
170                                                    (smoothed_dv * 9.0 + 1 * dv) /
171                                                    10.0;
172                                         }
173                                         dv = smoothed_dv;
174                               }
175                               fprintf(OutFP, "%s %6.2f\n", buf, dv);
176                     }
177                     fprintf(OutFP, "e\n");
178           }
179 
180           int ncpu = 4;
181           size_t ncpu_size = sizeof(ncpu);
182           sysctlbyname("hw.ncpu", &ncpu, &ncpu_size, NULL, 0);
183 
184           fprintf(OutFP, "set ylabel \"Cpu Utilization\"\n");
185           fprintf(OutFP, "set y2label \"MOps/sec (smoothed)\"\n");
186           fprintf(OutFP, "set ytics nomirror\n");
187           fprintf(OutFP, "set y2tics nomirror\n");
188           fprintf(OutFP, "set yrange [0:105]\n");
189           fprintf(OutFP, "set y2range [0:%d.0]\n",
190                     (93750 * ncpu + 999999) / 1000000);
191 
192           fprintf(OutFP,
193                     "plot "
194                     "\"-\" using 1:3 title \"%s\" with boxes lw 1, "
195                     "\"-\" using 1:3 title \"%s\" with boxes lw 1, "
196                     "\"-\" using 1:3 title \"%s\" with boxes lw 1, "
197                     "\"-\" using 1:3 title \"%s\" with boxes lw 1, "
198                     "\"-\" using 1:3 title \"%s\" with lines lw 1, "
199                     "\"-\" using 1:3 axes x1y2 title \"%s\" with lines lw 1, "
200                     "\"-\" using 1:3 axes x1y2 title \"%s\" with lines lw 1, "
201                     "\"-\" using 1:3 axes x1y2 title \"%s\" with lines lw 1\n",
202                     id2[0], id2[1], id2[2], id2[3], id2[4], id2[5], id2[6], id2[7]);
203 
204           for (jj = 0; jj < (int)(sizeof(plot2) / sizeof(plot2[0])); ++jj) {
205                     j = plot2[jj];
206 
207                     /*
208                      * Skip if disabled
209                      */
210                     if (j < 0) {
211                               fprintf(OutFP, "e\n");
212                               continue;
213                     }
214 
215                     /*
216                      * Dump points
217                      */
218                     smoothed_dv = 0.0;
219                     for (i = count - 1; i >= 2; --i) {
220                               /*
221                                * Timestamp
222                                */
223                               t = ary[i].realtime.tv_sec;
224                               if (t < 1000)
225                                         continue;
226                               if (UseGMT)
227                                         tmv = gmtime(&t);
228                               else
229                                         tmv = localtime(&t);
230                               strftime(buf, sizeof(buf), "%d-%b-%Y %H:%M:%S", tmv);
231                               value = ary[i].data[j];
232 
233                               if (jj <= 3) {
234                                         /*
235                                          * intr/sys/user/idle percentages
236                                          */
237                                         for (k = jj + 1; k <= 3; ++k)
238                                                   value += ary[i].data[plot2[k]];
239                                         dv = (double)value / 100.0;
240                                         if (SmoothOpt) {
241                                                   if (i == (int)(count - 1)) {
242                                                             smoothed_dv = dv;
243                                                   } else if (smoothed_dv < dv) {
244                                                             smoothed_dv =
245                                                              (smoothed_dv * 5.0 + 5 * dv) /
246                                                            10.0;
247                                                   } else {
248                                                             smoothed_dv =
249                                                              (smoothed_dv * 9.0 + 1 * dv) /
250                                                            10.0;
251                                                   }
252                                                   dv = smoothed_dv;
253                                         }
254                               } else {
255                                         if (jj >= 5) {
256                                                   /* fault counters */
257                                                   dv = (double)value / KCOLLECT_INTERVAL;
258                                                   dv = dv / 1e6;
259                                         } else {
260                                                   /* swap percentage (line graph) */
261                                                   dv = (double)value / 100.0;
262                                         }
263                                         if (i == (int)(count - 1)) {
264                                                   smoothed_dv = dv;
265                                         } else if (smoothed_dv < dv) {
266                                                   smoothed_dv =
267                                                    (smoothed_dv * 5.0 + 5 * dv) /
268                                                    10.0;
269                                         } else {
270                                                   smoothed_dv = (smoothed_dv * 9.0 + dv) /
271                                                                   10.0;
272                                         }
273                                         dv = smoothed_dv;
274                               }
275                               fprintf(OutFP, "%s %6.2f\n", buf, dv);
276                     }
277                     fprintf(OutFP, "e\n");
278           }
279           fflush(OutFP);
280 }
281