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 * $DragonFly: src/usr.bin/systat/iostat.c,v 1.4 2004/12/22 11:01:49 joerg Exp $ 30 * 31 * @(#)iostat.c 8.1 (Berkeley) 6/6/93 32 */ 33 /* 34 * Copyright (c) 1980, 1992, 1993 35 * The Regents of the University of California. All rights reserved. 36 * 37 * Redistribution and use in source and binary forms, with or without 38 * modification, are permitted provided that the following conditions 39 * are met: 40 * 1. Redistributions of source code must retain the above copyright 41 * notice, this list of conditions and the following disclaimer. 42 * 2. Redistributions in binary form must reproduce the above copyright 43 * notice, this list of conditions and the following disclaimer in the 44 * documentation and/or other materials provided with the distribution. 45 * 3. All advertising materials mentioning features or use of this software 46 * must display the following acknowledgement: 47 * This product includes software developed by the University of 48 * California, Berkeley and its contributors. 49 * 4. Neither the name of the University nor the names of its contributors 50 * may be used to endorse or promote products derived from this software 51 * without specific prior written permission. 52 * 53 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 54 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 55 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 56 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 57 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 59 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 60 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 63 * SUCH DAMAGE. 64 */ 65 66 #include <sys/param.h> 67 68 #include <err.h> 69 #include <devstat.h> 70 #include <kinfo.h> 71 #include <paths.h> 72 #include <stdlib.h> 73 #include <string.h> 74 #include "systat.h" 75 #include "extern.h" 76 #include "devs.h" 77 78 struct statinfo cur, last; 79 static struct kinfo_cputime cp_time, old_cp_time; 80 static double etime; 81 82 static int linesperregion; 83 static int numbers = 0; /* default display bar graphs */ 84 static int kbpt = 0; /* default ms/seek shown */ 85 86 static int barlabels(int); 87 static void histogram(long double, int, double); 88 static int numlabels(int); 89 static int devstats(int, int, int); 90 static void stat1(int, uint64_t); 91 92 WINDOW * 93 openiostat(void) 94 { 95 return (subwin(stdscr, LINES-1-5, 0, 5, 0)); 96 } 97 98 void 99 closeiostat(WINDOW *w) 100 { 101 if (w == NULL) 102 return; 103 wclear(w); 104 wrefresh(w); 105 delwin(w); 106 } 107 108 int 109 initiostat(void) 110 { 111 if (num_devices = getnumdevs() < 0) 112 return(0); 113 114 cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); 115 last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); 116 bzero(cur.dinfo, sizeof(struct devinfo)); 117 bzero(last.dinfo, sizeof(struct devinfo)); 118 119 /* 120 * This value for maxshowdevs (100) is bogus. I'm not sure exactly 121 * how to calculate it, though. 122 */ 123 if (dsinit(100, &cur, &last, NULL) != 1) 124 return(0); 125 126 return(1); 127 } 128 129 void 130 fetchiostat(void) 131 { 132 struct devinfo *tmp_dinfo; 133 134 if (kinfo_get_sched_cputime(&cp_time)) 135 err(1, "kinfo_get_sched_cputime"); 136 tmp_dinfo = last.dinfo; 137 last.dinfo = cur.dinfo; 138 cur.dinfo = tmp_dinfo; 139 140 last.busy_time = cur.busy_time; 141 142 /* 143 * Here what we want to do is refresh our device stats. 144 * getdevs() returns 1 when the device list has changed. 145 * If the device list has changed, we want to go through 146 * the selection process again, in case a device that we 147 * were previously displaying has gone away. 148 */ 149 switch (getdevs(&cur)) { 150 case -1: 151 errx(1, "%s", devstat_errbuf); 152 break; 153 case 1: 154 cmdiostat("refresh", NULL); 155 break; 156 default: 157 break; 158 } 159 num_devices = cur.dinfo->numdevs; 160 generation = cur.dinfo->generation; 161 162 } 163 164 #define INSET 10 165 166 void 167 labeliostat(void) 168 { 169 int row; 170 171 row = 0; 172 wmove(wnd, row, 0); wclrtobot(wnd); 173 mvwaddstr(wnd, row++, INSET, 174 "/0 /10 /20 /30 /40 /50 /60 /70 /80 /90 /100"); 175 mvwaddstr(wnd, row++, 0, "cpu user|"); 176 mvwaddstr(wnd, row++, 0, " nice|"); 177 mvwaddstr(wnd, row++, 0, " system|"); 178 mvwaddstr(wnd, row++, 0, "interrupt|"); 179 mvwaddstr(wnd, row++, 0, " idle|"); 180 if (numbers) 181 row = numlabels(row + 1); 182 else 183 row = barlabels(row + 1); 184 } 185 186 static int 187 numlabels(int row) 188 { 189 int i, col, regions, ndrives; 190 char tmpstr[10]; 191 192 #define COLWIDTH 17 193 #define DRIVESPERLINE ((wnd->_maxx - INSET) / COLWIDTH) 194 for (ndrives = 0, i = 0; i < num_devices; i++) 195 if (dev_select[i].selected) 196 ndrives++; 197 regions = howmany(ndrives, DRIVESPERLINE); 198 /* 199 * Deduct -regions for blank line after each scrolling region. 200 */ 201 linesperregion = (wnd->_maxy - row - regions) / regions; 202 /* 203 * Minimum region contains space for two 204 * label lines and one line of statistics. 205 */ 206 if (linesperregion < 3) 207 linesperregion = 3; 208 col = INSET; 209 for (i = 0; i < num_devices; i++) 210 if (dev_select[i].selected) { 211 if (col + COLWIDTH >= wnd->_maxx - INSET) { 212 col = INSET, row += linesperregion + 1; 213 if (row > wnd->_maxy - (linesperregion + 1)) 214 break; 215 } 216 sprintf(tmpstr, "%s%d", dev_select[i].device_name, 217 dev_select[i].unit_number); 218 mvwaddstr(wnd, row, col + 4, tmpstr); 219 mvwaddstr(wnd, row + 1, col, " KB/t tps MB/s "); 220 col += COLWIDTH; 221 } 222 if (col) 223 row += linesperregion + 1; 224 return (row); 225 } 226 227 static int 228 barlabels(int row) 229 { 230 int i; 231 char tmpstr[10]; 232 233 mvwaddstr(wnd, row++, INSET, 234 "/0 /10 /20 /30 /40 /50 /60 /70 /80 /90 /100"); 235 linesperregion = 2 + kbpt; 236 for (i = 0; i < num_devices; i++) 237 if (dev_select[i].selected) { 238 if (row > wnd->_maxy - linesperregion) 239 break; 240 sprintf(tmpstr, "%s%d", dev_select[i].device_name, 241 dev_select[i].unit_number); 242 mvwprintw(wnd, row++, 0, "%-5.5s MB/s|", 243 tmpstr); 244 mvwaddstr(wnd, row++, 0, " tps|"); 245 if (kbpt) 246 mvwaddstr(wnd, row++, 0, " KB/t|"); 247 } 248 return (row); 249 } 250 251 252 void 253 showiostat(void) 254 { 255 register long t; 256 register int i, row, col; 257 struct kinfo_cputime diff_cp_time; 258 259 diff_cp_time.cp_user = cp_time.cp_user - old_cp_time.cp_user; 260 diff_cp_time.cp_nice = cp_time.cp_nice - old_cp_time.cp_nice; 261 diff_cp_time.cp_sys = cp_time.cp_sys - old_cp_time.cp_sys; 262 diff_cp_time.cp_intr = cp_time.cp_intr - old_cp_time.cp_intr; 263 diff_cp_time.cp_idle = cp_time.cp_idle - old_cp_time.cp_idle; 264 old_cp_time = cp_time; 265 266 row = 1; 267 stat1(row++, diff_cp_time.cp_user); 268 stat1(row++, diff_cp_time.cp_nice); 269 stat1(row++, diff_cp_time.cp_sys); 270 stat1(row++, diff_cp_time.cp_intr); 271 stat1(row++, diff_cp_time.cp_idle); 272 if (!numbers) { 273 row += 2; 274 for (i = 0; i < num_devices; i++) 275 if (dev_select[i].selected) { 276 if (row > wnd->_maxy - linesperregion) 277 break; 278 row = devstats(row, INSET, i); 279 } 280 return; 281 } 282 col = INSET; 283 wmove(wnd, row + linesperregion, 0); 284 wdeleteln(wnd); 285 wmove(wnd, row + 3, 0); 286 winsertln(wnd); 287 for (i = 0; i < num_devices; i++) 288 if (dev_select[i].selected) { 289 if (col + COLWIDTH >= wnd->_maxx - INSET) { 290 col = INSET, row += linesperregion + 1; 291 if (row > wnd->_maxy - (linesperregion + 1)) 292 break; 293 wmove(wnd, row + linesperregion, 0); 294 wdeleteln(wnd); 295 wmove(wnd, row + 3, 0); 296 winsertln(wnd); 297 } 298 (void) devstats(row + 3, col, i); 299 col += COLWIDTH; 300 } 301 } 302 303 static int 304 devstats(int row, int col, int dn) 305 { 306 long double transfers_per_second; 307 long double kb_per_transfer, mb_per_second; 308 long double busy_seconds; 309 int di; 310 311 di = dev_select[dn].position; 312 313 busy_seconds = compute_etime(cur.busy_time, last.busy_time); 314 315 if (compute_stats(&cur.dinfo->devices[di], &last.dinfo->devices[di], 316 busy_seconds, NULL, NULL, NULL, 317 &kb_per_transfer, &transfers_per_second, 318 &mb_per_second, NULL, NULL) != 0) 319 errx(1, "%s", devstat_errbuf); 320 321 if (numbers) { 322 mvwprintw(wnd, row, col, " %5.2Lf %3.0Lf %5.2Lf ", 323 kb_per_transfer, transfers_per_second, 324 mb_per_second); 325 return(row); 326 } 327 wmove(wnd, row++, col); 328 histogram(mb_per_second, 50, .5); 329 wmove(wnd, row++, col); 330 histogram(transfers_per_second, 50, .5); 331 if (kbpt) { 332 wmove(wnd, row++, col); 333 histogram(kb_per_transfer, 50, .5); 334 } 335 336 return(row); 337 338 } 339 340 static void 341 stat1(int row, uint64_t difference) 342 { 343 register int i; 344 double time; 345 346 wmove(wnd, row, INSET); 347 #define CPUSCALE 0.5 348 histogram(100.0 * difference / etime, 50, CPUSCALE); 349 } 350 351 static void 352 histogram(long double val, int colwidth, double scale) 353 { 354 char buf[10]; 355 register int k; 356 register int v = (int)(val * scale) + 0.5; 357 358 k = MIN(v, colwidth); 359 if (v > colwidth) { 360 snprintf(buf, sizeof(buf), "%5.2Lf", val); 361 k -= strlen(buf); 362 while (k--) 363 waddch(wnd, 'X'); 364 waddstr(wnd, buf); 365 return; 366 } 367 while (k--) 368 waddch(wnd, 'X'); 369 wclrtoeol(wnd); 370 } 371 372 int 373 cmdiostat(char *cmd, char *args) 374 { 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