1 /* $NetBSD: iostat.c,v 1.33 2005/08/07 12:32:38 blymn Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1992, 1993 5 * The Regents of the University of California. All rights reserved. 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 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)iostat.c 8.1 (Berkeley) 6/6/93"; 36 #endif 37 __RCSID("$NetBSD: iostat.c,v 1.33 2005/08/07 12:32:38 blymn Exp $"); 38 #endif /* not lint */ 39 40 #include <sys/param.h> 41 42 #include <string.h> 43 44 #include "systat.h" 45 #include "extern.h" 46 #include "dkstats.h" 47 #include "tpstats.h" 48 49 static int linesperregion; 50 static double etime; 51 static int numbers = 0; /* default display bar graphs */ 52 static int secs = 0; /* default seconds shown */ 53 static int read_write = 0; /* default read/write shown */ 54 55 static int barlabels(int); 56 static void histogram(double, int, double); 57 static int numlabels(int); 58 static int stats(int, int, int); 59 static int tpstats(int, int, int); 60 static void stat1(int, int); 61 62 63 WINDOW * 64 openiostat(void) 65 { 66 67 return (subwin(stdscr, -1, 0, 5, 0)); 68 } 69 70 void 71 closeiostat(WINDOW *w) 72 { 73 74 if (w == NULL) 75 return; 76 wclear(w); 77 wrefresh(w); 78 delwin(w); 79 } 80 81 int 82 initiostat(void) 83 { 84 85 dkinit(1); 86 tpinit(1); 87 dkreadstats(); 88 tpreadstats(); 89 return(1); 90 } 91 92 void 93 fetchiostat(void) 94 { 95 96 if (dk_ndrive == 0) 97 return; 98 else 99 dkreadstats(); 100 101 if (tp_ndrive == 0) 102 return; 103 else 104 tpreadstats(); 105 } 106 107 #define INSET 14 108 109 void 110 labeliostat(void) 111 { 112 int row; 113 114 if ((dk_ndrive == 0) && (tp_ndrive == 0)) { 115 error("No drives defined."); 116 return; 117 } 118 row = 0; 119 wmove(wnd, row, 0); wclrtobot(wnd); 120 mvwaddstr(wnd, row++, INSET, 121 "/0 /10 /20 /30 /40 /50 /60 /70 /80 /90 /100"); 122 mvwaddstr(wnd, row++, 0, " CPU user|"); 123 mvwaddstr(wnd, row++, 0, " nice|"); 124 mvwaddstr(wnd, row++, 0, " system|"); 125 mvwaddstr(wnd, row++, 0, " interrupt|"); 126 mvwaddstr(wnd, row++, 0, " idle|"); 127 if (numbers) 128 row = numlabels(row + 1); 129 else 130 row = barlabels(row + 1); 131 } 132 133 static int 134 numlabels(int row) 135 { 136 int i, col, regions, ndrives; 137 138 #define COLWIDTH (9 + secs * 5 + 1 + read_write * 9 + 1) 139 #define DRIVESPERLINE ((getmaxx(wnd) + 1) / COLWIDTH) 140 for (ndrives = 0, i = 0; i < dk_ndrive; i++) 141 if (cur.dk_select[i]) 142 ndrives++; 143 for (i = 0; i < tp_ndrive; i++) 144 if (cur_tape.select[i]) 145 ndrives++; 146 147 regions = howmany(ndrives, DRIVESPERLINE); 148 /* 149 * Deduct -regions for blank line after each scrolling region. 150 */ 151 linesperregion = (getmaxy(wnd) - row - regions + 1) / regions; 152 /* 153 * Minimum region contains space for two 154 * label lines and one line of statistics. 155 */ 156 if (linesperregion < 3) 157 linesperregion = 3; 158 col = 0; 159 for (i = 0; i < (dk_ndrive + tp_ndrive); i++) 160 if (((i < dk_ndrive) && (cur.dk_select[i])) || 161 ((i >= dk_ndrive) && (cur_tape.select[i - dk_ndrive]))) { 162 if (col + COLWIDTH - 1 > getmaxx(wnd)) { 163 col = 0, row += linesperregion + 1; 164 if (row > getmaxy(wnd) - (linesperregion)) 165 break; 166 } 167 168 if (i < dk_ndrive) 169 mvwprintw(wnd, row, col + 5, "%s", 170 cur.dk_name[i]); 171 else 172 mvwprintw(wnd, row, col + 5, "%s", 173 cur_tape.name[i - dk_ndrive]); 174 175 if (read_write) 176 mvwprintw(wnd, row, col + 11 + secs * 5, 177 "(write)"); 178 mvwprintw(wnd, row + 1, col, " kBps %s", 179 read_write ? "r/s" : "tps"); 180 if (secs) 181 waddstr(wnd, " sec"); 182 if (read_write) 183 waddstr(wnd, " kBps w/s"); 184 col += COLWIDTH; 185 } 186 if (col) 187 row += linesperregion + 1; 188 return (row); 189 } 190 191 static int 192 barlabels(int row) 193 { 194 int i; 195 196 mvwaddstr(wnd, row++, INSET, 197 "/0 /10 /20 /30 /40 /50 /60 /70 /80 /90 /100"); 198 linesperregion = 2 + secs + (read_write ? 2 : 0); 199 for (i = 0; i < dk_ndrive; i++) 200 if (cur.dk_select[i]) { 201 if (row > getmaxy(wnd) - linesperregion) 202 break; 203 mvwprintw(wnd, row++, 0, "%7.7s kBps|", 204 cur.dk_name[i]); 205 mvwaddstr(wnd, row++, 0, " tps|"); 206 if (read_write) { 207 mvwprintw(wnd, row++, 0, " (write) kBps|"); 208 mvwaddstr(wnd, row++, 0, " tps|"); 209 } 210 if (secs) 211 mvwaddstr(wnd, row++, 0, " msec|"); 212 } 213 for (i = 0; i < tp_ndrive; i++) 214 if (cur_tape.select[i]) { 215 if (row > getmaxy(wnd) - linesperregion) 216 break; 217 mvwprintw(wnd, row++, 0, "%7.7s kBps|", 218 cur_tape.name[i]); 219 mvwaddstr(wnd, row++, 0, " tps|"); 220 if (read_write) { 221 mvwprintw(wnd, row++, 0, " (write) kBps|"); 222 mvwaddstr(wnd, row++, 0, " tps|"); 223 } 224 if (secs) 225 mvwaddstr(wnd, row++, 0, " msec|"); 226 } 227 return (row); 228 } 229 230 void 231 showiostat(void) 232 { 233 int i, row, col; 234 235 if (dk_ndrive == 0) 236 return; 237 dkswap(); 238 tpswap(); 239 240 etime = cur.cp_etime; 241 row = 1; 242 243 /* 244 * Interrupt CPU state not calculated yet. 245 */ 246 for (i = 0; i < CPUSTATES; i++) 247 stat1(row++, i); 248 if (!numbers) { 249 row += 2; 250 for (i = 0; i < dk_ndrive; i++) 251 if (cur.dk_select[i]) { 252 if (row > getmaxy(wnd) - linesperregion) 253 break; 254 row = stats(row, INSET, i); 255 } 256 for (i = 0; i < tp_ndrive; i++) 257 if (cur_tape.select[i]) { 258 if (row > getmaxy(wnd) - linesperregion) 259 break; 260 row = tpstats(row, INSET, i); 261 } 262 return; 263 } 264 col = 0; 265 wmove(wnd, row + linesperregion, 0); 266 wdeleteln(wnd); 267 wmove(wnd, row + 3, 0); 268 winsertln(wnd); 269 for (i = 0; i < dk_ndrive; i++) 270 if (cur.dk_select[i]) { 271 if (col + COLWIDTH - 1 > getmaxx(wnd)) { 272 col = 0, row += linesperregion + 1; 273 if (row > getmaxy(wnd) - (linesperregion + 1)) 274 break; 275 wmove(wnd, row + linesperregion, 0); 276 wdeleteln(wnd); 277 wmove(wnd, row + 3, 0); 278 winsertln(wnd); 279 } 280 (void) stats(row + 3, col, i); 281 col += COLWIDTH; 282 } 283 for (i = 0; i < tp_ndrive; i++) 284 if (cur_tape.select[i]) { 285 if (col + COLWIDTH - 1 > getmaxx(wnd)) { 286 col = 0, row += linesperregion + 1; 287 if (row > getmaxy(wnd) - (linesperregion + 1)) 288 break; 289 wmove(wnd, row + linesperregion, 0); 290 wdeleteln(wnd); 291 wmove(wnd, row + 3, 0); 292 winsertln(wnd); 293 } 294 (void) stats(row + 3, col, i); 295 col += COLWIDTH; 296 } 297 } 298 299 static int 300 stats(int row, int col, int dn) 301 { 302 double atime, rwords, wwords; 303 uint64_t rxfer; 304 305 /* time busy in disk activity */ 306 atime = (double)cur.dk_time[dn].tv_sec + 307 ((double)cur.dk_time[dn].tv_usec / (double)1000000); 308 309 /* # of k transferred */ 310 rwords = cur.dk_rbytes[dn] / 1024.0; 311 wwords = cur.dk_wbytes[dn] / 1024.0; 312 rxfer = cur.dk_rxfer[dn]; 313 if (!read_write) { 314 rwords = wwords; 315 rxfer += cur.dk_wxfer[dn]; 316 } 317 if (numbers) { 318 mvwprintw(wnd, row, col, "%5.0f%4.0f", 319 rwords / etime, rxfer / etime); 320 if (secs) 321 wprintw(wnd, "%5.1f", atime / etime); 322 if (read_write) 323 wprintw(wnd, " %5.0f%4.0f", 324 wwords / etime, cur.dk_wxfer[dn] / etime); 325 return (row); 326 } 327 328 wmove(wnd, row++, col); 329 histogram(rwords / etime, 50, 0.5); 330 wmove(wnd, row++, col); 331 histogram(rxfer / etime, 50, 0.5); 332 if (read_write) { 333 wmove(wnd, row++, col); 334 histogram(wwords / etime, 50, 0.5); 335 wmove(wnd, row++, col); 336 histogram(cur.dk_wxfer[dn] / etime, 50, 0.5); 337 } 338 339 if (secs) { 340 wmove(wnd, row++, col); 341 atime *= 1000; /* In milliseconds */ 342 histogram(atime / etime, 50, 0.5); 343 } 344 return (row); 345 } 346 347 static int 348 tpstats(int row, int col, int dn) 349 { 350 double atime, rwords, wwords; 351 uint64_t rxfer; 352 353 /* time busy in disk activity */ 354 atime = (double)cur_tape.time[dn].tv_sec + 355 ((double)cur_tape.time[dn].tv_usec / (double)1000000); 356 357 /* # of k transferred */ 358 rwords = cur_tape.rbytes[dn] / 1024.0; 359 wwords = cur_tape.wbytes[dn] / 1024.0; 360 rxfer = cur_tape.rxfer[dn]; 361 if (!read_write) { 362 rwords = wwords; 363 rxfer += cur_tape.wxfer[dn]; 364 } 365 if (numbers) { 366 mvwprintw(wnd, row, col, "%5.0f%4.0f", 367 rwords / etime, rxfer / etime); 368 if (secs) 369 wprintw(wnd, "%5.1f", atime / etime); 370 if (read_write) 371 wprintw(wnd, " %5.0f%4.0f", 372 wwords / etime, cur_tape.wxfer[dn] / etime); 373 return (row); 374 } 375 376 wmove(wnd, row++, col); 377 histogram(rwords / etime, 50, 0.5); 378 wmove(wnd, row++, col); 379 histogram(rxfer / etime, 50, 0.5); 380 if (read_write) { 381 wmove(wnd, row++, col); 382 histogram(wwords / etime, 50, 0.5); 383 wmove(wnd, row++, col); 384 histogram(cur_tape.wxfer[dn] / etime, 50, 0.5); 385 } 386 387 if (secs) { 388 wmove(wnd, row++, col); 389 atime *= 1000; /* In milliseconds */ 390 histogram(atime / etime, 50, 0.5); 391 } 392 return (row); 393 } 394 395 static void 396 stat1(int row, int o) 397 { 398 int i; 399 double total_time; 400 401 total_time = 0; 402 for (i = 0; i < CPUSTATES; i++) 403 total_time += cur.cp_time[i]; 404 if (total_time == 0.0) 405 total_time = 1.0; 406 wmove(wnd, row, INSET); 407 #define CPUSCALE 0.5 408 histogram(100.0 * cur.cp_time[o] / total_time, 50, CPUSCALE); 409 } 410 411 static void 412 histogram(double val, int colwidth, double scale) 413 { 414 int v = (int)(val * scale + 0.5); 415 int factor = 1; 416 int y, x; 417 418 while (v > colwidth) { 419 v = (v + 5) / 10; 420 factor *= 10; 421 } 422 getyx(wnd, y, x); 423 wclrtoeol(wnd); 424 whline(wnd, 'X', v); 425 if (factor != 1) 426 mvwprintw(wnd, y, x + colwidth + 1, "* %d ", factor); 427 } 428 429 void 430 iostat_bars(char *args) 431 { 432 numbers = 0; 433 wclear(wnd); 434 labeliostat(); 435 refresh(); 436 } 437 438 void 439 iostat_numbers(char *args) 440 { 441 numbers = 1; 442 wclear(wnd); 443 labeliostat(); 444 refresh(); 445 } 446 447 void 448 iostat_secs(char *args) 449 { 450 secs = !secs; 451 wclear(wnd); 452 labeliostat(); 453 refresh(); 454 } 455 456 void 457 iostat_rw(char *args) 458 { 459 read_write ^= 1; 460 wclear(wnd); 461 labeliostat(); 462 refresh(); 463 } 464 465 void 466 iostat_all(char *args) 467 { 468 read_write = 0; 469 wclear(wnd); 470 labeliostat(); 471 refresh(); 472 } 473