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