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