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 2008 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 * Convert array of 64-bit uints to 32-bit uints 322 */ 323 static void 324 convert_64to32(uint_t *dst, uint64_t *src, int size) 325 { 326 for (; size > 0; size--) 327 *dst++ = (uint32_t)(*src++); 328 } 329 330 /* 331 * Read records from input, classify, and decide on printing. 332 */ 333 static void 334 prpass(int input_pipe) 335 { 336 size_t size; 337 int i, j, state_change, recno = 0; 338 kid_t kid; 339 float trec, tnext = 0; 340 ulong_t old_niodevs = 0, prev_niodevs = 0; 341 iodevinfo_t *aio, *dio, *oio; 342 struct stat in_stat; 343 struct sa tx; 344 uint64_t ts, te; /* time interval start and end */ 345 346 do_disk = (strchr(fopt, 'd') != NULL); 347 if (!input_pipe && fstat(fin, &in_stat) == -1) 348 fail(1, "unable to stat data file"); 349 350 if (sflg) 351 tnext = start_time; 352 353 while (safe_read(fin, &tx, sizeof (struct sa))) { 354 /* 355 * First, we convert 32-bit tx to 64-bit nx structure 356 * which is used later. Conversion could be done 357 * after initial operations, right before calculations, 358 * but it would introduce additional juggling with vars. 359 * Thus, we convert all data now, and don't care about 360 * tx any further. 361 */ 362 nx.valid = tx.valid; 363 nx.ts = tx.ts; 364 convert_32to64((uint64_t *)&nx.csi, (uint_t *)&tx.csi, 365 sizeof (tx.csi) / sizeof (uint_t)); 366 convert_32to64((uint64_t *)&nx.cvmi, (uint_t *)&tx.cvmi, 367 sizeof (tx.cvmi) / sizeof (uint_t)); 368 convert_32to64((uint64_t *)&nx.si, (uint_t *)&tx.si, 369 sizeof (tx.si) / sizeof (uint_t)); 370 (void) memcpy(&nx.vmi, &tx.vmi, 371 sizeof (tx) - (((char *)&tx.vmi) - ((char *)&tx))); 372 /* 373 * sadc is the only utility used to generate sar data 374 * and it uses the valid field as follows: 375 * 0 - dummy record 376 * 1 - data record 377 * We can use this fact to improve sar's ability to detect 378 * bad data, since any value apart from 0 or 1 can be 379 * interpreted as invalid data. 380 */ 381 if (nx.valid != 0 && nx.valid != 1) 382 fail(2, "data file not in sar format"); 383 state_change = 0; 384 niodevs = nx.niodevs; 385 /* 386 * niodevs has the value of current number of devices 387 * from nx structure. 388 * 389 * The following 'if' condition is to decide whether memory 390 * has to be allocated or not if already allocated memory is 391 * bigger or smaller than memory needed to store the current 392 * niodevs details in memory. 393 * 394 * when first while loop starts, pre_niodevs has 0 and then 395 * always get initialized to the current number of devices 396 * from nx.niodevs if it is different from previously read 397 * niodevs. 398 * 399 * if the current niodevs has the same value of previously 400 * allocated memory i.e, for prev_niodevs, it skips the 401 * following 'if' loop or otherwise it allocates memory for 402 * current devises (niodevs) and stores that value in 403 * prev_niodevs for next time when loop continues to read 404 * from the file. 405 */ 406 if (niodevs != prev_niodevs) { 407 off_t curr_pos; 408 /* 409 * The required buffer size must fit in a size_t. 410 */ 411 if (SIZE_MAX / sizeof (iodevinfo_t) < niodevs) 412 fail(2, "insufficient address space to hold " 413 "%lu device records", niodevs); 414 size = niodevs * sizeof (iodevinfo_t); 415 prev_niodevs = niodevs; 416 /* 417 * The data file must exceed this size to be valid. 418 */ 419 if (!input_pipe) { 420 if ((curr_pos = lseek(fin, 0, SEEK_CUR)) == 421 (off_t)-1) 422 fail(1, "lseek failed"); 423 if (in_stat.st_size < curr_pos || 424 size > in_stat.st_size - curr_pos) 425 fail(2, "data file corrupt; specified size" 426 "exceeds actual"); 427 } 428 429 safe_zalloc((void **)&nxio, size, 1); 430 } 431 if (niodevs != old_niodevs) 432 state_change = 1; 433 for (i = 0; i < niodevs; i++) { 434 if (safe_read(fin, &nxio[i], sizeof (iodevinfo_t)) == 0) 435 fail(1, "premature end-of-file seen"); 436 if (i < old_niodevs && 437 nxio[i].ks.ks_kid != oxio[i].ks.ks_kid) 438 state_change = 1; 439 } 440 curt = localtime(&nx.ts); 441 trec = curt->tm_hour * 3600.0 + 442 curt->tm_min * 60.0 + 443 curt->tm_sec; 444 if ((recno == 0) && (trec < start_time)) 445 continue; 446 if ((eflg) && (trec > end_time)) 447 break; 448 if ((oflg) && (passno == 1)) { 449 /* 450 * The calculated values are stroed in nx strcuture. 451 * Convert 64-bit nx to 32-bit tx structure. 452 */ 453 tx.valid = nx.valid; 454 tx.ts = nx.ts; 455 convert_64to32((uint_t *)&tx.csi, (uint64_t *)&nx.csi, 456 sizeof (nx.csi) / sizeof (uint64_t)); 457 convert_64to32((uint_t *)&tx.cvmi, (uint64_t *)&nx.cvmi, 458 sizeof (nx.cvmi) / sizeof (uint64_t)); 459 convert_64to32((uint_t *)&tx.si, (uint64_t *)&nx.si, 460 sizeof (nx.si) / sizeof (uint64_t)); 461 (void) memcpy(&tx.vmi, &nx.vmi, 462 sizeof (nx) - (((char *)&nx.vmi) - ((char *)&nx))); 463 if (tx.valid != 0 && tx.valid != 1) 464 fail(2, "data file not in sar format"); 465 466 safe_write(fout, &tx, sizeof (struct sa)); 467 for (i = 0; i < niodevs; i++) 468 safe_write(fout, &nxio[i], 469 sizeof (iodevinfo_t)); 470 } 471 472 if (recno == 0) { 473 if (passno == 1) 474 prtmachid(); 475 476 prthdg(); 477 recno = 1; 478 if ((iflg) && (tnext == 0)) 479 tnext = trec; 480 } 481 482 if (nx.valid == 0) { 483 /* 484 * This dummy record signifies system restart 485 * New initial values of counters follow in next 486 * record. 487 */ 488 if (!realtime) { 489 prttim(); 490 (void) printf("\tunix restarts\n"); 491 recno = 1; 492 continue; 493 } 494 } 495 if ((iflg) && (trec < tnext)) 496 continue; 497 498 if (state_change) { 499 /* 500 * Either the number of devices or the ordering of 501 * the kstats has changed. We need to re-organise 502 * the layout of our avg/delta arrays so that we 503 * can cope with this in update_counters(). 504 */ 505 size = niodevs * sizeof (iodevinfo_t); 506 safe_zalloc((void *)&aio, size, 0); 507 safe_zalloc((void *)&dio, size, 0); 508 safe_zalloc((void *)&oio, size, 0); 509 510 /* 511 * Loop through all the newly read iodev's, locate 512 * the corresponding entry in the old arrays and 513 * copy the entries into the same bucket of the 514 * new arrays. 515 */ 516 for (i = 0; i < niodevs; i++) { 517 kid = nxio[i].ks.ks_kid; 518 for (j = 0; j < old_niodevs; j++) { 519 if (oxio[j].ks.ks_kid == kid) { 520 oio[i] = oxio[j]; 521 aio[i] = axio[j]; 522 dio[i] = dxio[j]; 523 } 524 } 525 } 526 527 free(axio); 528 free(oxio); 529 free(dxio); 530 531 axio = aio; 532 oxio = oio; 533 dxio = dio; 534 535 old_niodevs = niodevs; 536 } 537 538 if (recno++ > 1) { 539 ts = ox.csi.cpu[0] + ox.csi.cpu[1] + 540 ox.csi.cpu[2] + ox.csi.cpu[3]; 541 te = nx.csi.cpu[0] + nx.csi.cpu[1] + 542 nx.csi.cpu[2] + nx.csi.cpu[3]; 543 tdiff = (float)(te - ts); 544 sec_diff = tdiff / hz; 545 percent = 100.0 / tdiff; 546 547 /* 548 * If the CPU stat counters have rolled 549 * backward, this is our best indication that 550 * a CPU has been offlined. We don't have 551 * enough data to compute a sensible delta, so 552 * toss out this interval, but compute the next 553 * interval's delta from these values. 554 */ 555 if (tdiff <= 0) { 556 ox = nx; 557 continue; 558 } 559 update_counters(); 560 prtopt(); 561 lines++; 562 if (passno == 1) 563 totsec_diff += sec_diff; 564 } 565 ox = nx; /* Age the data */ 566 (void) memcpy(oxio, nxio, niodevs * sizeof (iodevinfo_t)); 567 if (isec > 0) 568 while (tnext <= trec) 569 tnext += isec; 570 } 571 /* 572 * After this place, all functions are using niodevs to access the 573 * memory for device details. Here, old_niodevs has the correct value 574 * of memory allocated for storing device information. Since niodevs 575 * doesn't have correct value, sometimes, it was corrupting memory. 576 */ 577 niodevs = old_niodevs; 578 if (lines > 1) 579 prtavg(); 580 (void) memset(&ax, 0, sizeof (ax)); /* Zero out the accumulators. */ 581 (void) memset(&kmi, 0, sizeof (kmi)); 582 lines = 0; 583 /* 584 * axio will not be allocated if the user specified -e or -s, and 585 * no records in the file fell inside the specified time range. 586 */ 587 if (axio) { 588 (void) memset(axio, 0, niodevs * sizeof (iodevinfo_t)); 589 } 590 } 591 592 /* 593 * Print time label routine. 594 */ 595 static void 596 prttim(void) 597 { 598 curt = localtime(&nx.ts); 599 (void) printf("%.2d:%.2d:%.2d", curt->tm_hour, curt->tm_min, 600 curt->tm_sec); 601 tabflg = 1; 602 } 603 604 /* 605 * Test if 8-spaces to be added routine. 606 */ 607 static void 608 tsttab(void) 609 { 610 if (tabflg == 0) 611 (void) printf(" "); 612 else 613 tabflg = 0; 614 } 615 616 /* 617 * Print machine identification. 618 */ 619 static void 620 prtmachid(void) 621 { 622 struct utsname name; 623 624 (void) uname(&name); 625 (void) printf("\n%s %s %s %s %s %.2d/%.2d/%.4d\n", 626 name.sysname, name.nodename, name.release, name.version, 627 name.machine, curt->tm_mon + 1, curt->tm_mday, 628 curt->tm_year + 1900); 629 } 630 631 /* 632 * Print report heading routine. 633 */ 634 static void 635 prthdg(void) 636 { 637 int jj = 0; 638 char ccc; 639 640 (void) printf("\n"); 641 prttim(); 642 while ((ccc = fopt[jj++]) != NULL) { 643 tsttab(); 644 switch (ccc) { 645 case 'u': 646 (void) printf(" %7s %7s %7s %7s\n", 647 "%usr", 648 "%sys", 649 "%wio", 650 "%idle"); 651 break; 652 case 'b': 653 (void) printf(" %7s %7s %7s %7s %7s %7s %7s %7s\n", 654 "bread/s", 655 "lread/s", 656 "%rcache", 657 "bwrit/s", 658 "lwrit/s", 659 "%wcache", 660 "pread/s", 661 "pwrit/s"); 662 break; 663 case 'd': 664 (void) printf(" %-8.8s %7s %7s %7s %7s %7s %7s\n", 665 "device", 666 "%busy", 667 "avque", 668 "r+w/s", 669 "blks/s", 670 "avwait", 671 "avserv"); 672 break; 673 case 'y': 674 (void) printf(" %7s %7s %7s %7s %7s %7s\n", 675 "rawch/s", 676 "canch/s", 677 "outch/s", 678 "rcvin/s", 679 "xmtin/s", 680 "mdmin/s"); 681 break; 682 case 'c': 683 (void) printf(" %7s %7s %7s %7s %7s %7s %7s\n", 684 "scall/s", 685 "sread/s", 686 "swrit/s", 687 "fork/s", 688 "exec/s", 689 "rchar/s", 690 "wchar/s"); 691 break; 692 case 'w': 693 (void) printf(" %7s %7s %7s %7s %7s\n", 694 "swpin/s", 695 "bswin/s", 696 "swpot/s", 697 "bswot/s", 698 "pswch/s"); 699 break; 700 case 'a': 701 (void) printf(" %7s %7s %7s\n", 702 "iget/s", 703 "namei/s", 704 "dirbk/s"); 705 break; 706 case 'q': 707 (void) printf(" %7s %7s %7s %7s\n", 708 "runq-sz", 709 "%runocc", 710 "swpq-sz", 711 "%swpocc"); 712 break; 713 case 'v': 714 (void) printf(" %s %s %s %s\n", 715 "proc-sz ov", 716 "inod-sz ov", 717 "file-sz ov", 718 "lock-sz"); 719 break; 720 case 'm': 721 (void) printf(" %7s %7s\n", 722 "msg/s", 723 "sema/s"); 724 break; 725 case 'p': 726 (void) printf(" %7s %7s %7s %7s %7s %7s\n", 727 "atch/s", 728 "pgin/s", 729 "ppgin/s", 730 "pflt/s", 731 "vflt/s", 732 "slock/s"); 733 break; 734 case 'g': 735 (void) printf(" %8s %8s %8s %8s %8s\n", 736 "pgout/s", 737 "ppgout/s", 738 "pgfree/s", 739 "pgscan/s", 740 "%ufs_ipf"); 741 break; 742 case 'r': 743 (void) printf(" %7s %8s\n", 744 "freemem", 745 "freeswap"); 746 break; 747 case 'k': 748 (void) printf(" %7s %7s %5s %7s %7s %5s %11s %5s\n", 749 "sml_mem", 750 "alloc", 751 "fail", 752 "lg_mem", 753 "alloc", 754 "fail", 755 "ovsz_alloc", 756 "fail"); 757 break; 758 } 759 } 760 if (jj > 2 || do_disk) 761 (void) printf("\n"); 762 } 763 764 /* 765 * compute deltas and update accumulators 766 */ 767 static void 768 update_counters(void) 769 { 770 int i; 771 iodevinfo_t *nio, *oio, *aio, *dio; 772 773 ulong_delta((uint64_t *)&nx.csi, (uint64_t *)&ox.csi, 774 (uint64_t *)&dx.csi, (uint64_t *)&ax.csi, 0, sizeof (ax.csi)); 775 ulong_delta((uint64_t *)&nx.si, (uint64_t *)&ox.si, 776 (uint64_t *)&dx.si, (uint64_t *)&ax.si, 0, sizeof (ax.si)); 777 ulong_delta((uint64_t *)&nx.cvmi, (uint64_t *)&ox.cvmi, 778 (uint64_t *)&dx.cvmi, (uint64_t *)&ax.cvmi, 0, 779 sizeof (ax.cvmi)); 780 781 ax.vmi.freemem += dx.vmi.freemem = nx.vmi.freemem - ox.vmi.freemem; 782 ax.vmi.swap_avail += dx.vmi.swap_avail = 783 nx.vmi.swap_avail - ox.vmi.swap_avail; 784 785 nio = nxio; 786 oio = oxio; 787 aio = axio; 788 dio = dxio; 789 for (i = 0; i < niodevs; i++) { 790 aio->kios.wlastupdate += dio->kios.wlastupdate 791 = nio->kios.wlastupdate - oio->kios.wlastupdate; 792 aio->kios.reads += dio->kios.reads 793 = nio->kios.reads - oio->kios.reads; 794 aio->kios.writes += dio->kios.writes 795 = nio->kios.writes - oio->kios.writes; 796 aio->kios.nread += dio->kios.nread 797 = nio->kios.nread - oio->kios.nread; 798 aio->kios.nwritten += dio->kios.nwritten 799 = nio->kios.nwritten - oio->kios.nwritten; 800 aio->kios.wlentime += dio->kios.wlentime 801 = nio->kios.wlentime - oio->kios.wlentime; 802 aio->kios.rlentime += dio->kios.rlentime 803 = nio->kios.rlentime - oio->kios.rlentime; 804 aio->kios.wtime += dio->kios.wtime 805 = nio->kios.wtime - oio->kios.wtime; 806 aio->kios.rtime += dio->kios.rtime 807 = nio->kios.rtime - oio->kios.rtime; 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; 843 844 tsttab(); 845 846 hr_etime = (double)xio[ii].kios.wlastupdate; 847 if (hr_etime == 0.0) 848 hr_etime = (double)NANOSEC; 849 etime = hr_etime / (double)NANOSEC; 850 tps = (double)(xio[ii].kios.reads + xio[ii].kios.writes) / etime; 851 avq = (double)xio[ii].kios.wlentime / hr_etime; 852 avs = (double)xio[ii].kios.rlentime / hr_etime; 853 854 (void) printf(" %-8.8s ", nxio[ii].ks.ks_name); 855 (void) printf("%7.0f %7.1f %7.0f %7.0f %7.1f %7.1f\n", 856 (double)xio[ii].kios.rtime * 100.0 / hr_etime, 857 avq + avs, 858 tps, 859 BLKS(xio[ii].kios.nread + xio[ii].kios.nwritten) / etime, 860 (tps > 0 ? avq / tps * 1000.0 : 0.0), 861 (tps > 0 ? avs / tps * 1000.0 : 0.0)); 862 } 863 864 static void 865 prt_y_opt(struct sa64 *xx) 866 { 867 (void) printf(" %7.0f %7.0f %7.0f %7.0f %7.0f %7.0f\n", 868 (float)xx->csi.rawch / sec_diff, 869 (float)xx->csi.canch / sec_diff, 870 (float)xx->csi.outch / sec_diff, 871 (float)xx->csi.rcvint / sec_diff, 872 (float)xx->csi.xmtint / sec_diff, 873 (float)xx->csi.mdmint / sec_diff); 874 } 875 876 static void 877 prt_c_opt(struct sa64 *xx) 878 { 879 (void) printf(" %7.0f %7.0f %7.0f %7.2f %7.2f %7.0f %7.0f\n", 880 (float)xx->csi.syscall / sec_diff, 881 (float)xx->csi.sysread / sec_diff, 882 (float)xx->csi.syswrite / sec_diff, 883 (float)(xx->csi.sysfork + xx->csi.sysvfork) / sec_diff, 884 (float)xx->csi.sysexec / sec_diff, 885 (float)xx->csi.readch / sec_diff, 886 (float)xx->csi.writech / sec_diff); 887 } 888 889 static void 890 prt_w_opt(struct sa64 *xx) 891 { 892 (void) printf(" %7.2f %7.1f %7.2f %7.1f %7.0f\n", 893 (float)xx->cvmi.swapin / sec_diff, 894 (float)PGTOBLK(xx->cvmi.pgswapin) / sec_diff, 895 (float)xx->cvmi.swapout / sec_diff, 896 (float)PGTOBLK(xx->cvmi.pgswapout) / sec_diff, 897 (float)xx->csi.pswitch / sec_diff); 898 } 899 900 static void 901 prt_a_opt(struct sa64 *xx) 902 { 903 (void) printf(" %7.0f %7.0f %7.0f\n", 904 (float)xx->csi.ufsiget / sec_diff, 905 (float)xx->csi.namei / sec_diff, 906 (float)xx->csi.ufsdirblk / sec_diff); 907 } 908 909 static void 910 prt_q_opt(struct sa64 *xx) 911 { 912 if (xx->si.runocc == 0 || xx->si.updates == 0) 913 (void) printf(" %7.1f %7.0f", 0., 0.); 914 else { 915 (void) printf(" %7.1f %7.0f", 916 (float)xx->si.runque / (float)xx->si.runocc, 917 (float)xx->si.runocc / (float)xx->si.updates * 100.0); 918 } 919 if (xx->si.swpocc == 0 || xx->si.updates == 0) 920 (void) printf(" %7.1f %7.0f\n", 0., 0.); 921 else { 922 (void) printf(" %7.1f %7.0f\n", 923 (float)xx->si.swpque / (float)xx->si.swpocc, 924 (float)xx->si.swpocc / (float)xx->si.updates * 100.0); 925 } 926 } 927 928 static void 929 prt_v_opt(struct sa64 *xx) 930 { 931 (void) printf(" %4lu/%-4lu %4llu %4lu/%-4lu %4llu %4lu/%-4lu " 932 "%4llu %4lu/%-4lu\n", 933 nx.szproc, nx.mszproc, xx->csi.procovf, 934 nx.szinode, nx.mszinode, xx->csi.inodeovf, 935 nx.szfile, nx.mszfile, xx->csi.fileovf, 936 nx.szlckr, nx.mszlckr); 937 } 938 939 static void 940 prt_m_opt(struct sa64 *xx) 941 { 942 (void) printf(" %7.2f %7.2f\n", 943 (float)xx->csi.msg / sec_diff, 944 (float)xx->csi.sema / sec_diff); 945 } 946 947 static void 948 prt_p_opt(struct sa64 *xx) 949 { 950 (void) printf(" %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n", 951 (float)xx->cvmi.pgfrec / sec_diff, 952 (float)xx->cvmi.pgin / sec_diff, 953 (float)xx->cvmi.pgpgin / sec_diff, 954 (float)(xx->cvmi.prot_fault + xx->cvmi.cow_fault) / sec_diff, 955 (float)(xx->cvmi.hat_fault + xx->cvmi.as_fault) / sec_diff, 956 (float)xx->cvmi.softlock / sec_diff); 957 } 958 959 static void 960 prt_g_opt(struct sa64 *xx) 961 { 962 (void) printf(" %8.2f %8.2f %8.2f %8.2f %8.2f\n", 963 (float)xx->cvmi.pgout / sec_diff, 964 (float)xx->cvmi.pgpgout / sec_diff, 965 (float)xx->cvmi.dfree / sec_diff, 966 (float)xx->cvmi.scan / sec_diff, 967 (float)xx->csi.ufsipage * 100.0 / 968 denom((float)xx->csi.ufsipage + 969 (float)xx->csi.ufsinopage)); 970 } 971 972 static void 973 prt_r_opt(struct sa64 *xx) 974 { 975 /* Avoid divide by Zero - Should never happen */ 976 if (xx->si.updates == 0) 977 (void) printf(" %7.0f %8.0f\n", 0., 0.); 978 else { 979 (void) printf(" %7.0f %8.0f\n", 980 (double)xx->vmi.freemem / (float)xx->si.updates, 981 (double)PGTOBLK(xx->vmi.swap_avail) / 982 (float)xx->si.updates); 983 } 984 } 985 986 static void 987 prt_k_opt(struct sa64 *xx, int n) 988 { 989 if (n != 1) { 990 (void) printf(" %7.0f %7.0f %5.0f %7.0f %7.0f %5.0f %11.0f" 991 " %5.0f\n", 992 (float)kmi.km_mem[KMEM_SMALL] / n, 993 (float)kmi.km_alloc[KMEM_SMALL] / n, 994 (float)kmi.km_fail[KMEM_SMALL] / n, 995 (float)kmi.km_mem[KMEM_LARGE] / n, 996 (float)kmi.km_alloc[KMEM_LARGE] / n, 997 (float)kmi.km_fail[KMEM_LARGE] / n, 998 (float)kmi.km_alloc[KMEM_OSIZE] / n, 999 (float)kmi.km_fail[KMEM_OSIZE] / n); 1000 } else { 1001 /* 1002 * If we are not reporting averages, use the read values 1003 * directly. 1004 */ 1005 (void) printf(" %7.0f %7.0f %5.0f %7.0f %7.0f %5.0f %11.0f" 1006 " %5.0f\n", 1007 (float)xx->kmi.km_mem[KMEM_SMALL], 1008 (float)xx->kmi.km_alloc[KMEM_SMALL], 1009 (float)xx->kmi.km_fail[KMEM_SMALL], 1010 (float)xx->kmi.km_mem[KMEM_LARGE], 1011 (float)xx->kmi.km_alloc[KMEM_LARGE], 1012 (float)xx->kmi.km_fail[KMEM_LARGE], 1013 (float)xx->kmi.km_alloc[KMEM_OSIZE], 1014 (float)xx->kmi.km_fail[KMEM_OSIZE]); 1015 } 1016 } 1017 1018 /* 1019 * Print options routine. 1020 */ 1021 static void 1022 prtopt(void) 1023 { 1024 int ii, jj = 0; 1025 char ccc; 1026 1027 prttim(); 1028 1029 while ((ccc = fopt[jj++]) != NULL) { 1030 if (ccc != 'd') 1031 tsttab(); 1032 switch (ccc) { 1033 case 'u': 1034 prt_u_opt(&dx); 1035 break; 1036 case 'b': 1037 prt_b_opt(&dx); 1038 break; 1039 case 'd': 1040 for (ii = 0; ii < niodevs; ii++) 1041 prt_d_opt(ii, dxio); 1042 break; 1043 case 'y': 1044 prt_y_opt(&dx); 1045 break; 1046 case 'c': 1047 prt_c_opt(&dx); 1048 break; 1049 case 'w': 1050 prt_w_opt(&dx); 1051 break; 1052 case 'a': 1053 prt_a_opt(&dx); 1054 break; 1055 case 'q': 1056 prt_q_opt(&dx); 1057 break; 1058 case 'v': 1059 prt_v_opt(&dx); 1060 break; 1061 case 'm': 1062 prt_m_opt(&dx); 1063 break; 1064 case 'p': 1065 prt_p_opt(&dx); 1066 break; 1067 case 'g': 1068 prt_g_opt(&dx); 1069 break; 1070 case 'r': 1071 prt_r_opt(&dx); 1072 break; 1073 case 'k': 1074 prt_k_opt(&nx, 1); 1075 /* 1076 * To avoid overflow, copy the data from the sa record 1077 * into a struct kmeminfo_l which has members with 1078 * larger data types. 1079 */ 1080 kmi.km_mem[KMEM_SMALL] += nx.kmi.km_mem[KMEM_SMALL]; 1081 kmi.km_alloc[KMEM_SMALL] += nx.kmi.km_alloc[KMEM_SMALL]; 1082 kmi.km_fail[KMEM_SMALL] += nx.kmi.km_fail[KMEM_SMALL]; 1083 kmi.km_mem[KMEM_LARGE] += nx.kmi.km_mem[KMEM_LARGE]; 1084 kmi.km_alloc[KMEM_LARGE] += nx.kmi.km_alloc[KMEM_LARGE]; 1085 kmi.km_fail[KMEM_LARGE] += nx.kmi.km_fail[KMEM_LARGE]; 1086 kmi.km_alloc[KMEM_OSIZE] += nx.kmi.km_alloc[KMEM_OSIZE]; 1087 kmi.km_fail[KMEM_OSIZE] += nx.kmi.km_fail[KMEM_OSIZE]; 1088 break; 1089 } 1090 } 1091 if (jj > 2 || do_disk) 1092 (void) printf("\n"); 1093 if (realtime) 1094 (void) fflush(stdout); 1095 } 1096 1097 /* 1098 * Print average routine. 1099 */ 1100 static void 1101 prtavg(void) 1102 { 1103 int ii, jj = 0; 1104 char ccc; 1105 1106 tdiff = ax.csi.cpu[0] + ax.csi.cpu[1] + ax.csi.cpu[2] + ax.csi.cpu[3]; 1107 if (tdiff <= 0.0) 1108 return; 1109 1110 sec_diff = tdiff / hz; 1111 percent = 100.0 / tdiff; 1112 (void) printf("\n"); 1113 1114 while ((ccc = fopt[jj++]) != NULL) { 1115 if (ccc != 'v') 1116 (void) printf("Average "); 1117 switch (ccc) { 1118 case 'u': 1119 prt_u_opt(&ax); 1120 break; 1121 case 'b': 1122 prt_b_opt(&ax); 1123 break; 1124 case 'd': 1125 tabflg = 1; 1126 for (ii = 0; ii < niodevs; ii++) 1127 prt_d_opt(ii, axio); 1128 break; 1129 case 'y': 1130 prt_y_opt(&ax); 1131 break; 1132 case 'c': 1133 prt_c_opt(&ax); 1134 break; 1135 case 'w': 1136 prt_w_opt(&ax); 1137 break; 1138 case 'a': 1139 prt_a_opt(&ax); 1140 break; 1141 case 'q': 1142 prt_q_opt(&ax); 1143 break; 1144 case 'v': 1145 break; 1146 case 'm': 1147 prt_m_opt(&ax); 1148 break; 1149 case 'p': 1150 prt_p_opt(&ax); 1151 break; 1152 case 'g': 1153 prt_g_opt(&ax); 1154 break; 1155 case 'r': 1156 prt_r_opt(&ax); 1157 break; 1158 case 'k': 1159 prt_k_opt(&ax, lines); 1160 break; 1161 } 1162 } 1163 } 1164 1165 static void 1166 ulong_delta(uint64_t *new, uint64_t *old, uint64_t *delta, uint64_t *accum, 1167 int begin, int end) 1168 { 1169 int i; 1170 uint64_t n, o, d; 1171 1172 for (i = begin; i < end; i += sizeof (uint64_t)) { 1173 n = *new++; 1174 o = *old++; 1175 if (o > n) { 1176 d = n + 0x100000000LL - o; 1177 } else { 1178 d = n - o; 1179 } 1180 *accum++ += *delta++ = d; 1181 } 1182 } 1183 1184 /* 1185 * used to prevent zero denominators 1186 */ 1187 static float 1188 denom(float x) 1189 { 1190 return ((x > 0.5) ? x : 1.0); 1191 } 1192 1193 /* 1194 * a little calculation that comes up often when computing frequency 1195 * of one operation relative to another 1196 */ 1197 static float 1198 freq(float x, float y) 1199 { 1200 return ((x < 0.5) ? 100.0 : (x - y) / x * 100.0); 1201 } 1202 1203 static void 1204 usage(void) 1205 { 1206 (void) fprintf(stderr, 1207 "usage: sar [-ubdycwaqvmpgrkA][-o file] t [n]\n" 1208 "\tsar [-ubdycwaqvmpgrkA] [-s hh:mm][-e hh:mm][-i ss][-f file]\n"); 1209 } 1210 1211 static void 1212 fail(int do_perror, char *message, ...) 1213 { 1214 va_list args; 1215 1216 va_start(args, message); 1217 (void) fprintf(stderr, "sar: "); 1218 (void) vfprintf(stderr, message, args); 1219 va_end(args); 1220 (void) fprintf(stderr, "\n"); 1221 switch (do_perror) { 1222 case 0: /* usage message */ 1223 usage(); 1224 break; 1225 case 1: /* perror output */ 1226 perror(""); 1227 break; 1228 case 2: /* no further output */ 1229 break; 1230 default: /* error */ 1231 (void) fprintf(stderr, "unsupported failure mode\n"); 1232 break; 1233 } 1234 exit(2); 1235 } 1236 1237 static int 1238 safe_strtoi(char const *val, char *errmsg) 1239 { 1240 char *end; 1241 long tmp; 1242 1243 errno = 0; 1244 tmp = strtol(val, &end, 10); 1245 if (*end != '\0' || errno) 1246 fail(0, "%s %s", errmsg, val); 1247 return ((int)tmp); 1248 } 1249 1250 static void 1251 safe_zalloc(void **ptr, int size, int free_first) 1252 { 1253 if (free_first && *ptr != NULL) 1254 free(*ptr); 1255 if ((*ptr = malloc(size)) == NULL) 1256 fail(1, "malloc failed"); 1257 (void) memset(*ptr, 0, size); 1258 } 1259 1260 static int 1261 safe_read(int fd, void *buf, size_t size) 1262 { 1263 size_t rsize = read(fd, buf, size); 1264 1265 if (rsize == 0) 1266 return (0); 1267 1268 if (rsize != size) 1269 fail(1, "read failed"); 1270 1271 return (1); 1272 } 1273 1274 static void 1275 safe_write(int fd, void *buf, size_t size) 1276 { 1277 if (write(fd, buf, size) != size) 1278 fail(1, "write failed"); 1279 } 1280