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