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