1 /* 2 * Copyright (c) 1998 Kenneth D. Merry. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 /* 31 * Copyright (c) 1980, 1992, 1993 32 * The Regents of the University of California. All rights reserved. 33 * 34 * Redistribution and use in source and binary forms, with or without 35 * modification, are permitted provided that the following conditions 36 * are met: 37 * 1. Redistributions of source code must retain the above copyright 38 * notice, this list of conditions and the following disclaimer. 39 * 2. Redistributions in binary form must reproduce the above copyright 40 * notice, this list of conditions and the following disclaimer in the 41 * documentation and/or other materials provided with the distribution. 42 * 3. All advertising materials mentioning features or use of this software 43 * must display the following acknowledgement: 44 * This product includes software developed by the University of 45 * California, Berkeley and its contributors. 46 * 4. Neither the name of the University nor the names of its contributors 47 * may be used to endorse or promote products derived from this software 48 * without specific prior written permission. 49 * 50 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 51 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 52 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 53 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 54 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 55 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 56 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 57 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 58 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 59 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 60 * SUCH DAMAGE. 61 */ 62 63 #ifndef lint 64 static char sccsid[] = "@(#)iostat.c 8.1 (Berkeley) 6/6/93"; 65 #endif not lint 66 67 #include <sys/param.h> 68 #include <sys/dkstat.h> 69 70 #include <string.h> 71 #include <stdlib.h> 72 #include <nlist.h> 73 #include <paths.h> 74 #include <devstat.h> 75 #include <err.h> 76 #include "systat.h" 77 #include "extern.h" 78 #include "devs.h" 79 80 struct statinfo cur, last; 81 82 static int linesperregion; 83 static double etime; 84 static int numbers = 0; /* default display bar graphs */ 85 static int kbpt = 0; /* default ms/seek shown */ 86 87 static int barlabels __P((int)); 88 static void histogram __P((long double, int, double)); 89 static int numlabels __P((int)); 90 static int devstats __P((int, int, int)); 91 static void stat1 __P((int, int)); 92 93 WINDOW * 94 openiostat() 95 { 96 return (subwin(stdscr, LINES-1-5, 0, 5, 0)); 97 } 98 99 void 100 closeiostat(w) 101 WINDOW *w; 102 { 103 if (w == NULL) 104 return; 105 wclear(w); 106 wrefresh(w); 107 delwin(w); 108 } 109 110 int 111 initiostat() 112 { 113 if (num_devices = getnumdevs() < 0) 114 return(0); 115 116 cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); 117 last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); 118 bzero(cur.dinfo, sizeof(struct devinfo)); 119 bzero(last.dinfo, sizeof(struct devinfo)); 120 121 /* 122 * This value for maxshowdevs (100) is bogus. I'm not sure exactly 123 * how to calculate it, though. 124 */ 125 if (dsinit(100, &cur, &last, NULL) != 1) 126 return(0); 127 128 return(1); 129 } 130 131 void 132 fetchiostat() 133 { 134 struct devinfo *tmp_dinfo; 135 size_t len; 136 int err; 137 138 len = sizeof(cur.cp_time); 139 err = sysctlbyname("kern.cp_time", &cur.cp_time, &len, NULL, 0); 140 if (err || len != sizeof(cur.cp_time)) { 141 perror("kern.cp_time"); 142 exit (1); 143 } 144 tmp_dinfo = last.dinfo; 145 last.dinfo = cur.dinfo; 146 cur.dinfo = tmp_dinfo; 147 148 last.busy_time = cur.busy_time; 149 150 /* 151 * Here what we want to do is refresh our device stats. 152 * getdevs() returns 1 when the device list has changed. 153 * If the device list has changed, we want to go through 154 * the selection process again, in case a device that we 155 * were previously displaying has gone away. 156 */ 157 switch (getdevs(&cur)) { 158 case -1: 159 errx(1, "%s", devstat_errbuf); 160 break; 161 case 1: 162 cmdiostat("refresh", NULL); 163 break; 164 default: 165 break; 166 } 167 num_devices = cur.dinfo->numdevs; 168 generation = cur.dinfo->generation; 169 170 } 171 172 #define INSET 10 173 174 void 175 labeliostat() 176 { 177 int row; 178 179 row = 0; 180 wmove(wnd, row, 0); wclrtobot(wnd); 181 mvwaddstr(wnd, row++, INSET, 182 "/0 /10 /20 /30 /40 /50 /60 /70 /80 /90 /100"); 183 mvwaddstr(wnd, row++, 0, "cpu user|"); 184 mvwaddstr(wnd, row++, 0, " nice|"); 185 mvwaddstr(wnd, row++, 0, " system|"); 186 mvwaddstr(wnd, row++, 0, "interrupt|"); 187 mvwaddstr(wnd, row++, 0, " idle|"); 188 if (numbers) 189 row = numlabels(row + 1); 190 else 191 row = barlabels(row + 1); 192 } 193 194 static int 195 numlabels(row) 196 int row; 197 { 198 int i, col, regions, ndrives; 199 char tmpstr[10]; 200 201 #define COLWIDTH 17 202 #define DRIVESPERLINE ((wnd->_maxx - INSET) / COLWIDTH) 203 for (ndrives = 0, i = 0; i < num_devices; i++) 204 if (dev_select[i].selected) 205 ndrives++; 206 regions = howmany(ndrives, DRIVESPERLINE); 207 /* 208 * Deduct -regions for blank line after each scrolling region. 209 */ 210 linesperregion = (wnd->_maxy - row - regions) / regions; 211 /* 212 * Minimum region contains space for two 213 * label lines and one line of statistics. 214 */ 215 if (linesperregion < 3) 216 linesperregion = 3; 217 col = INSET; 218 for (i = 0; i < num_devices; i++) 219 if (dev_select[i].selected) { 220 if (col + COLWIDTH >= wnd->_maxx - INSET) { 221 col = INSET, row += linesperregion + 1; 222 if (row > wnd->_maxy - (linesperregion + 1)) 223 break; 224 } 225 sprintf(tmpstr, "%s%d", dev_select[i].device_name, 226 dev_select[i].unit_number); 227 mvwaddstr(wnd, row, col + 4, tmpstr); 228 mvwaddstr(wnd, row + 1, col, " KB/t tps MB/s "); 229 col += COLWIDTH; 230 } 231 if (col) 232 row += linesperregion + 1; 233 return (row); 234 } 235 236 static int 237 barlabels(row) 238 int row; 239 { 240 int i; 241 char tmpstr[10]; 242 243 mvwaddstr(wnd, row++, INSET, 244 "/0 /10 /20 /30 /40 /50 /60 /70 /80 /90 /100"); 245 linesperregion = 2 + kbpt; 246 for (i = 0; i < num_devices; i++) 247 if (dev_select[i].selected) { 248 if (row > wnd->_maxy - linesperregion) 249 break; 250 sprintf(tmpstr, "%s%d", dev_select[i].device_name, 251 dev_select[i].unit_number); 252 mvwprintw(wnd, row++, 0, "%-5.5s MB/s|", 253 tmpstr); 254 mvwaddstr(wnd, row++, 0, " tps|"); 255 if (kbpt) 256 mvwaddstr(wnd, row++, 0, " KB/t|"); 257 } 258 return (row); 259 } 260 261 262 void 263 showiostat() 264 { 265 register long t; 266 register int i, row, col; 267 268 #define X(fld) t = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = t 269 etime = 0; 270 for(i = 0; i < CPUSTATES; i++) { 271 X(cp_time); 272 etime += cur.cp_time[i]; 273 } 274 if (etime == 0.0) 275 etime = 1.0; 276 etime /= hertz; 277 row = 1; 278 for (i = 0; i < CPUSTATES; i++) 279 stat1(row++, i); 280 if (!numbers) { 281 row += 2; 282 for (i = 0; i < num_devices; i++) 283 if (dev_select[i].selected) { 284 if (row > wnd->_maxy - linesperregion) 285 break; 286 row = devstats(row, INSET, i); 287 } 288 return; 289 } 290 col = INSET; 291 wmove(wnd, row + linesperregion, 0); 292 wdeleteln(wnd); 293 wmove(wnd, row + 3, 0); 294 winsertln(wnd); 295 for (i = 0; i < num_devices; i++) 296 if (dev_select[i].selected) { 297 if (col + COLWIDTH >= wnd->_maxx - INSET) { 298 col = INSET, row += linesperregion + 1; 299 if (row > wnd->_maxy - (linesperregion + 1)) 300 break; 301 wmove(wnd, row + linesperregion, 0); 302 wdeleteln(wnd); 303 wmove(wnd, row + 3, 0); 304 winsertln(wnd); 305 } 306 (void) devstats(row + 3, col, i); 307 col += COLWIDTH; 308 } 309 } 310 311 static int 312 devstats(row, col, dn) 313 int row, col, dn; 314 { 315 long double transfers_per_second; 316 long double kb_per_transfer, mb_per_second; 317 long double busy_seconds; 318 int di; 319 320 di = dev_select[dn].position; 321 322 busy_seconds = compute_etime(cur.busy_time, last.busy_time); 323 324 if (compute_stats(&cur.dinfo->devices[di], &last.dinfo->devices[di], 325 busy_seconds, NULL, NULL, NULL, 326 &kb_per_transfer, &transfers_per_second, 327 &mb_per_second, NULL, NULL) != 0) 328 errx(1, "%s", devstat_errbuf); 329 330 if (numbers) { 331 mvwprintw(wnd, row, col, " %5.2Lf %3.0Lf %5.2Lf ", 332 kb_per_transfer, transfers_per_second, 333 mb_per_second); 334 return(row); 335 } 336 wmove(wnd, row++, col); 337 histogram(mb_per_second, 50, .5); 338 wmove(wnd, row++, col); 339 histogram(transfers_per_second, 50, .5); 340 if (kbpt) { 341 wmove(wnd, row++, col); 342 histogram(kb_per_transfer, 50, .5); 343 } 344 345 return(row); 346 347 } 348 349 static void 350 stat1(row, o) 351 int row, o; 352 { 353 register int i; 354 double time; 355 356 time = 0; 357 for (i = 0; i < CPUSTATES; i++) 358 time += cur.cp_time[i]; 359 if (time == 0.0) 360 time = 1.0; 361 wmove(wnd, row, INSET); 362 #define CPUSCALE 0.5 363 histogram(100.0 * cur.cp_time[o] / time, 50, CPUSCALE); 364 } 365 366 static void 367 histogram(val, colwidth, scale) 368 long double val; 369 int colwidth; 370 double scale; 371 { 372 char buf[10]; 373 register int k; 374 register int v = (int)(val * scale) + 0.5; 375 376 k = MIN(v, colwidth); 377 if (v > colwidth) { 378 snprintf(buf, sizeof(buf), "%5.2Lf", val); 379 k -= strlen(buf); 380 while (k--) 381 waddch(wnd, 'X'); 382 waddstr(wnd, buf); 383 return; 384 } 385 while (k--) 386 waddch(wnd, 'X'); 387 wclrtoeol(wnd); 388 } 389 390 int 391 cmdiostat(cmd, args) 392 char *cmd, *args; 393 { 394 395 if (prefix(cmd, "kbpt")) 396 kbpt = !kbpt; 397 else if (prefix(cmd, "numbers")) 398 numbers = 1; 399 else if (prefix(cmd, "bars")) 400 numbers = 0; 401 else if (!dscmd(cmd, args, 100, &cur)) 402 return (0); 403 wclear(wnd); 404 labeliostat(); 405 refresh(); 406 return (1); 407 } 408