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, 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 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, KCOLLECT_SYSCALLS, KCOLLECT_NLOOKUP }; 83 const char *id1[] = { 84 "free", "cache", 85 "inact", "active", 86 "wired", "load" }; 87 const char *id2[] = { 88 "idle", "intr", "system", "user", 89 "swap", 90 "faults", "syscalls", "nlookups" }; 91 struct tm *tmv; 92 char buf[64]; 93 uint64_t value; 94 time_t t; 95 double dv; 96 double smoothed_dv; 97 int i; 98 int j; 99 int jj; 100 int k; 101 102 /* 103 * NOTE: be sure to reset any fields adjusted by the second plot, 104 * in case we are streaming plots with -f. 105 */ 106 fprintf(OutFP, "set xdata time\n"); 107 fprintf(OutFP, "set timefmt \"%%d-%%b-%%Y %%H:%%M:%%S\"\n"); 108 fprintf(OutFP, "set style fill solid 1.0\n"); 109 fprintf(OutFP, "set multiplot layout 2,1\n"); 110 fprintf(OutFP, "set key outside\n"); 111 fprintf(OutFP, "set lmargin 10\n"); 112 fprintf(OutFP, "set rmargin 25\n"); 113 fprintf(OutFP, "set xtics rotate\n"); 114 fprintf(OutFP, "set format x '%%H:%%M'\n"); 115 116 fprintf(OutFP, "set ylabel \"GB\"\n"); 117 118 fprintf(OutFP, "set yrange [0:%d]\n", 119 (int)((KCOLLECT_GETSCALE(ary[0].data[KCOLLECT_MEMFRE]) + 120 999999) / 1000000000)); 121 fprintf(OutFP, "set autoscale y2\n"); 122 fprintf(OutFP, "set y2label \"Load\"\n"); 123 fprintf(OutFP, "set ytics nomirror\n"); 124 fprintf(OutFP, "set y2tics nomirror\n"); 125 126 fprintf(OutFP, 127 "plot " 128 "\"-\" using 1:3 title \"%s\" with boxes lw 1, " 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 axes x1y2 title \"%s\" with lines lw 1\n", 134 id1[0], id1[1], id1[2], id1[3], id1[4], id1[5]); 135 136 for (jj = 0; jj < (int)(sizeof(plot1) / sizeof(plot1[0])); ++jj) { 137 j = plot1[jj]; 138 139 smoothed_dv = 0.0; 140 for (i = count - 1; i >= 2; --i) { 141 /* 142 * Timestamp 143 */ 144 t = ary[i].realtime.tv_sec; 145 if (t < 1000) 146 continue; 147 if (UseGMT) 148 tmv = gmtime(&t); 149 else 150 tmv = localtime(&t); 151 strftime(buf, sizeof(buf), "%d-%b-%Y %H:%M:%S", tmv); 152 value = ary[i].data[j]; 153 if (jj <= 4) { 154 for (k = jj + 1; k <= 4; ++k) 155 value += ary[i].data[plot1[k]]; 156 dv = (double)value / 1e9; 157 } else { 158 dv = (double)value / 100.0; 159 } 160 if (SmoothOpt) { 161 if (i == (int)(count - 1)) { 162 smoothed_dv = dv; 163 } else if (smoothed_dv < dv) { 164 smoothed_dv = 165 (smoothed_dv * 5.0 + 5 * dv) / 166 10.0; 167 } else { 168 smoothed_dv = 169 (smoothed_dv * 9.0 + 1 * dv) / 170 10.0; 171 } 172 dv = smoothed_dv; 173 } 174 fprintf(OutFP, "%s %6.2f\n", buf, dv); 175 } 176 fprintf(OutFP, "e\n"); 177 } 178 179 fprintf(OutFP, "set ylabel \"Cpu Utilization\"\n"); 180 fprintf(OutFP, "set y2label \"MOps/sec (smoothed)\"\n"); 181 fprintf(OutFP, "set ytics nomirror\n"); 182 fprintf(OutFP, "set y2tics nomirror\n"); 183 fprintf(OutFP, "set yrange [0:105]\n"); 184 fprintf(OutFP, "set y2range [0:1.0]\n"); 185 186 fprintf(OutFP, 187 "plot " 188 "\"-\" using 1:3 title \"%s\" with boxes lw 1, " 189 "\"-\" using 1:3 title \"%s\" with boxes lw 1, " 190 "\"-\" using 1:3 title \"%s\" with boxes lw 1, " 191 "\"-\" using 1:3 title \"%s\" with boxes lw 1, " 192 "\"-\" using 1:3 title \"%s\" with lines lw 1, " 193 "\"-\" using 1:3 axes x1y2 title \"%s\" with lines lw 1, " 194 "\"-\" using 1:3 axes x1y2 title \"%s\" with lines lw 1, " 195 "\"-\" using 1:3 axes x1y2 title \"%s\" with lines lw 1\n", 196 id2[0], id2[1], id2[2], id2[3], id2[4], id2[5], id2[6], id2[7]); 197 198 for (jj = 0; jj < (int)(sizeof(plot2) / sizeof(plot2[0])); ++jj) { 199 j = plot2[jj]; 200 201 smoothed_dv = 0.0; 202 for (i = count - 1; i >= 2; --i) { 203 /* 204 * Timestamp 205 */ 206 t = ary[i].realtime.tv_sec; 207 if (t < 1000) 208 continue; 209 if (UseGMT) 210 tmv = gmtime(&t); 211 else 212 tmv = localtime(&t); 213 strftime(buf, sizeof(buf), "%d-%b-%Y %H:%M:%S", tmv); 214 value = ary[i].data[j]; 215 216 if (jj <= 3) { 217 /* 218 * intr/sys/user/idle percentages 219 */ 220 for (k = jj + 1; k <= 3; ++k) 221 value += ary[i].data[plot2[k]]; 222 dv = (double)value / 100.0; 223 if (SmoothOpt) { 224 if (i == (int)(count - 1)) { 225 smoothed_dv = dv; 226 } else if (smoothed_dv < dv) { 227 smoothed_dv = 228 (smoothed_dv * 5.0 + 5 * dv) / 229 10.0; 230 } else { 231 smoothed_dv = 232 (smoothed_dv * 9.0 + 1 * dv) / 233 10.0; 234 } 235 dv = smoothed_dv; 236 } 237 } else { 238 if (jj >= 5) { 239 /* fault counters */ 240 dv = (double)value / KCOLLECT_INTERVAL; 241 dv = dv / 1e6; 242 } else { 243 /* swap percentage (line graph) */ 244 dv = (double)value / 100.0; 245 } 246 if (i == (int)(count - 1)) { 247 smoothed_dv = dv; 248 } else if (smoothed_dv < dv) { 249 smoothed_dv = 250 (smoothed_dv * 5.0 + 5 * dv) / 251 10.0; 252 } else { 253 smoothed_dv = (smoothed_dv * 9.0 + dv) / 254 10.0; 255 } 256 dv = smoothed_dv; 257 } 258 fprintf(OutFP, "%s %6.2f\n", buf, dv); 259 } 260 fprintf(OutFP, "e\n"); 261 } 262 fflush(OutFP); 263 } 264