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