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