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, -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