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