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