1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 /* 33 * sar generates a report either from an input data file or by invoking sadc to 34 * read system activity counters at the specified intervals. 35 * 36 * usage: sar [-ubdycwaqvmpgrkA] [-o file] t [n] 37 * sar [-ubdycwaqvmpgrkA][-s hh:mm][-e hh:mm][-i ss][-f file] 38 */ 39 40 #include <sys/param.h> 41 #include <sys/stat.h> 42 #include <sys/sysinfo.h> 43 #include <sys/time.h> 44 #include <sys/types.h> 45 #include <sys/utsname.h> 46 #include <sys/wait.h> 47 48 #include <ctype.h> 49 #include <errno.h> 50 #include <fcntl.h> 51 #include <limits.h> 52 #include <signal.h> 53 #include <stdarg.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <time.h> 58 #include <unistd.h> 59 60 #include "sa.h" 61 62 #define PGTOBLK(x) ((x) * (pagesize >> 9)) 63 #define BLKTOPG(x) ((x) / (pagesize >> 9)) 64 #define BLKS(x) ((x) >> 9) 65 66 static void prpass(int); 67 static void prtopt(void); 68 static void prtavg(void); 69 static void prttim(void); 70 static void prtmachid(void); 71 static void prthdg(void); 72 static void tsttab(void); 73 static void update_counters(void); 74 static void usage(void); 75 static void fail(int, char *, ...); 76 static void safe_zalloc(void **, int, int); 77 static int safe_read(int, void *, size_t); 78 static void safe_write(int, void *, size_t); 79 static int safe_strtoi(char const *, char *); 80 static void ulong_delta(ulong_t *, ulong_t *, ulong_t *, ulong_t *, 81 int, int); 82 static float denom(float); 83 static float freq(float, float); 84 85 static struct sa nx, ox, ax, dx; 86 static iodevinfo_t *nxio, *oxio, *axio, *dxio; 87 static struct tm *curt, args, arge; 88 89 static int sflg, eflg, iflg, oflg, fflg; 90 static int realtime, passno = 0, do_disk; 91 static int t = 0, n = 0, lines = 0; 92 static int hz; 93 static int niodevs; 94 static int tabflg; 95 static char options[30], fopt[30]; 96 static float tdiff, sec_diff, totsec_diff = 0.0, percent; 97 static time_t ts, te; /* time interval start and end */ 98 static float start_time, end_time, isec; 99 static int fin, fout; 100 static pid_t childid; 101 static int pipedes[2]; 102 static char arg1[10], arg2[10]; 103 static int pagesize; 104 105 /* 106 * To avoid overflow in the kmem allocation data, declare a copy of the 107 * main kmeminfo_t type with larger data types. Use this for storing 108 * the data held to display average values 109 */ 110 static struct kmeminfo_l 111 { 112 u_longlong_t km_mem[KMEM_NCLASS]; 113 u_longlong_t km_alloc[KMEM_NCLASS]; 114 u_longlong_t km_fail[KMEM_NCLASS]; 115 } kmi; 116 117 int 118 main(int argc, char **argv) 119 { 120 char flnm[PATH_MAX], ofile[PATH_MAX]; 121 char ccc; 122 time_t temp; 123 int i, jj = 0; 124 125 pagesize = sysconf(_SC_PAGESIZE); 126 127 /* 128 * Process options with arguments and pack options 129 * without arguments. 130 */ 131 while ((i = getopt(argc, argv, "ubdycwaqvmpgrkAo:s:e:i:f:")) != EOF) 132 switch (ccc = (char)i) { 133 case 'o': 134 oflg++; 135 if (strlcpy(ofile, optarg, sizeof (ofile)) >= 136 sizeof (ofile)) { 137 fail(2, "-o filename is too long: %s", optarg); 138 } 139 break; 140 case 's': 141 if (sscanf(optarg, "%d:%d:%d", 142 &args.tm_hour, &args.tm_min, &args.tm_sec) < 1) 143 fail(0, "-%c %s -- illegal option argument", 144 ccc, optarg); 145 else { 146 sflg++, 147 start_time = args.tm_hour*3600.0 + 148 args.tm_min*60.0 + 149 args.tm_sec; 150 } 151 break; 152 case 'e': 153 if (sscanf(optarg, "%d:%d:%d", 154 &arge.tm_hour, &arge.tm_min, &arge.tm_sec) < 1) 155 fail(0, "-%c %s -- illegal option argument", 156 ccc, optarg); 157 else { 158 eflg++; 159 end_time = arge.tm_hour*3600.0 + 160 arge.tm_min*60.0 + 161 arge.tm_sec; 162 } 163 break; 164 case 'i': 165 if (sscanf(optarg, "%f", &isec) < 1) 166 fail(0, "-%c %s -- illegal option argument", 167 ccc, optarg); 168 else { 169 if (isec > 0.0) 170 iflg++; 171 } 172 break; 173 case 'f': 174 fflg++; 175 if (strlcpy(flnm, optarg, sizeof (flnm)) >= 176 sizeof (ofile)) { 177 fail(2, "-f filename is too long: %s", optarg); 178 } 179 break; 180 case '?': 181 usage(); 182 exit(1); 183 break; 184 default: 185 186 /* 187 * Check for repeated options. To make sure 188 * that options[30] does not overflow. 189 */ 190 if (strchr(options, ccc) == NULL) 191 (void) strncat(options, &ccc, 1); 192 break; 193 } 194 195 /* 196 * Are starting and ending times consistent? 197 */ 198 if ((sflg) && (eflg) && (end_time <= start_time)) 199 fail(0, "ending time <= starting time"); 200 201 /* 202 * Determine if t and n arguments are given, and whether to run in real 203 * time or from a file. 204 */ 205 switch (argc - optind) { 206 case 0: /* Get input data from file */ 207 if (fflg == 0) { 208 temp = time(NULL); 209 curt = localtime(&temp); 210 (void) snprintf(flnm, PATH_MAX, "/var/adm/sa/sa%.2d", 211 curt->tm_mday); 212 } 213 if ((fin = open(flnm, 0)) == -1) 214 fail(1, "can't open %s", flnm); 215 break; 216 case 1: /* Real time data; one cycle */ 217 realtime++; 218 t = safe_strtoi(argv[optind], "invalid sampling interval"); 219 n = 2; 220 break; 221 case 2: /* Real time data; specified cycles */ 222 default: 223 realtime++; 224 t = safe_strtoi(argv[optind], "invalid sampling interval"); 225 n = 1 + safe_strtoi(argv[optind+1], "invalid sample count"); 226 break; 227 } 228 229 /* 230 * "u" is the default option, which displays CPU utilization. 231 */ 232 if (strlen(options) == 0) 233 (void) strcpy(options, "u"); 234 235 /* 236 * "A" means all data options. 237 */ 238 if (strchr(options, 'A') != NULL) 239 (void) strcpy(options, "udqbwcayvmpgrk"); 240 241 if (realtime) { 242 /* 243 * Get input data from sadc via pipe. 244 */ 245 if (t <= 0) 246 fail(0, "sampling interval t <= 0 sec"); 247 if (n < 2) 248 fail(0, "number of sample intervals n <= 0"); 249 (void) sprintf(arg1, "%d", t); 250 (void) sprintf(arg2, "%d", n); 251 if (pipe(pipedes) == -1) 252 fail(1, "pipe failed"); 253 if ((childid = fork()) == 0) { 254 /* 255 * Child: shift pipedes[write] to stdout, 256 * and close the pipe entries. 257 */ 258 (void) dup2(pipedes[1], 1); 259 if (pipedes[0] != 1) 260 (void) close(pipedes[0]); 261 if (pipedes[1] != 1) 262 (void) close(pipedes[1]); 263 264 if (execlp("/usr/lib/sa/sadc", 265 "/usr/lib/sa/sadc", arg1, arg2, 0) == -1) 266 fail(1, "exec of /usr/lib/sa/sadc failed"); 267 } else if (childid == -1) { 268 fail(1, "Could not fork to exec sadc"); 269 } 270 /* 271 * Parent: close unused output. 272 */ 273 fin = pipedes[0]; 274 (void) close(pipedes[1]); 275 } 276 277 if (oflg) { 278 if (strcmp(ofile, flnm) == 0) 279 fail(0, "output file name same as input file name"); 280 fout = creat(ofile, 00644); 281 } 282 283 hz = sysconf(_SC_CLK_TCK); 284 285 nxio = oxio = dxio = axio = NULL; 286 287 if (realtime) { 288 /* 289 * Make single pass, processing all options. 290 */ 291 (void) strcpy(fopt, options); 292 passno++; 293 prpass(realtime); 294 (void) kill(childid, SIGINT); 295 (void) wait(NULL); 296 } else { 297 /* 298 * Make multiple passes, one for each option. 299 */ 300 while (strlen(strncpy(fopt, &options[jj++], 1))) { 301 if (lseek(fin, 0, SEEK_SET) == (off_t)-1) 302 fail(0, "lseek failed"); 303 passno++; 304 prpass(realtime); 305 } 306 } 307 308 return (0); 309 } 310 311 /* 312 * Read records from input, classify, and decide on printing. 313 */ 314 static void 315 prpass(int input_pipe) 316 { 317 size_t size; 318 int i, j, state_change, recno = 0; 319 kid_t kid; 320 float trec, tnext = 0; 321 ulong_t old_niodevs = 0, prev_niodevs = 0; 322 iodevinfo_t *aio, *dio, *oio; 323 struct stat in_stat; 324 325 do_disk = (strchr(fopt, 'd') != NULL); 326 if (!input_pipe && fstat(fin, &in_stat) == -1) 327 fail(1, "unable to stat data file"); 328 329 if (sflg) 330 tnext = start_time; 331 332 while (safe_read(fin, &nx, sizeof (struct sa))) { 333 /* 334 * sadc is the only utility used to generate sar data 335 * and it uses the valid field as follows: 336 * 0 - dummy record 337 * 1 - data record 338 * We can use this fact to improve sar's ability to detect 339 * bad data, since any value apart from 0 or 1 can be 340 * interpreted as invalid data. 341 */ 342 if (nx.valid != 0 && nx.valid != 1) 343 fail(2, "data file not in sar format"); 344 state_change = 0; 345 niodevs = nx.niodevs; 346 /* 347 * niodevs has the value of current number of devices 348 * from nx structure. 349 * 350 * The following 'if' condition is to decide whether memory 351 * has to be allocated or not if already allocated memory is 352 * bigger or smaller than memory needed to store the current 353 * niodevs details in memory. 354 * 355 * when first while loop starts, pre_niodevs has 0 and then 356 * always get initialized to the current number of devices 357 * from nx.niodevs if it is different from previously read 358 * niodevs. 359 * 360 * if the current niodevs has the same value of previously 361 * allocated memory i.e, for prev_niodevs, it skips the 362 * following 'if' loop or otherwise it allocates memory for 363 * current devises (niodevs) and stores that value in 364 * prev_niodevs for next time when loop continues to read 365 * from the file. 366 */ 367 if (niodevs != prev_niodevs) { 368 off_t curr_pos; 369 /* 370 * The required buffer size must fit in a size_t. 371 */ 372 if (SIZE_MAX / sizeof (iodevinfo_t) < niodevs) 373 fail(2, "insufficient address space to hold " 374 "%lu device records", niodevs); 375 size = niodevs * sizeof (iodevinfo_t); 376 prev_niodevs = niodevs; 377 /* 378 * The data file must exceed this size to be valid. 379 */ 380 if (!input_pipe) { 381 if ((curr_pos = lseek(fin, 0, SEEK_CUR)) == 382 (off_t)-1) 383 fail(1, "lseek failed"); 384 if (in_stat.st_size < curr_pos || 385 size > in_stat.st_size - curr_pos) 386 fail(2, "data file corrupt; specified size" 387 "exceeds actual"); 388 } 389 390 safe_zalloc((void **)&nxio, size, 1); 391 } 392 if (niodevs != old_niodevs) 393 state_change = 1; 394 for (i = 0; i < niodevs; i++) { 395 if (safe_read(fin, &nxio[i], sizeof (iodevinfo_t)) == 0) 396 fail(1, "premature end-of-file seen"); 397 if (i < old_niodevs && 398 nxio[i].ks.ks_kid != oxio[i].ks.ks_kid) 399 state_change = 1; 400 } 401 curt = localtime(&nx.ts); 402 trec = curt->tm_hour * 3600.0 + 403 curt->tm_min * 60.0 + 404 curt->tm_sec; 405 if ((recno == 0) && (trec < start_time)) 406 continue; 407 if ((eflg) && (trec > end_time)) 408 break; 409 if ((oflg) && (passno == 1)) { 410 safe_write(fout, &nx, sizeof (struct sa)); 411 for (i = 0; i < niodevs; i++) 412 safe_write(fout, &nxio[i], 413 sizeof (iodevinfo_t)); 414 } 415 416 if (recno == 0) { 417 if (passno == 1) 418 prtmachid(); 419 420 prthdg(); 421 recno = 1; 422 if ((iflg) && (tnext == 0)) 423 tnext = trec; 424 } 425 426 if (nx.valid == 0) { 427 /* 428 * This dummy record signifies system restart 429 * New initial values of counters follow in next 430 * record. 431 */ 432 if (!realtime) { 433 prttim(); 434 (void) printf("\tunix restarts\n"); 435 recno = 1; 436 continue; 437 } 438 } 439 if ((iflg) && (trec < tnext)) 440 continue; 441 442 if (state_change) { 443 /* 444 * Either the number of devices or the ordering of 445 * the kstats has changed. We need to re-organise 446 * the layout of our avg/delta arrays so that we 447 * can cope with this in update_counters(). 448 */ 449 size = niodevs * sizeof (iodevinfo_t); 450 safe_zalloc((void *)&aio, size, 0); 451 safe_zalloc((void *)&dio, size, 0); 452 safe_zalloc((void *)&oio, size, 0); 453 454 /* 455 * Loop through all the newly read iodev's, locate 456 * the corresponding entry in the old arrays and 457 * copy the entries into the same bucket of the 458 * new arrays. 459 */ 460 for (i = 0; i < niodevs; i++) { 461 kid = nxio[i].ks.ks_kid; 462 for (j = 0; j < old_niodevs; j++) { 463 if (oxio[j].ks.ks_kid == kid) { 464 oio[i] = oxio[j]; 465 aio[i] = axio[j]; 466 dio[i] = dxio[j]; 467 } 468 } 469 } 470 471 free(axio); 472 free(oxio); 473 free(dxio); 474 475 axio = aio; 476 oxio = oio; 477 dxio = dio; 478 479 old_niodevs = niodevs; 480 } 481 482 if (recno++ > 1) { 483 ts = ox.csi.cpu[0] + ox.csi.cpu[1] + 484 ox.csi.cpu[2] + ox.csi.cpu[3]; 485 te = nx.csi.cpu[0] + nx.csi.cpu[1] + 486 nx.csi.cpu[2] + nx.csi.cpu[3]; 487 tdiff = (float)(te - ts); 488 sec_diff = tdiff / hz; 489 percent = 100.0 / tdiff; 490 491 /* 492 * If the CPU stat counters have rolled 493 * backward, this is our best indication that 494 * a CPU has been offlined. We don't have 495 * enough data to compute a sensible delta, so 496 * toss out this interval, but compute the next 497 * interval's delta from these values. 498 */ 499 if (tdiff <= 0) { 500 ox = nx; 501 continue; 502 } 503 update_counters(); 504 prtopt(); 505 lines++; 506 if (passno == 1) 507 totsec_diff += sec_diff; 508 } 509 ox = nx; /* Age the data */ 510 (void) memcpy(oxio, nxio, niodevs * sizeof (iodevinfo_t)); 511 if (isec > 0) 512 while (tnext <= trec) 513 tnext += isec; 514 } 515 /* 516 * After this place, all functions are using niodevs to access the 517 * memory for device details. Here, old_niodevs has the correct value 518 * of memory allocated for storing device information. Since niodevs 519 * doesn't have correct value, sometimes, it was corrupting memory. 520 */ 521 niodevs = old_niodevs; 522 if (lines > 1) 523 prtavg(); 524 (void) memset(&ax, 0, sizeof (ax)); /* Zero out the accumulators. */ 525 (void) memset(&kmi, 0, sizeof (kmi)); 526 lines = 0; 527 /* 528 * axio will not be allocated if the user specified -e or -s, and 529 * no records in the file fell inside the specified time range. 530 */ 531 if (axio) { 532 (void) memset(axio, 0, niodevs * sizeof (iodevinfo_t)); 533 } 534 } 535 536 /* 537 * Print time label routine. 538 */ 539 static void 540 prttim(void) 541 { 542 curt = localtime(&nx.ts); 543 (void) printf("%.2d:%.2d:%.2d", curt->tm_hour, curt->tm_min, 544 curt->tm_sec); 545 tabflg = 1; 546 } 547 548 /* 549 * Test if 8-spaces to be added routine. 550 */ 551 static void 552 tsttab(void) 553 { 554 if (tabflg == 0) 555 (void) printf(" "); 556 else 557 tabflg = 0; 558 } 559 560 /* 561 * Print machine identification. 562 */ 563 static void 564 prtmachid(void) 565 { 566 struct utsname name; 567 568 (void) uname(&name); 569 (void) printf("\n%s %s %s %s %s %.2d/%.2d/%.4d\n", 570 name.sysname, name.nodename, name.release, name.version, 571 name.machine, curt->tm_mon + 1, curt->tm_mday, 572 curt->tm_year + 1900); 573 } 574 575 /* 576 * Print report heading routine. 577 */ 578 static void 579 prthdg(void) 580 { 581 int jj = 0; 582 char ccc; 583 584 (void) printf("\n"); 585 prttim(); 586 while ((ccc = fopt[jj++]) != NULL) { 587 tsttab(); 588 switch (ccc) { 589 case 'u': 590 (void) printf(" %7s %7s %7s %7s\n", 591 "%usr", 592 "%sys", 593 "%wio", 594 "%idle"); 595 break; 596 case 'b': 597 (void) printf(" %7s %7s %7s %7s %7s %7s %7s %7s\n", 598 "bread/s", 599 "lread/s", 600 "%rcache", 601 "bwrit/s", 602 "lwrit/s", 603 "%wcache", 604 "pread/s", 605 "pwrit/s"); 606 break; 607 case 'd': 608 (void) printf(" %-8.8s %7s %7s %7s %7s %7s %7s\n", 609 "device", 610 "%busy", 611 "avque", 612 "r+w/s", 613 "blks/s", 614 "avwait", 615 "avserv"); 616 break; 617 case 'y': 618 (void) printf(" %7s %7s %7s %7s %7s %7s\n", 619 "rawch/s", 620 "canch/s", 621 "outch/s", 622 "rcvin/s", 623 "xmtin/s", 624 "mdmin/s"); 625 break; 626 case 'c': 627 (void) printf(" %7s %7s %7s %7s %7s %7s %7s\n", 628 "scall/s", 629 "sread/s", 630 "swrit/s", 631 "fork/s", 632 "exec/s", 633 "rchar/s", 634 "wchar/s"); 635 break; 636 case 'w': 637 (void) printf(" %7s %7s %7s %7s %7s\n", 638 "swpin/s", 639 "bswin/s", 640 "swpot/s", 641 "bswot/s", 642 "pswch/s"); 643 break; 644 case 'a': 645 (void) printf(" %7s %7s %7s\n", 646 "iget/s", 647 "namei/s", 648 "dirbk/s"); 649 break; 650 case 'q': 651 (void) printf(" %7s %7s %7s %7s\n", 652 "runq-sz", 653 "%runocc", 654 "swpq-sz", 655 "%swpocc"); 656 break; 657 case 'v': 658 (void) printf(" %s %s %s %s\n", 659 "proc-sz ov", 660 "inod-sz ov", 661 "file-sz ov", 662 "lock-sz"); 663 break; 664 case 'm': 665 (void) printf(" %7s %7s\n", 666 "msg/s", 667 "sema/s"); 668 break; 669 case 'p': 670 (void) printf(" %7s %7s %7s %7s %7s %7s\n", 671 "atch/s", 672 "pgin/s", 673 "ppgin/s", 674 "pflt/s", 675 "vflt/s", 676 "slock/s"); 677 break; 678 case 'g': 679 (void) printf(" %8s %8s %8s %8s %8s\n", 680 "pgout/s", 681 "ppgout/s", 682 "pgfree/s", 683 "pgscan/s", 684 "%ufs_ipf"); 685 break; 686 case 'r': 687 (void) printf(" %7s %8s\n", 688 "freemem", 689 "freeswap"); 690 break; 691 case 'k': 692 (void) printf(" %7s %7s %5s %7s %7s %5s %11s %5s\n", 693 "sml_mem", 694 "alloc", 695 "fail", 696 "lg_mem", 697 "alloc", 698 "fail", 699 "ovsz_alloc", 700 "fail"); 701 break; 702 } 703 } 704 if (jj > 2 || do_disk) 705 (void) printf("\n"); 706 } 707 708 /* 709 * compute deltas and update accumulators 710 */ 711 static void 712 update_counters(void) 713 { 714 int i; 715 iodevinfo_t *nio, *oio, *aio, *dio; 716 717 ulong_delta((ulong_t *)&nx.csi, (ulong_t *)&ox.csi, 718 (ulong_t *)&dx.csi, (ulong_t *)&ax.csi, 0, sizeof (ax.csi)); 719 ulong_delta((ulong_t *)&nx.si, (ulong_t *)&ox.si, 720 (ulong_t *)&dx.si, (ulong_t *)&ax.si, 0, sizeof (ax.si)); 721 ulong_delta((ulong_t *)&nx.cvmi, (ulong_t *)&ox.cvmi, 722 (ulong_t *)&dx.cvmi, (ulong_t *)&ax.cvmi, 0, 723 sizeof (ax.cvmi)); 724 725 ax.vmi.freemem += dx.vmi.freemem = nx.vmi.freemem - ox.vmi.freemem; 726 ax.vmi.swap_avail += dx.vmi.swap_avail = 727 nx.vmi.swap_avail - ox.vmi.swap_avail; 728 729 nio = nxio; 730 oio = oxio; 731 aio = axio; 732 dio = dxio; 733 for (i = 0; i < niodevs; i++) { 734 aio->kios.wlastupdate += dio->kios.wlastupdate 735 = nio->kios.wlastupdate - oio->kios.wlastupdate; 736 aio->kios.reads += dio->kios.reads 737 = nio->kios.reads - oio->kios.reads; 738 aio->kios.writes += dio->kios.writes 739 = nio->kios.writes - oio->kios.writes; 740 aio->kios.nread += dio->kios.nread 741 = nio->kios.nread - oio->kios.nread; 742 aio->kios.nwritten += dio->kios.nwritten 743 = nio->kios.nwritten - oio->kios.nwritten; 744 aio->kios.wlentime += dio->kios.wlentime 745 = nio->kios.wlentime - oio->kios.wlentime; 746 aio->kios.rlentime += dio->kios.rlentime 747 = nio->kios.rlentime - oio->kios.rlentime; 748 aio->kios.wtime += dio->kios.wtime 749 = nio->kios.wtime - oio->kios.wtime; 750 aio->kios.rtime += dio->kios.rtime 751 = nio->kios.rtime - oio->kios.rtime; 752 nio++; 753 oio++; 754 aio++; 755 dio++; 756 } 757 } 758 759 static void 760 prt_u_opt(struct sa *xx) 761 { 762 (void) printf(" %7.0f %7.0f %7.0f %7.0f\n", 763 (float)xx->csi.cpu[1] * percent, 764 (float)xx->csi.cpu[2] * percent, 765 (float)xx->csi.cpu[3] * percent, 766 (float)xx->csi.cpu[0] * percent); 767 } 768 769 static void 770 prt_b_opt(struct sa *xx) 771 { 772 (void) printf(" %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f\n", 773 (float)xx->csi.bread / sec_diff, 774 (float)xx->csi.lread / sec_diff, 775 freq((float)xx->csi.lread, (float)xx->csi.bread), 776 (float)xx->csi.bwrite / sec_diff, 777 (float)xx->csi.lwrite / sec_diff, 778 freq((float)xx->csi.lwrite, (float)xx->csi.bwrite), 779 (float)xx->csi.phread / sec_diff, 780 (float)xx->csi.phwrite / sec_diff); 781 } 782 783 static void 784 prt_d_opt(int ii, iodevinfo_t *xio) 785 { 786 double etime, hr_etime, tps, avq, avs; 787 788 tsttab(); 789 790 hr_etime = (double)xio[ii].kios.wlastupdate; 791 if (hr_etime == 0.0) 792 hr_etime = (double)NANOSEC; 793 etime = hr_etime / (double)NANOSEC; 794 tps = (double)(xio[ii].kios.reads + xio[ii].kios.writes) / etime; 795 avq = (double)xio[ii].kios.wlentime / hr_etime; 796 avs = (double)xio[ii].kios.rlentime / hr_etime; 797 798 (void) printf(" %-8.8s ", nxio[ii].ks.ks_name); 799 (void) printf("%7.0f %7.1f %7.0f %7.0f %7.1f %7.1f\n", 800 (double)xio[ii].kios.rtime * 100.0 / hr_etime, 801 avq + avs, 802 tps, 803 BLKS(xio[ii].kios.nread + xio[ii].kios.nwritten) / etime, 804 (tps > 0 ? avq / tps * 1000.0 : 0.0), 805 (tps > 0 ? avs / tps * 1000.0 : 0.0)); 806 } 807 808 static void 809 prt_y_opt(struct sa *xx) 810 { 811 (void) printf(" %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f\n", 812 (float)xx->csi.rawch / sec_diff, 813 (float)xx->csi.canch / sec_diff, 814 (float)xx->csi.outch / sec_diff, 815 (float)xx->csi.rcvint / sec_diff, 816 (float)xx->csi.xmtint / sec_diff, 817 (float)xx->csi.mdmint / sec_diff); 818 } 819 820 static void 821 prt_c_opt(struct sa *xx) 822 { 823 (void) printf(" %7.0f %7.0f %7.0f %7.2f %7.2f %7.0f %7.0f\n", 824 (float)xx->csi.syscall / sec_diff, 825 (float)xx->csi.sysread / sec_diff, 826 (float)xx->csi.syswrite / sec_diff, 827 (float)(xx->csi.sysfork + xx->csi.sysvfork) / sec_diff, 828 (float)xx->csi.sysexec / sec_diff, 829 (float)xx->csi.readch / sec_diff, 830 (float)xx->csi.writech / sec_diff); 831 } 832 833 static void 834 prt_w_opt(struct sa *xx) 835 { 836 (void) printf(" %7.2f %7.1f %7.2f %7.1f %7.0f\n", 837 (float)xx->cvmi.swapin / sec_diff, 838 (float)PGTOBLK(xx->cvmi.pgswapin) / sec_diff, 839 (float)xx->cvmi.swapout / sec_diff, 840 (float)PGTOBLK(xx->cvmi.pgswapout) / sec_diff, 841 (float)xx->csi.pswitch / sec_diff); 842 } 843 844 static void 845 prt_a_opt(struct sa *xx) 846 { 847 (void) printf(" %7.0f %7.0f %7.0f\n", 848 (float)xx->csi.ufsiget / sec_diff, 849 (float)xx->csi.namei / sec_diff, 850 (float)xx->csi.ufsdirblk / sec_diff); 851 } 852 853 static void 854 prt_q_opt(struct sa *xx) 855 { 856 if (xx->si.runocc == 0 || xx->si.updates == 0) 857 (void) printf(" %7.1f %7.0f", 0., 0.); 858 else { 859 (void) printf(" %7.1f %7.0f", 860 (float)xx->si.runque / (float)xx->si.runocc, 861 (float)xx->si.runocc / (float)xx->si.updates * 100.0); 862 } 863 if (xx->si.swpocc == 0 || xx->si.updates == 0) 864 (void) printf(" %7.1f %7.0f\n", 0., 0.); 865 else { 866 (void) printf(" %7.1f %7.0f\n", 867 (float)xx->si.swpque / (float)xx->si.swpocc, 868 (float)xx->si.swpocc / (float)xx->si.updates * 100.0); 869 } 870 } 871 872 static void 873 prt_v_opt(struct sa *xx) 874 { 875 (void) printf(" %4lu/%-4lu %4u %4lu/%-4lu %4u %4lu/%-4lu " 876 "%4u %4lu/%-4lu\n", 877 nx.szproc, nx.mszproc, xx->csi.procovf, 878 nx.szinode, nx.mszinode, xx->csi.inodeovf, 879 nx.szfile, nx.mszfile, xx->csi.fileovf, 880 nx.szlckr, nx.mszlckr); 881 } 882 883 static void 884 prt_m_opt(struct sa *xx) 885 { 886 (void) printf(" %7.2f %7.2f\n", 887 (float)xx->csi.msg / sec_diff, 888 (float)xx->csi.sema / sec_diff); 889 } 890 891 static void 892 prt_p_opt(struct sa *xx) 893 { 894 (void) printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n", 895 (float)xx->cvmi.pgfrec / sec_diff, 896 (float)xx->cvmi.pgin / sec_diff, 897 (float)xx->cvmi.pgpgin / sec_diff, 898 (float)(xx->cvmi.prot_fault + xx->cvmi.cow_fault) / sec_diff, 899 (float)(xx->cvmi.hat_fault + xx->cvmi.as_fault) / sec_diff, 900 (float)xx->cvmi.softlock / sec_diff); 901 } 902 903 static void 904 prt_g_opt(struct sa *xx) 905 { 906 (void) printf(" %8.2f %8.2f %8.2f %8.2f %8.2f\n", 907 (float)xx->cvmi.pgout / sec_diff, 908 (float)xx->cvmi.pgpgout / sec_diff, 909 (float)xx->cvmi.dfree / sec_diff, 910 (float)xx->cvmi.scan / sec_diff, 911 (float)xx->csi.ufsipage * 100.0 / 912 denom((float)xx->csi.ufsipage + 913 (float)xx->csi.ufsinopage)); 914 } 915 916 static void 917 prt_r_opt(struct sa *xx) 918 { 919 /* Avoid divide by Zero - Should never happen */ 920 if (xx->si.updates == 0) 921 (void) printf(" %7.0f %8.0f\n", 0., 0.); 922 else { 923 (void) printf(" %7.0f %8.0f\n", 924 (double)xx->vmi.freemem / (float)xx->si.updates, 925 (double)PGTOBLK(xx->vmi.swap_avail) / 926 (float)xx->si.updates); 927 } 928 } 929 930 static void 931 prt_k_opt(struct sa *xx, int n) 932 { 933 if (n != 1) { 934 (void) printf(" %7.0f %7.0f %5.0f %7.0f %7.0f %5.0f %11.0f" 935 " %5.0f\n", 936 (float)kmi.km_mem[KMEM_SMALL] / n, 937 (float)kmi.km_alloc[KMEM_SMALL] / n, 938 (float)kmi.km_fail[KMEM_SMALL] / n, 939 (float)kmi.km_mem[KMEM_LARGE] / n, 940 (float)kmi.km_alloc[KMEM_LARGE] / n, 941 (float)kmi.km_fail[KMEM_LARGE] / n, 942 (float)kmi.km_alloc[KMEM_OSIZE] / n, 943 (float)kmi.km_fail[KMEM_OSIZE] / n); 944 } else { 945 /* 946 * If we are not reporting averages, use the read values 947 * directly. 948 */ 949 (void) printf(" %7.0f %7.0f %5.0f %7.0f %7.0f %5.0f %11.0f" 950 " %5.0f\n", 951 (float)xx->kmi.km_mem[KMEM_SMALL], 952 (float)xx->kmi.km_alloc[KMEM_SMALL], 953 (float)xx->kmi.km_fail[KMEM_SMALL], 954 (float)xx->kmi.km_mem[KMEM_LARGE], 955 (float)xx->kmi.km_alloc[KMEM_LARGE], 956 (float)xx->kmi.km_fail[KMEM_LARGE], 957 (float)xx->kmi.km_alloc[KMEM_OSIZE], 958 (float)xx->kmi.km_fail[KMEM_OSIZE]); 959 } 960 } 961 962 /* 963 * Print options routine. 964 */ 965 static void 966 prtopt(void) 967 { 968 int ii, jj = 0; 969 char ccc; 970 971 prttim(); 972 973 while ((ccc = fopt[jj++]) != NULL) { 974 if (ccc != 'd') 975 tsttab(); 976 switch (ccc) { 977 case 'u': 978 prt_u_opt(&dx); 979 break; 980 case 'b': 981 prt_b_opt(&dx); 982 break; 983 case 'd': 984 for (ii = 0; ii < niodevs; ii++) 985 prt_d_opt(ii, dxio); 986 break; 987 case 'y': 988 prt_y_opt(&dx); 989 break; 990 case 'c': 991 prt_c_opt(&dx); 992 break; 993 case 'w': 994 prt_w_opt(&dx); 995 break; 996 case 'a': 997 prt_a_opt(&dx); 998 break; 999 case 'q': 1000 prt_q_opt(&dx); 1001 break; 1002 case 'v': 1003 prt_v_opt(&dx); 1004 break; 1005 case 'm': 1006 prt_m_opt(&dx); 1007 break; 1008 case 'p': 1009 prt_p_opt(&dx); 1010 break; 1011 case 'g': 1012 prt_g_opt(&dx); 1013 break; 1014 case 'r': 1015 prt_r_opt(&dx); 1016 break; 1017 case 'k': 1018 prt_k_opt(&nx, 1); 1019 /* 1020 * To avoid overflow, copy the data from the sa record 1021 * into a struct kmeminfo_l which has members with 1022 * larger data types. 1023 */ 1024 kmi.km_mem[KMEM_SMALL] += nx.kmi.km_mem[KMEM_SMALL]; 1025 kmi.km_alloc[KMEM_SMALL] += nx.kmi.km_alloc[KMEM_SMALL]; 1026 kmi.km_fail[KMEM_SMALL] += nx.kmi.km_fail[KMEM_SMALL]; 1027 kmi.km_mem[KMEM_LARGE] += nx.kmi.km_mem[KMEM_LARGE]; 1028 kmi.km_alloc[KMEM_LARGE] += nx.kmi.km_alloc[KMEM_LARGE]; 1029 kmi.km_fail[KMEM_LARGE] += nx.kmi.km_fail[KMEM_LARGE]; 1030 kmi.km_alloc[KMEM_OSIZE] += nx.kmi.km_alloc[KMEM_OSIZE]; 1031 kmi.km_fail[KMEM_OSIZE] += nx.kmi.km_fail[KMEM_OSIZE]; 1032 break; 1033 } 1034 } 1035 if (jj > 2 || do_disk) 1036 (void) printf("\n"); 1037 if (realtime) 1038 (void) fflush(stdout); 1039 } 1040 1041 /* 1042 * Print average routine. 1043 */ 1044 static void 1045 prtavg(void) 1046 { 1047 int ii, jj = 0; 1048 char ccc; 1049 1050 tdiff = ax.csi.cpu[0] + ax.csi.cpu[1] + ax.csi.cpu[2] + ax.csi.cpu[3]; 1051 if (tdiff <= 0.0) 1052 return; 1053 1054 sec_diff = tdiff / hz; 1055 percent = 100.0 / tdiff; 1056 (void) printf("\n"); 1057 1058 while ((ccc = fopt[jj++]) != NULL) { 1059 if (ccc != 'v') 1060 (void) printf("Average "); 1061 switch (ccc) { 1062 case 'u': 1063 prt_u_opt(&ax); 1064 break; 1065 case 'b': 1066 prt_b_opt(&ax); 1067 break; 1068 case 'd': 1069 tabflg = 1; 1070 for (ii = 0; ii < niodevs; ii++) 1071 prt_d_opt(ii, axio); 1072 break; 1073 case 'y': 1074 prt_y_opt(&ax); 1075 break; 1076 case 'c': 1077 prt_c_opt(&ax); 1078 break; 1079 case 'w': 1080 prt_w_opt(&ax); 1081 break; 1082 case 'a': 1083 prt_a_opt(&ax); 1084 break; 1085 case 'q': 1086 prt_q_opt(&ax); 1087 break; 1088 case 'v': 1089 break; 1090 case 'm': 1091 prt_m_opt(&ax); 1092 break; 1093 case 'p': 1094 prt_p_opt(&ax); 1095 break; 1096 case 'g': 1097 prt_g_opt(&ax); 1098 break; 1099 case 'r': 1100 prt_r_opt(&ax); 1101 break; 1102 case 'k': 1103 prt_k_opt(&ax, lines); 1104 break; 1105 } 1106 } 1107 } 1108 1109 static void 1110 ulong_delta(ulong_t *new, ulong_t *old, ulong_t *delta, ulong_t *accum, 1111 int begin, int end) 1112 { 1113 int i; 1114 ulong_t *np, *op, *dp, *ap; 1115 1116 np = new; 1117 op = old; 1118 dp = delta; 1119 ap = accum; 1120 for (i = begin; i < end; i += sizeof (ulong_t)) 1121 *ap++ += *dp++ = *np++ - *op++; 1122 } 1123 1124 /* 1125 * used to prevent zero denominators 1126 */ 1127 static float 1128 denom(float x) 1129 { 1130 return ((x > 0.5) ? x : 1.0); 1131 } 1132 1133 /* 1134 * a little calculation that comes up often when computing frequency 1135 * of one operation relative to another 1136 */ 1137 static float 1138 freq(float x, float y) 1139 { 1140 return ((x < 0.5) ? 100.0 : (x - y) / x * 100.0); 1141 } 1142 1143 static void 1144 usage(void) 1145 { 1146 (void) fprintf(stderr, 1147 "usage: sar [-ubdycwaqvmpgrkA][-o file] t [n]\n" 1148 "\tsar [-ubdycwaqvmpgrkA] [-s hh:mm][-e hh:mm][-i ss][-f file]\n"); 1149 } 1150 1151 static void 1152 fail(int do_perror, char *message, ...) 1153 { 1154 va_list args; 1155 1156 va_start(args, message); 1157 (void) fprintf(stderr, "sar: "); 1158 (void) vfprintf(stderr, message, args); 1159 va_end(args); 1160 (void) fprintf(stderr, "\n"); 1161 switch (do_perror) { 1162 case 0: /* usage message */ 1163 usage(); 1164 break; 1165 case 1: /* perror output */ 1166 perror(""); 1167 break; 1168 case 2: /* no further output */ 1169 break; 1170 default: /* error */ 1171 (void) fprintf(stderr, "unsupported failure mode\n"); 1172 break; 1173 } 1174 exit(2); 1175 } 1176 1177 static int 1178 safe_strtoi(char const *val, char *errmsg) 1179 { 1180 char *end; 1181 long tmp; 1182 1183 errno = 0; 1184 tmp = strtol(val, &end, 10); 1185 if (*end != '\0' || errno) 1186 fail(0, "%s %s", errmsg, val); 1187 return ((int)tmp); 1188 } 1189 1190 static void 1191 safe_zalloc(void **ptr, int size, int free_first) 1192 { 1193 if (free_first && *ptr != NULL) 1194 free(*ptr); 1195 if ((*ptr = malloc(size)) == NULL) 1196 fail(1, "malloc failed"); 1197 (void) memset(*ptr, 0, size); 1198 } 1199 1200 static int 1201 safe_read(int fd, void *buf, size_t size) 1202 { 1203 size_t rsize = read(fd, buf, size); 1204 1205 if (rsize == 0) 1206 return (0); 1207 1208 if (rsize != size) 1209 fail(1, "read failed"); 1210 1211 return (1); 1212 } 1213 1214 static void 1215 safe_write(int fd, void *buf, size_t size) 1216 { 1217 if (write(fd, buf, size) != size) 1218 fail(1, "write failed"); 1219 } 1220