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 <stdbool.h> 76 #include <stdlib.h> 77 #include <string.h> 78 79 #include "systat.h" 80 #include "extern.h" 81 #include "devs.h" 82 83 static int linesperregion; 84 static double etime; 85 static bool numbers = false; /* default display bar graphs */ 86 static bool kbpt = false; /* default ms/seek shown */ 87 88 static int barlabels(int); 89 static void histogram(long double, int, double); 90 static int numlabels(int); 91 static int devstats(int, int, int); 92 static void stat1(int, int); 93 94 WINDOW * 95 openiostat(void) 96 { 97 return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0)); 98 } 99 100 void 101 closeiostat(WINDOW *w) 102 { 103 if (w == NULL) 104 return; 105 wclear(w); 106 wrefresh(w); 107 delwin(w); 108 } 109 110 int 111 initiostat(void) 112 { 113 /* 114 * This value for maxshowdevs (100) is bogus. I'm not sure exactly 115 * how to calculate it, though. 116 */ 117 if (dsinit(7) != 1) 118 return(0); 119 120 return(1); 121 } 122 123 void 124 fetchiostat(void) 125 { 126 struct devinfo *tmp_dinfo; 127 size_t len; 128 129 len = sizeof(cur_dev.cp_time); 130 if (sysctlbyname("kern.cp_time", &cur_dev.cp_time, &len, NULL, 0) 131 || len != sizeof(cur_dev.cp_time)) { 132 perror("kern.cp_time"); 133 exit (1); 134 } 135 tmp_dinfo = last_dev.dinfo; 136 last_dev.dinfo = cur_dev.dinfo; 137 cur_dev.dinfo = tmp_dinfo; 138 139 last_dev.snap_time = cur_dev.snap_time; 140 141 /* 142 * Here what we want to do is refresh our device stats. 143 * getdevs() returns 1 when the device list has changed. 144 * If the device list has changed, we want to go through 145 * the selection process again, in case a device that we 146 * were previously displaying has gone away. 147 */ 148 switch (devstat_getdevs(NULL, &cur_dev)) { 149 case -1: 150 errx(1, "%s", devstat_errbuf); 151 break; 152 case 1: 153 cmdiostat("refresh", NULL); 154 break; 155 default: 156 break; 157 } 158 num_devices = cur_dev.dinfo->numdevs; 159 generation = cur_dev.dinfo->generation; 160 161 } 162 163 #define INSET 10 164 165 void 166 labeliostat(void) 167 { 168 int row; 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, "interrupt|"); 178 mvwaddstr(wnd, row++, 0, " idle|"); 179 if (numbers) 180 row = numlabels(row + 1); 181 else 182 row = barlabels(row + 1); 183 } 184 185 static int 186 numlabels(int row) 187 { 188 int i, _col, regions, ndrives; 189 char tmpstr[32]; 190 191 #define COLWIDTH 17 192 #define DRIVESPERLINE ((getmaxx(wnd) - 1 - INSET) / COLWIDTH) 193 for (ndrives = 0, i = 0; i < num_devices; i++) 194 if (dev_select[i].selected) 195 ndrives++; 196 regions = howmany(ndrives, DRIVESPERLINE); 197 /* 198 * Deduct -regions for blank line after each scrolling region. 199 */ 200 linesperregion = (getmaxy(wnd) - 1 - row - regions) / regions; 201 /* 202 * Minimum region contains space for two 203 * label lines and one line of statistics. 204 */ 205 if (linesperregion < 3) 206 linesperregion = 3; 207 _col = INSET; 208 for (i = 0; i < num_devices; i++) 209 if (dev_select[i].selected) { 210 if (_col + COLWIDTH >= getmaxx(wnd) - 1 - INSET) { 211 _col = INSET, row += linesperregion + 1; 212 if (row > getmaxy(wnd) - 1 - (linesperregion + 1)) 213 break; 214 } 215 snprintf(tmpstr, sizeof(tmpstr), "%s%d", dev_select[i].device_name, 216 dev_select[i].unit_number); 217 mvwaddstr(wnd, row, _col + 4, tmpstr); 218 mvwaddstr(wnd, row + 1, _col, " KB/t tps MB/s "); 219 _col += COLWIDTH; 220 } 221 if (_col) 222 row += linesperregion + 1; 223 return (row); 224 } 225 226 static int 227 barlabels(int row) 228 { 229 int i; 230 char tmpstr[32]; 231 232 mvwaddstr(wnd, row++, INSET, 233 "/0% /10 /20 /30 /40 /50 /60 /70 /80 /90 /100"); 234 linesperregion = 2 + kbpt; 235 for (i = 0; i < num_devices; i++) 236 if (dev_select[i].selected) { 237 if (row > getmaxy(wnd) - 1 - linesperregion) 238 break; 239 snprintf(tmpstr, sizeof(tmpstr), "%s%d", dev_select[i].device_name, 240 dev_select[i].unit_number); 241 mvwprintw(wnd, row++, 0, "%-5.5s MB/s|", 242 tmpstr); 243 mvwaddstr(wnd, row++, 0, " tps|"); 244 if (kbpt) 245 mvwaddstr(wnd, row++, 0, " KB/t|"); 246 } 247 return (row); 248 } 249 250 void 251 showiostat(void) 252 { 253 long t; 254 int i, row, _col; 255 256 #define X(fld) t = cur_dev.fld[i]; cur_dev.fld[i] -= last_dev.fld[i]; last_dev.fld[i] = t 257 etime = 0; 258 for(i = 0; i < CPUSTATES; i++) { 259 X(cp_time); 260 etime += cur_dev.cp_time[i]; 261 } 262 if (etime == 0.0) 263 etime = 1.0; 264 etime /= hertz; 265 row = 1; 266 for (i = 0; i < CPUSTATES; i++) 267 stat1(row++, i); 268 if (!numbers) { 269 row += 2; 270 for (i = 0; i < num_devices; i++) 271 if (dev_select[i].selected) { 272 if (row > getmaxy(wnd) - linesperregion) 273 break; 274 row = devstats(row, INSET, i); 275 } 276 return; 277 } 278 _col = INSET; 279 wmove(wnd, row + linesperregion, 0); 280 wdeleteln(wnd); 281 wmove(wnd, row + 3, 0); 282 winsertln(wnd); 283 for (i = 0; i < num_devices; i++) 284 if (dev_select[i].selected) { 285 if (_col + COLWIDTH >= getmaxx(wnd) - 1 - INSET) { 286 _col = INSET, row += linesperregion + 1; 287 if (row > getmaxy(wnd) - 1 - (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) devstats(row + 3, _col, i); 295 _col += COLWIDTH; 296 } 297 } 298 299 static int 300 devstats(int row, int _col, int dn) 301 { 302 long double transfers_per_second; 303 long double kb_per_transfer, mb_per_second; 304 long double busy_seconds; 305 int di; 306 307 di = dev_select[dn].position; 308 309 busy_seconds = cur_dev.snap_time - last_dev.snap_time; 310 311 if (devstat_compute_statistics(&cur_dev.dinfo->devices[di], 312 &last_dev.dinfo->devices[di], busy_seconds, 313 DSM_KB_PER_TRANSFER, &kb_per_transfer, 314 DSM_TRANSFERS_PER_SECOND, &transfers_per_second, 315 DSM_MB_PER_SECOND, &mb_per_second, DSM_NONE) != 0) 316 errx(1, "%s", devstat_errbuf); 317 318 if (numbers) { 319 mvwprintw(wnd, row, _col, " %5.2Lf %3.0Lf %5.2Lf ", 320 kb_per_transfer, transfers_per_second, 321 mb_per_second); 322 return(row); 323 } 324 wmove(wnd, row++, _col); 325 histogram(mb_per_second, 50, .5); 326 wmove(wnd, row++, _col); 327 histogram(transfers_per_second, 50, .5); 328 if (kbpt) { 329 wmove(wnd, row++, _col); 330 histogram(kb_per_transfer, 50, .5); 331 } 332 333 return(row); 334 335 } 336 337 static void 338 stat1(int row, int o) 339 { 340 int i; 341 double dtime; 342 343 dtime = 0.0; 344 for (i = 0; i < CPUSTATES; i++) 345 dtime += cur_dev.cp_time[i]; 346 if (dtime == 0.0) 347 dtime = 1.0; 348 wmove(wnd, row, INSET); 349 #define CPUSCALE 0.5 350 histogram(100.0 * cur_dev.cp_time[o] / dtime, 50, CPUSCALE); 351 } 352 353 static void 354 histogram(long double val, int colwidth, double scale) 355 { 356 char buf[10]; 357 int k; 358 int v = (int)(val * scale) + 0.5; 359 360 k = MIN(v, colwidth); 361 if (v > colwidth) { 362 snprintf(buf, sizeof(buf), "%5.2Lf", val); 363 k -= strlen(buf); 364 while (k--) 365 waddch(wnd, 'X'); 366 waddstr(wnd, buf); 367 return; 368 } 369 while (k--) 370 waddch(wnd, 'X'); 371 wclrtoeol(wnd); 372 } 373 374 int 375 cmdiostat(const char *cmd, const char *args) 376 { 377 378 if (prefix(cmd, "kbpt")) 379 kbpt = !kbpt; 380 else if (prefix(cmd, "numbers")) 381 numbers = true; 382 else if (prefix(cmd, "bars")) 383 numbers = false; 384 else if (!dscmd(cmd, args, 100, &cur_dev)) 385 return (0); 386 wclear(wnd); 387 labeliostat(); 388 refresh(); 389 return (1); 390 } 391