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