1 /* 2 * Copyright (c) 1998 Kenneth D. Merry. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD: src/usr.bin/systat/iostat.c,v 1.9.2.1 2000/07/02 10:03:17 ps Exp $ 29 * 30 * @(#)iostat.c 8.1 (Berkeley) 6/6/93 31 */ 32 /* 33 * Copyright (c) 1980, 1992, 1993 34 * The Regents of the University of California. All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 3. Neither the name of the University nor the names of its contributors 45 * may be used to endorse or promote products derived from this software 46 * without specific prior written permission. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 58 * SUCH DAMAGE. 59 */ 60 61 #include <sys/user.h> 62 63 #include <err.h> 64 #include <devstat.h> 65 #include <kinfo.h> 66 #include <paths.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include "systat.h" 70 #include "extern.h" 71 #include "devs.h" 72 73 struct statinfo cur, last; 74 static struct kinfo_cputime cp_time, old_cp_time; 75 76 static int linesperregion; 77 static int numbers = 0; /* default display bar graphs */ 78 static int kbpt = 0; /* default ms/seek shown */ 79 80 static int barlabels(int); 81 static void histogram(long double, int, double); 82 static int numlabels(int); 83 static int devstats(int, int, int); 84 static void stat1(int, uint64_t, uint64_t); 85 86 WINDOW * 87 openiostat(void) 88 { 89 return (subwin(stdscr, LINES-1-5, 0, 5, 0)); 90 } 91 92 void 93 closeiostat(WINDOW *w) 94 { 95 if (w == NULL) 96 return; 97 wclear(w); 98 wrefresh(w); 99 delwin(w); 100 } 101 102 int 103 initiostat(void) 104 { 105 if ((num_devices = getnumdevs()) < 0) 106 return(0); 107 108 cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); 109 last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); 110 bzero(cur.dinfo, sizeof(struct devinfo)); 111 bzero(last.dinfo, sizeof(struct devinfo)); 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(100, &cur, &last, NULL) != 1) 118 return(0); 119 120 return(1); 121 } 122 123 void 124 fetchiostat(void) 125 { 126 struct devinfo *tmp_dinfo; 127 128 if (kinfo_get_sched_cputime(&cp_time)) 129 err(1, "kinfo_get_sched_cputime"); 130 tmp_dinfo = last.dinfo; 131 last.dinfo = cur.dinfo; 132 cur.dinfo = tmp_dinfo; 133 134 last.busy_time = cur.busy_time; 135 136 /* 137 * Here what we want to do is refresh our device stats. 138 * getdevs() returns 1 when the device list has changed. 139 * If the device list has changed, we want to go through 140 * the selection process again, in case a device that we 141 * were previously displaying has gone away. 142 */ 143 switch (getdevs(&cur)) { 144 case -1: 145 errx(1, "%s", devstat_errbuf); 146 break; 147 case 1: 148 cmdiostat("refresh", NULL); 149 break; 150 default: 151 break; 152 } 153 num_devices = cur.dinfo->numdevs; 154 generation = cur.dinfo->generation; 155 156 } 157 158 #define INSET 10 159 160 void 161 labeliostat(void) 162 { 163 int row; 164 165 row = 0; 166 wmove(wnd, row, 0); wclrtobot(wnd); 167 mvwaddstr(wnd, row++, INSET, 168 "/0 /10 /20 /30 /40 /50 /60 /70 /80 /90 /100"); 169 mvwaddstr(wnd, row++, 0, "cpu user|"); 170 mvwaddstr(wnd, row++, 0, " nice|"); 171 mvwaddstr(wnd, row++, 0, " system|"); 172 mvwaddstr(wnd, row++, 0, "interrupt|"); 173 mvwaddstr(wnd, row++, 0, " idle|"); 174 if (numbers) 175 row = numlabels(row + 1); 176 else 177 row = barlabels(row + 1); 178 } 179 180 static int 181 numlabels(int row) 182 { 183 int i, _col, regions, ndrives; 184 char tmpstr[10]; 185 186 #define COLWIDTH 17 187 #define DRIVESPERLINE ((wnd->_maxx - INSET) / COLWIDTH) 188 for (ndrives = 0, i = 0; i < num_devices; i++) 189 if (dev_select[i].selected) 190 ndrives++; 191 regions = howmany(ndrives, DRIVESPERLINE); 192 /* 193 * Deduct -regions for blank line after each scrolling region. 194 */ 195 linesperregion = (wnd->_maxy - row - regions) / regions; 196 /* 197 * Minimum region contains space for two 198 * label lines and one line of statistics. 199 */ 200 if (linesperregion < 3) 201 linesperregion = 3; 202 _col = INSET; 203 for (i = 0; i < num_devices; i++) 204 if (dev_select[i].selected) { 205 if (_col + COLWIDTH >= wnd->_maxx - INSET) { 206 _col = INSET, row += linesperregion + 1; 207 if (row > wnd->_maxy - (linesperregion + 1)) 208 break; 209 } 210 sprintf(tmpstr, "%.6s%d", dev_select[i].device_name, 211 dev_select[i].unit_number); 212 mvwaddstr(wnd, row, _col + 4, tmpstr); 213 mvwaddstr(wnd, row + 1, _col, " KB/t tps MB/s "); 214 _col += COLWIDTH; 215 } 216 if (_col) 217 row += linesperregion + 1; 218 return (row); 219 } 220 221 static int 222 barlabels(int row) 223 { 224 int i; 225 char tmpstr[10]; 226 227 mvwaddstr(wnd, row++, INSET, 228 "/0 /10 /20 /30 /40 /50 /60 /70 /80 /90 /100"); 229 linesperregion = 2 + kbpt; 230 for (i = 0; i < num_devices; i++) 231 if (dev_select[i].selected) { 232 if (row > wnd->_maxy - linesperregion) 233 break; 234 sprintf(tmpstr, "%.4s%d", dev_select[i].device_name, 235 dev_select[i].unit_number); 236 mvwprintw(wnd, row++, 0, "%-5.5s MB/s|", 237 tmpstr); 238 mvwaddstr(wnd, row++, 0, " tps|"); 239 if (kbpt) 240 mvwaddstr(wnd, row++, 0, " KB/t|"); 241 } 242 return (row); 243 } 244 245 246 void 247 showiostat(void) 248 { 249 int i, row, _col; 250 struct kinfo_cputime diff_cp_time; 251 uint64_t cp_total; 252 253 diff_cp_time.cp_user = cp_time.cp_user - old_cp_time.cp_user; 254 diff_cp_time.cp_nice = cp_time.cp_nice - old_cp_time.cp_nice; 255 diff_cp_time.cp_sys = cp_time.cp_sys - old_cp_time.cp_sys; 256 diff_cp_time.cp_intr = cp_time.cp_intr - old_cp_time.cp_intr; 257 diff_cp_time.cp_idle = cp_time.cp_idle - old_cp_time.cp_idle; 258 old_cp_time = cp_time; 259 260 row = 1; 261 cp_total = diff_cp_time.cp_user + diff_cp_time.cp_nice + 262 diff_cp_time.cp_sys + diff_cp_time.cp_intr + diff_cp_time.cp_idle; 263 stat1(row++, diff_cp_time.cp_user, cp_total); 264 stat1(row++, diff_cp_time.cp_nice, cp_total); 265 stat1(row++, diff_cp_time.cp_sys, cp_total); 266 stat1(row++, diff_cp_time.cp_intr, cp_total); 267 stat1(row++, diff_cp_time.cp_idle, cp_total); 268 if (!numbers) { 269 row += 2; 270 for (i = 0; i < num_devices; i++) 271 if (dev_select[i].selected) { 272 if (row > wnd->_maxy - 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 >= wnd->_maxx - INSET) { 286 _col = INSET, row += linesperregion + 1; 287 if (row > wnd->_maxy - (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 = compute_etime(cur.busy_time, last.busy_time); 310 311 if (compute_stats(&cur.dinfo->devices[di], &last.dinfo->devices[di], 312 busy_seconds, NULL, NULL, NULL, 313 &kb_per_transfer, &transfers_per_second, 314 &mb_per_second, NULL, NULL) != 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, uint64_t difference, uint64_t total) 338 { 339 double dtime; 340 341 if (total > 0) 342 dtime = 100.0 * difference / total; 343 else 344 dtime = 0; 345 wmove(wnd, row, INSET); 346 #define CPUSCALE 0.5 347 histogram(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 if (val <= 0) 358 v = 0; 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, char *args) 375 { 376 if (prefix(cmd, "kbpt")) 377 kbpt = !kbpt; 378 else if (prefix(cmd, "numbers")) 379 numbers = 1; 380 else if (prefix(cmd, "bars")) 381 numbers = 0; 382 else if (!dscmd(cmd, args, 100, &cur)) 383 return (0); 384 wclear(wnd); 385 labeliostat(); 386 refresh(); 387 return (1); 388 } 389