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
main(int argc,char ** argv)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
convert_32to64(uint64_t * dst,uint_t * src,int size)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
convert_64to32(uint_t * dst,uint64_t * src,int size)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
prpass(int input_pipe)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
prttim(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
tsttab(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
prtmachid(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
prthdg(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
update_counters(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
prt_u_opt(struct sa64 * xx)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
prt_b_opt(struct sa64 * xx)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
prt_d_opt(int ii,iodevinfo_t * xio)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
prt_y_opt(struct sa64 * xx)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
prt_c_opt(struct sa64 * xx)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
prt_w_opt(struct sa64 * xx)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
prt_a_opt(struct sa64 * xx)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
prt_q_opt(struct sa64 * xx)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
prt_v_opt(struct sa64 * xx)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
prt_m_opt(struct sa64 * xx)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
prt_p_opt(struct sa64 * xx)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
prt_g_opt(struct sa64 * xx)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
prt_r_opt(struct sa64 * xx)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
prt_k_opt(struct sa64 * xx,int n)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
prtopt(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
prtavg(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
ulong_delta(uint64_t * new,uint64_t * old,uint64_t * delta,uint64_t * accum,int begin,int end)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
denom(float x)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
freq(float x,float y)1201 freq(float x, float y)
1202 {
1203 return ((x < 0.5) ? 100.0 : (x - y) / x * 100.0);
1204 }
1205
1206 static void
usage(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
fail(int do_perror,char * message,...)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
safe_strtoi(char const * val,char * errmsg)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
safe_zalloc(void ** ptr,int size,int free_first)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
safe_read(int fd,void * buf,size_t size)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
safe_write(int fd,void * buf,size_t size)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