kcollect - Implement gnuplot output feature
[dragonfly.git] / usr.bin / kcollect / gnuplot.c
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
35 start_gnuplot(int ac __unused, char **av __unused)
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 void
45 dump_gnuplot(kcollect_t *ary, size_t count, const char *plotfile)
46 {
47         int plot1[] = { KCOLLECT_MEMWIR, KCOLLECT_MEMACT,
48                         KCOLLECT_MEMINA, KCOLLECT_MEMCAC,
49                         KCOLLECT_MEMFRE, KCOLLECT_LOAD };
50         int plot2[] = { KCOLLECT_USERPCT, KCOLLECT_SYSTPCT,
51                         KCOLLECT_INTRPCT, KCOLLECT_IDLEPCT,
52                         KCOLLECT_VMFAULT, KCOLLECT_SYSCALLS, KCOLLECT_NLOOKUP };
53         struct tm *tmv;
54         char buf[64];
55         uint64_t value;
56         time_t t;
57         double dv;
58         double smoothed_dv;
59         int i;
60         int j;
61         int jj;
62         int k;
63
64         /*
65          * If plotfile is specified allow .jpg or .JPG or .png or .PNG
66          */
67         if (plotfile) {
68                 const char *ext;
69
70                 if ((ext = strrchr(plotfile, '.')) == NULL) {
71                         ext = "";
72                 } else {
73                         ++ext;
74                 }
75                 if (strcmp(ext, "jpg") == 0 ||
76                     strcmp(ext, "JPG") == 0) {
77                         fprintf(OutFP, "set terminal jpeg size %d,%d\n",
78                                 OutputWidth, OutputHeight);
79                 } else if (strcmp(ext, "png") == 0 ||
80                            strcmp(ext, "PNG") == 0) {
81                         fprintf(OutFP, "set terminal png size %d,%d\n",
82                                 OutputWidth, OutputHeight);
83                 } else {
84                         fprintf(stderr, "plotfile must be .jpg or .png\n");
85                         exit(1);
86                 }
87                 fprintf(OutFP, "set output \"%s\"\n", plotfile);
88         } else {
89                 fprintf(OutFP, "set terminal x11 persist size %d,%d\n",
90                         OutputWidth, OutputHeight);
91         }
92
93         /*
94          * NOTE: be sure to reset any fields adjusted by the second plot,
95          *       in case we are streaming plots with -f.
96          */
97         fprintf(OutFP, "set xdata time\n");
98         fprintf(OutFP, "set timefmt \"%%d-%%b-%%Y %%H:%%M:%%S\"\n");
99         fprintf(OutFP, "set style fill solid 1.0\n");
100         fprintf(OutFP, "set multiplot layout 2,1\n");
101         fprintf(OutFP, "set key outside\n");
102         fprintf(OutFP, "set lmargin 10\n");
103         fprintf(OutFP, "set rmargin 25\n");
104         fprintf(OutFP, "set xtics rotate\n");
105         fprintf(OutFP, "set format x '%%H:%%M'\n");
106
107         fprintf(OutFP, "set ylabel \"GB\"\n");
108
109         fprintf(OutFP, "set yrange [0:%d]\n",
110                 (int)((KCOLLECT_GETSCALE(ary[0].data[KCOLLECT_MEMFRE]) +
111                        999999) / 1000000000));
112         fprintf(OutFP, "set autoscale y2\n");
113         fprintf(OutFP, "set y2label \"Load\"\n");
114         fprintf(OutFP, "set ytics nomirror\n");
115         fprintf(OutFP, "set y2tics nomirror\n");
116
117         fprintf(OutFP,
118                 "plot "
119                 "\"-\" using 1:3 title \"wired\" with boxes lw 1, "
120                 "\"-\" using 1:3 title \"active\" with boxes lw 1, "
121                 "\"-\" using 1:3 title \"inact\" with boxes lw 1, "
122                 "\"-\" using 1:3 title \"cache\" with boxes lw 1, "
123                 "\"-\" using 1:3 title \"free\" with boxes lw 1, "
124                 "\"-\" using 1:3 axes x1y2 title \"load\" with lines lw 1\n");
125
126         for (jj = 0; jj < (int)(sizeof(plot1) / sizeof(plot1[0])); ++jj) {
127                 j = plot1[jj];
128
129                 for (i = count - 1; i >= 2; --i) {
130                         /*
131                          * Timestamp
132                          */
133                         t = ary[i].realtime.tv_sec;
134                         if (t < 1000)
135                                 continue;
136                         if (UseGMT)
137                                 tmv = gmtime(&t);
138                         else
139                                 tmv = localtime(&t);
140                         strftime(buf, sizeof(buf), "%d-%b-%Y %H:%M:%S", tmv);
141                         value = ary[i].data[j];
142                         if (jj <= 4) {
143                                 for (k = jj + 1; k <= 4; ++k)
144                                         value += ary[i].data[plot1[k]];
145                                 dv = (double)value / 1e9;
146                         } else {
147                                 dv = (double)value / 100.0;
148                         }
149                         fprintf(OutFP, "%s %6.2f\n", buf, dv);
150                 }
151                 fprintf(OutFP, "e\n");
152         }
153
154         fprintf(OutFP, "set ylabel \"Cpu Utilization\"\n");
155         fprintf(OutFP, "set y2label \"MOps/sec (smoothed)\"\n");
156         fprintf(OutFP, "set ytics nomirror\n");
157         fprintf(OutFP, "set y2tics nomirror\n");
158         fprintf(OutFP, "set yrange [0:105]\n");
159         fprintf(OutFP, "set y2range [0:1.0]\n");
160
161         fprintf(OutFP,
162                 "plot "
163                 "\"-\" using 1:3 title \"user\" with boxes lw 1, "
164                 "\"-\" using 1:3 title \"system\" with boxes lw 1, "
165                 "\"-\" using 1:3 title \"intr\" with boxes lw 1, "
166                 "\"-\" using 1:3 title \"idle\" with boxes lw 1, "
167                 "\"-\" using 1:3 axes x1y2 title \"faults\" with lines lw 1, "
168                 "\"-\" using 1:3 axes x1y2 title \"syscalls\" with lines lw 1, "
169                 "\"-\" using 1:3 axes x1y2 title \"nlookup\" with lines lw 1\n"
170         );
171
172         for (jj = 0; jj < (int)(sizeof(plot2) / sizeof(plot2[0])); ++jj) {
173                 j = plot2[jj];
174
175                 smoothed_dv = 0.0;
176                 for (i = count - 1; i >= 2; --i) {
177                         /*
178                          * Timestamp
179                          */
180                         t = ary[i].realtime.tv_sec;
181                         if (t < 1000)
182                                 continue;
183                         if (UseGMT)
184                                 tmv = gmtime(&t);
185                         else
186                                 tmv = localtime(&t);
187                         strftime(buf, sizeof(buf), "%d-%b-%Y %H:%M:%S", tmv);
188                         value = ary[i].data[j];
189
190                         if (jj <= 3) {
191                                 for (k = jj + 1; k <= 3; ++k)
192                                         value += ary[i].data[plot2[k]];
193                                 dv = (double)value / 100.0;
194                         } else {
195                                 dv = (double)value / KCOLLECT_INTERVAL;
196                                 dv = dv / 1e6;
197                                 smoothed_dv = (smoothed_dv * 9.0 + dv) / 10.0;
198                                 dv = smoothed_dv;
199                         }
200                         fprintf(OutFP, "%s %6.2f\n", buf, dv);
201                 }
202                 fprintf(OutFP, "e\n");
203         }
204         fflush(OutFP);
205 }