1 /* $NetBSD: iostat.c,v 1.2 1995/01/20 08:51:57 jtc 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)iostat.c 8.1 (Berkeley) 6/6/93"; 39 #endif 40 static char rcsid[] = "$NetBSD: iostat.c,v 1.2 1995/01/20 08:51:57 jtc Exp $"; 41 #endif not lint 42 43 #include <sys/param.h> 44 #include <sys/dkstat.h> 45 #include <sys/buf.h> 46 47 #include <string.h> 48 #include <stdlib.h> 49 #include <nlist.h> 50 #include <paths.h> 51 #include "systat.h" 52 #include "extern.h" 53 54 static struct nlist namelist[] = { 55 #define X_DK_BUSY 0 56 { "_dk_busy" }, 57 #define X_DK_TIME 1 58 { "_dk_time" }, 59 #define X_DK_XFER 2 60 { "_dk_xfer" }, 61 #define X_DK_WDS 3 62 { "_dk_wds" }, 63 #define X_DK_SEEK 4 64 { "_dk_seek" }, 65 #define X_CP_TIME 5 66 { "_cp_time" }, 67 #ifdef vax 68 #define X_MBDINIT (X_CP_TIME+1) 69 { "_mbdinit" }, 70 #define X_UBDINIT (X_CP_TIME+2) 71 { "_ubdinit" }, 72 #endif 73 #ifdef tahoe 74 #define X_VBDINIT (X_CP_TIME+1) 75 { "_vbdinit" }, 76 #endif 77 { "" }, 78 }; 79 80 static struct { 81 int dk_busy; 82 long cp_time[CPUSTATES]; 83 long *dk_time; 84 long *dk_wds; 85 long *dk_seek; 86 long *dk_xfer; 87 } s, s1; 88 89 static int linesperregion; 90 static double etime; 91 static int numbers = 0; /* default display bar graphs */ 92 static int msps = 0; /* default ms/seek shown */ 93 94 static int barlabels __P((int)); 95 static void histogram __P((double, int, double)); 96 static int numlabels __P((int)); 97 static int stats __P((int, int, int)); 98 static void stat1 __P((int, int)); 99 100 101 WINDOW * 102 openiostat() 103 { 104 return (subwin(stdscr, LINES-1-5, 0, 5, 0)); 105 } 106 107 void 108 closeiostat(w) 109 WINDOW *w; 110 { 111 if (w == NULL) 112 return; 113 wclear(w); 114 wrefresh(w); 115 delwin(w); 116 } 117 118 int 119 initiostat() 120 { 121 if (namelist[X_DK_BUSY].n_type == 0) { 122 if (kvm_nlist(kd, namelist)) { 123 nlisterr(namelist); 124 return(0); 125 } 126 if (namelist[X_DK_BUSY].n_type == 0) { 127 error("Disk init information isn't in namelist"); 128 return(0); 129 } 130 } 131 if (! dkinit()) 132 return(0); 133 if (dk_ndrive) { 134 #define allocate(e, t) \ 135 s./**/e = (t *)calloc(dk_ndrive, sizeof (t)); \ 136 s1./**/e = (t *)calloc(dk_ndrive, sizeof (t)); 137 allocate(dk_time, long); 138 allocate(dk_wds, long); 139 allocate(dk_seek, long); 140 allocate(dk_xfer, long); 141 #undef allocate 142 } 143 return(1); 144 } 145 146 void 147 fetchiostat() 148 { 149 if (namelist[X_DK_BUSY].n_type == 0) 150 return; 151 NREAD(X_DK_BUSY, &s.dk_busy, LONG); 152 NREAD(X_DK_TIME, s.dk_time, dk_ndrive * LONG); 153 NREAD(X_DK_XFER, s.dk_xfer, dk_ndrive * LONG); 154 NREAD(X_DK_WDS, s.dk_wds, dk_ndrive * LONG); 155 NREAD(X_DK_SEEK, s.dk_seek, dk_ndrive * LONG); 156 NREAD(X_CP_TIME, s.cp_time, sizeof s.cp_time); 157 } 158 159 #define INSET 10 160 161 void 162 labeliostat() 163 { 164 int row; 165 166 if (namelist[X_DK_BUSY].n_type == 0) { 167 error("No dk_busy defined."); 168 return; 169 } 170 row = 0; 171 wmove(wnd, row, 0); wclrtobot(wnd); 172 mvwaddstr(wnd, row++, INSET, 173 "/0 /10 /20 /30 /40 /50 /60 /70 /80 /90 /100"); 174 mvwaddstr(wnd, row++, 0, "cpu user|"); 175 mvwaddstr(wnd, row++, 0, " nice|"); 176 mvwaddstr(wnd, row++, 0, " system|"); 177 mvwaddstr(wnd, row++, 0, " idle|"); 178 if (numbers) 179 row = numlabels(row + 1); 180 else 181 row = barlabels(row + 1); 182 } 183 184 static int 185 numlabels(row) 186 int row; 187 { 188 int i, col, regions, ndrives; 189 190 #define COLWIDTH 14 191 #define DRIVESPERLINE ((wnd->maxx - INSET) / COLWIDTH) 192 for (ndrives = 0, i = 0; i < dk_ndrive; i++) 193 if (dk_select[i]) 194 ndrives++; 195 regions = howmany(ndrives, DRIVESPERLINE); 196 /* 197 * Deduct -regions for blank line after each scrolling region. 198 */ 199 linesperregion = (wnd->maxy - row - regions) / regions; 200 /* 201 * Minimum region contains space for two 202 * label lines and one line of statistics. 203 */ 204 if (linesperregion < 3) 205 linesperregion = 3; 206 col = 0; 207 for (i = 0; i < dk_ndrive; i++) 208 if (dk_select[i] && dk_mspw[i] != 0.0) { 209 if (col + COLWIDTH >= wnd->maxx - INSET) { 210 col = 0, row += linesperregion + 1; 211 if (row > wnd->maxy - (linesperregion + 1)) 212 break; 213 } 214 mvwaddstr(wnd, row, col + 4, dr_name[i]); 215 mvwaddstr(wnd, row + 1, col, "bps tps msps"); 216 col += COLWIDTH; 217 } 218 if (col) 219 row += linesperregion + 1; 220 return (row); 221 } 222 223 static int 224 barlabels(row) 225 int row; 226 { 227 int i; 228 229 mvwaddstr(wnd, row++, INSET, 230 "/0 /5 /10 /15 /20 /25 /30 /35 /40 /45 /50"); 231 linesperregion = 2 + msps; 232 for (i = 0; i < dk_ndrive; i++) 233 if (dk_select[i] && dk_mspw[i] != 0.0) { 234 if (row > wnd->maxy - linesperregion) 235 break; 236 mvwprintw(wnd, row++, 0, "%3.3s bps|", dr_name[i]); 237 mvwaddstr(wnd, row++, 0, " tps|"); 238 if (msps) 239 mvwaddstr(wnd, row++, 0, " msps|"); 240 } 241 return (row); 242 } 243 244 245 void 246 showiostat() 247 { 248 register long t; 249 register int i, row, col; 250 251 if (namelist[X_DK_BUSY].n_type == 0) 252 return; 253 for (i = 0; i < dk_ndrive; i++) { 254 #define X(fld) t = s.fld[i]; s.fld[i] -= s1.fld[i]; s1.fld[i] = t 255 X(dk_xfer); X(dk_seek); X(dk_wds); X(dk_time); 256 } 257 etime = 0; 258 for(i = 0; i < CPUSTATES; i++) { 259 X(cp_time); 260 etime += s.cp_time[i]; 261 } 262 if (etime == 0.0) 263 etime = 1.0; 264 etime /= (float) hz; 265 row = 1; 266 267 /* 268 * Last CPU state not calculated yet. 269 */ 270 for (i = 0; i < CPUSTATES - 1; i++) 271 stat1(row++, i); 272 if (!numbers) { 273 row += 2; 274 for (i = 0; i < dk_ndrive; i++) 275 if (dk_select[i] && dk_mspw[i] != 0.0) { 276 if (row > wnd->maxy - linesperregion) 277 break; 278 row = stats(row, INSET, i); 279 } 280 return; 281 } 282 col = 0; 283 wmove(wnd, row + linesperregion, 0); 284 wdeleteln(wnd); 285 wmove(wnd, row + 3, 0); 286 winsertln(wnd); 287 for (i = 0; i < dk_ndrive; i++) 288 if (dk_select[i] && dk_mspw[i] != 0.0) { 289 if (col + COLWIDTH >= wnd->maxx) { 290 col = 0, row += linesperregion + 1; 291 if (row > wnd->maxy - (linesperregion + 1)) 292 break; 293 wmove(wnd, row + linesperregion, 0); 294 wdeleteln(wnd); 295 wmove(wnd, row + 3, 0); 296 winsertln(wnd); 297 } 298 (void) stats(row + 3, col, i); 299 col += COLWIDTH; 300 } 301 } 302 303 static int 304 stats(row, col, dn) 305 int row, col, dn; 306 { 307 double atime, words, xtime, itime; 308 309 atime = s.dk_time[dn]; 310 atime /= (float) hz; 311 words = s.dk_wds[dn]*32.0; /* number of words transferred */ 312 xtime = dk_mspw[dn]*words; /* transfer time */ 313 itime = atime - xtime; /* time not transferring */ 314 if (xtime < 0) 315 itime += xtime, xtime = 0; 316 if (itime < 0) 317 xtime += itime, itime = 0; 318 if (numbers) { 319 mvwprintw(wnd, row, col, "%3.0f%4.0f%5.1f", 320 words / 512 / etime, s.dk_xfer[dn] / etime, 321 s.dk_seek[dn] ? itime * 1000. / s.dk_seek[dn] : 0.0); 322 return (row); 323 } 324 wmove(wnd, row++, col); 325 histogram(words / 512 / etime, 50, 1.0); 326 wmove(wnd, row++, col); 327 histogram(s.dk_xfer[dn] / etime, 50, 1.0); 328 if (msps) { 329 wmove(wnd, row++, col); 330 histogram(s.dk_seek[dn] ? itime * 1000. / s.dk_seek[dn] : 0, 331 50, 1.0); 332 } 333 return (row); 334 } 335 336 static void 337 stat1(row, o) 338 int row, o; 339 { 340 register int i; 341 double time; 342 343 time = 0; 344 for (i = 0; i < CPUSTATES; i++) 345 time += s.cp_time[i]; 346 if (time == 0.0) 347 time = 1.0; 348 wmove(wnd, row, INSET); 349 #define CPUSCALE 0.5 350 histogram(100.0 * s.cp_time[o] / time, 50, CPUSCALE); 351 } 352 353 static void 354 histogram(val, colwidth, scale) 355 double val; 356 int colwidth; 357 double scale; 358 { 359 char buf[10]; 360 register int k; 361 register int v = (int)(val * scale) + 0.5; 362 363 k = MIN(v, colwidth); 364 if (v > colwidth) { 365 sprintf(buf, "%4.1f", val); 366 k -= strlen(buf); 367 while (k--) 368 waddch(wnd, 'X'); 369 waddstr(wnd, buf); 370 return; 371 } 372 while (k--) 373 waddch(wnd, 'X'); 374 wclrtoeol(wnd); 375 } 376 377 int 378 cmdiostat(cmd, args) 379 char *cmd, *args; 380 { 381 382 if (prefix(cmd, "msps")) 383 msps = !msps; 384 else if (prefix(cmd, "numbers")) 385 numbers = 1; 386 else if (prefix(cmd, "bars")) 387 numbers = 0; 388 else if (!dkcmd(cmd, args)) 389 return (0); 390 wclear(wnd); 391 labeliostat(); 392 refresh(); 393 return (1); 394 } 395