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