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