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