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