xref: /onnv-gate/usr/src/cmd/cpc/common/cpustat.c (revision 10265:b988146c84e5)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
53438Sas198278  * Common Development and Distribution License (the "License").
63438Sas198278  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*10265SKrishnendu.Sadhukhan@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <sys/types.h>
270Sstevel@tonic-gate #include <sys/processor.h>
280Sstevel@tonic-gate #include <sys/pset.h>
290Sstevel@tonic-gate #include <sys/lwp.h>
300Sstevel@tonic-gate #include <sys/priocntl.h>
310Sstevel@tonic-gate #include <sys/fxpriocntl.h>
320Sstevel@tonic-gate #include <time.h>
330Sstevel@tonic-gate #include <stdio.h>
340Sstevel@tonic-gate #include <stdlib.h>
350Sstevel@tonic-gate #include <inttypes.h>
360Sstevel@tonic-gate #include <unistd.h>
370Sstevel@tonic-gate #include <limits.h>
380Sstevel@tonic-gate #include <string.h>
390Sstevel@tonic-gate #include <strings.h>
400Sstevel@tonic-gate #include <thread.h>
410Sstevel@tonic-gate #include <errno.h>
420Sstevel@tonic-gate #include <libintl.h>
430Sstevel@tonic-gate #include <locale.h>
440Sstevel@tonic-gate #include <kstat.h>
450Sstevel@tonic-gate #include <synch.h>
460Sstevel@tonic-gate #include <libcpc.h>
473438Sas198278 #include <sys/resource.h>
480Sstevel@tonic-gate 
490Sstevel@tonic-gate #include "cpucmds.h"
50*10265SKrishnendu.Sadhukhan@Sun.COM #include "statcommon.h"
510Sstevel@tonic-gate 
520Sstevel@tonic-gate static struct options {
530Sstevel@tonic-gate 	int debug;
540Sstevel@tonic-gate 	int dotitle;
550Sstevel@tonic-gate 	int dohelp;
560Sstevel@tonic-gate 	int dotick;
570Sstevel@tonic-gate 	int dosoaker;
580Sstevel@tonic-gate 	int doperiod;
590Sstevel@tonic-gate 	char *pgmname;
600Sstevel@tonic-gate 	uint_t mseconds;
610Sstevel@tonic-gate 	uint_t nsamples;
620Sstevel@tonic-gate 	uint_t nsets;
630Sstevel@tonic-gate 	uint_t mseconds_rest;
640Sstevel@tonic-gate 	cpc_setgrp_t *master;
650Sstevel@tonic-gate } __options;
660Sstevel@tonic-gate 
670Sstevel@tonic-gate /*
680Sstevel@tonic-gate  * States for soaker threads.
690Sstevel@tonic-gate  */
700Sstevel@tonic-gate #define	SOAK_PAUSE	0
710Sstevel@tonic-gate #define	SOAK_RUN	1
720Sstevel@tonic-gate 
730Sstevel@tonic-gate struct tstate {
740Sstevel@tonic-gate 	processorid_t	cpuid;
750Sstevel@tonic-gate 	int		chip_id;
760Sstevel@tonic-gate 	cpc_setgrp_t	*sgrp;
770Sstevel@tonic-gate 	int		status;
780Sstevel@tonic-gate 	thread_t	tid;
790Sstevel@tonic-gate 	int		soak_state;
800Sstevel@tonic-gate 	mutex_t		soak_lock;
810Sstevel@tonic-gate 	cond_t		soak_cv;
820Sstevel@tonic-gate };
830Sstevel@tonic-gate 
840Sstevel@tonic-gate static const struct options *opts = (const struct options *)&__options;
850Sstevel@tonic-gate 
860Sstevel@tonic-gate static cpc_t *cpc;
870Sstevel@tonic-gate 
880Sstevel@tonic-gate struct tstate	*gstate;
890Sstevel@tonic-gate static int	ncpus;
900Sstevel@tonic-gate static int	max_chip_id;
910Sstevel@tonic-gate static int	*chip_designees;    /* cpuid of CPU which counts for phs chip */
920Sstevel@tonic-gate static int	smt = 0;	    /* If set, cpustat needs to be SMT-aware. */
930Sstevel@tonic-gate static pcinfo_t	fxinfo = { 0, "FX", NULL }; /* FX scheduler class info */
940Sstevel@tonic-gate 
95*10265SKrishnendu.Sadhukhan@Sun.COM static uint_t timestamp_fmt = NODATE;
96*10265SKrishnendu.Sadhukhan@Sun.COM 
970Sstevel@tonic-gate /*ARGSUSED*/
980Sstevel@tonic-gate static void
cpustat_errfn(const char * fn,int subcode,const char * fmt,va_list ap)990Sstevel@tonic-gate cpustat_errfn(const char *fn, int subcode, const char *fmt, va_list ap)
1000Sstevel@tonic-gate {
1010Sstevel@tonic-gate 	(void) fprintf(stderr, "%s: ", opts->pgmname);
1020Sstevel@tonic-gate 	if (opts->debug)
1030Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: ", fn);
1040Sstevel@tonic-gate 	(void) vfprintf(stderr, fmt, ap);
1050Sstevel@tonic-gate }
1060Sstevel@tonic-gate 
1070Sstevel@tonic-gate static int cpustat(void);
1080Sstevel@tonic-gate static int get_chipid(kstat_ctl_t *kc, processorid_t cpuid);
1090Sstevel@tonic-gate static void *soaker(void *arg);
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate 
1120Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
1130Sstevel@tonic-gate #define	TEXT_DOMAIN	"SYS_TEST"
1140Sstevel@tonic-gate #endif
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate int
main(int argc,char * argv[])1170Sstevel@tonic-gate main(int argc, char *argv[])
1180Sstevel@tonic-gate {
1190Sstevel@tonic-gate 	struct options	*opts = &__options;
1200Sstevel@tonic-gate 	int		c, errcnt = 0, ret;
1210Sstevel@tonic-gate 	cpc_setgrp_t	*sgrp;
1220Sstevel@tonic-gate 	char		*errstr;
1230Sstevel@tonic-gate 	double		period;
1240Sstevel@tonic-gate 	char		*endp;
1253438Sas198278 	struct rlimit	rl;
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
1280Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate 	if ((opts->pgmname = strrchr(argv[0], '/')) == NULL)
1310Sstevel@tonic-gate 		opts->pgmname = argv[0];
1320Sstevel@tonic-gate 	else
1330Sstevel@tonic-gate 		opts->pgmname++;
1340Sstevel@tonic-gate 
1353438Sas198278 	/* Make sure we can open enough files */
1363438Sas198278 	rl.rlim_max = rl.rlim_cur = RLIM_INFINITY;
1373438Sas198278 	if (setrlimit(RLIMIT_NOFILE, &rl) != 0) {
1383438Sas198278 		errstr = strerror(errno);
1393438Sas198278 		(void) fprintf(stderr,
140*10265SKrishnendu.Sadhukhan@Sun.COM 		    gettext("%s: setrlimit failed - %s\n"),
141*10265SKrishnendu.Sadhukhan@Sun.COM 		    opts->pgmname, errstr);
1423438Sas198278 	}
1433438Sas198278 
1440Sstevel@tonic-gate 	if ((cpc = cpc_open(CPC_VER_CURRENT)) == NULL) {
1450Sstevel@tonic-gate 		errstr = strerror(errno);
1460Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: cannot access performance "
1470Sstevel@tonic-gate 		    "counters - %s\n"), opts->pgmname, errstr);
1480Sstevel@tonic-gate 		return (1);
1490Sstevel@tonic-gate 	}
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate 	(void) cpc_seterrhndlr(cpc, cpustat_errfn);
1520Sstevel@tonic-gate 	strtoset_errfn = cpustat_errfn;
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate 	/*
1550Sstevel@tonic-gate 	 * Check to see if cpustat needs to be SMT-aware.
1560Sstevel@tonic-gate 	 */
1570Sstevel@tonic-gate 	smt = smt_limited_cpc_hw(cpc);
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate 	/*
1600Sstevel@tonic-gate 	 * Establish some defaults
1610Sstevel@tonic-gate 	 */
1620Sstevel@tonic-gate 	opts->mseconds = 5000;
1630Sstevel@tonic-gate 	opts->nsamples = UINT_MAX;
1640Sstevel@tonic-gate 	opts->dotitle = 1;
1650Sstevel@tonic-gate 	if ((opts->master = cpc_setgrp_new(cpc, smt)) == NULL) {
1660Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: out of heap\n"),
1670Sstevel@tonic-gate 		    opts->pgmname);
1680Sstevel@tonic-gate 		return (1);
1690Sstevel@tonic-gate 	}
1700Sstevel@tonic-gate 
171*10265SKrishnendu.Sadhukhan@Sun.COM 	while ((c = getopt(argc, argv, "Dc:hntT:sp:")) != EOF && errcnt == 0)
1720Sstevel@tonic-gate 		switch (c) {
1730Sstevel@tonic-gate 		case 'D':			/* enable debugging */
1740Sstevel@tonic-gate 			opts->debug++;
1750Sstevel@tonic-gate 			break;
1760Sstevel@tonic-gate 		case 'c':			/* specify statistics */
1770Sstevel@tonic-gate 			if ((sgrp = cpc_setgrp_newset(opts->master,
1780Sstevel@tonic-gate 			    optarg, &errcnt)) != NULL)
1790Sstevel@tonic-gate 				opts->master = sgrp;
1800Sstevel@tonic-gate 			break;
1810Sstevel@tonic-gate 		case 'n':			/* no titles */
1820Sstevel@tonic-gate 			opts->dotitle = 0;
1830Sstevel@tonic-gate 			break;
1840Sstevel@tonic-gate 		case 'p':			/* periodic behavior */
1850Sstevel@tonic-gate 			opts->doperiod = 1;
1860Sstevel@tonic-gate 			period = strtod(optarg, &endp);
1870Sstevel@tonic-gate 			if (*endp != '\0') {
1880Sstevel@tonic-gate 				(void) fprintf(stderr, gettext("%s: invalid "
1890Sstevel@tonic-gate 				    "parameter \"%s\"\n"), opts->pgmname,
1900Sstevel@tonic-gate 				    optarg);
1910Sstevel@tonic-gate 				errcnt++;
1920Sstevel@tonic-gate 			}
1930Sstevel@tonic-gate 			break;
1940Sstevel@tonic-gate 		case 's':			/* run soaker thread */
1950Sstevel@tonic-gate 			opts->dosoaker = 1;
1960Sstevel@tonic-gate 			break;
1970Sstevel@tonic-gate 		case 't':			/* print %tick */
1980Sstevel@tonic-gate 			opts->dotick = 1;
1990Sstevel@tonic-gate 			break;
200*10265SKrishnendu.Sadhukhan@Sun.COM 		case 'T':
201*10265SKrishnendu.Sadhukhan@Sun.COM 			if (optarg) {
202*10265SKrishnendu.Sadhukhan@Sun.COM 				if (*optarg == 'u')
203*10265SKrishnendu.Sadhukhan@Sun.COM 					timestamp_fmt = UDATE;
204*10265SKrishnendu.Sadhukhan@Sun.COM 				else if (*optarg == 'd')
205*10265SKrishnendu.Sadhukhan@Sun.COM 					timestamp_fmt = DDATE;
206*10265SKrishnendu.Sadhukhan@Sun.COM 				else
207*10265SKrishnendu.Sadhukhan@Sun.COM 					errcnt++;
208*10265SKrishnendu.Sadhukhan@Sun.COM 			} else {
209*10265SKrishnendu.Sadhukhan@Sun.COM 				errcnt++;
210*10265SKrishnendu.Sadhukhan@Sun.COM 			}
211*10265SKrishnendu.Sadhukhan@Sun.COM 			break;
2120Sstevel@tonic-gate 		case 'h':			/* help */
2130Sstevel@tonic-gate 			opts->dohelp = 1;
2140Sstevel@tonic-gate 			break;
2150Sstevel@tonic-gate 		case '?':
2160Sstevel@tonic-gate 		default:
2170Sstevel@tonic-gate 			errcnt++;
2180Sstevel@tonic-gate 			break;
2190Sstevel@tonic-gate 		}
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate 	switch (argc - optind) {
2220Sstevel@tonic-gate 	case 0:
2230Sstevel@tonic-gate 		break;
2240Sstevel@tonic-gate 	case 2:
2250Sstevel@tonic-gate 		opts->nsamples = strtol(argv[optind + 1], &endp, 10);
2260Sstevel@tonic-gate 		if (*endp != '\0') {
2270Sstevel@tonic-gate 			(void) fprintf(stderr,
2280Sstevel@tonic-gate 			    gettext("%s: invalid argument \"%s\"\n"),
2290Sstevel@tonic-gate 			    opts->pgmname, argv[optind + 1]);
2300Sstevel@tonic-gate 			errcnt++;
2310Sstevel@tonic-gate 			break;
2320Sstevel@tonic-gate 		}
2330Sstevel@tonic-gate 		/*FALLTHROUGH*/
2340Sstevel@tonic-gate 	case 1:
2350Sstevel@tonic-gate 		opts->mseconds = (uint_t)(strtod(argv[optind], &endp) * 1000.0);
2360Sstevel@tonic-gate 		if (*endp != '\0') {
2370Sstevel@tonic-gate 			(void) fprintf(stderr,
2380Sstevel@tonic-gate 			    gettext("%s: invalid argument \"%s\"\n"),
2390Sstevel@tonic-gate 			    opts->pgmname, argv[optind]);
2400Sstevel@tonic-gate 			errcnt++;
2410Sstevel@tonic-gate 		}
2420Sstevel@tonic-gate 		break;
2430Sstevel@tonic-gate 	default:
2440Sstevel@tonic-gate 		errcnt++;
2450Sstevel@tonic-gate 		break;
2460Sstevel@tonic-gate 	}
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate 	if (opts->nsamples == 0 || opts->mseconds == 0)
2490Sstevel@tonic-gate 		errcnt++;
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 	if (errcnt != 0 || opts->dohelp ||
2520Sstevel@tonic-gate 	    (opts->nsets = cpc_setgrp_numsets(opts->master)) == 0) {
2530Sstevel@tonic-gate 		(void) fprintf(opts->dohelp ? stdout : stderr, gettext(
2540Sstevel@tonic-gate 		    "Usage:\n\t%s [-c events] [-p period] [-nstD] "
255*10265SKrishnendu.Sadhukhan@Sun.COM 		    "[-T d|u] [interval [count]]\n\n"
2560Sstevel@tonic-gate 		    "\t-c events specify processor events to be monitored\n"
2570Sstevel@tonic-gate 		    "\t-n\t  suppress titles\n"
2580Sstevel@tonic-gate 		    "\t-p period cycle through event list periodically\n"
2590Sstevel@tonic-gate 		    "\t-s\t  run user soaker thread for system-only events\n"
2600Sstevel@tonic-gate 		    "\t-t\t  include %s register\n"
261*10265SKrishnendu.Sadhukhan@Sun.COM 		    "\t-T d|u\t  Display a timestamp in date (d) or unix "
262*10265SKrishnendu.Sadhukhan@Sun.COM 		    "time_t (u)\n"
2630Sstevel@tonic-gate 		    "\t-D\t  enable debug mode\n"
2640Sstevel@tonic-gate 		    "\t-h\t  print extended usage information\n\n"
2650Sstevel@tonic-gate 		    "\tUse cputrack(1) to monitor per-process statistics.\n"),
2660Sstevel@tonic-gate 		    opts->pgmname, CPC_TICKREG_NAME);
2670Sstevel@tonic-gate 		if (opts->dohelp) {
2680Sstevel@tonic-gate 			(void) putchar('\n');
2690Sstevel@tonic-gate 			(void) capabilities(cpc, stdout);
2700Sstevel@tonic-gate 			exit(0);
2710Sstevel@tonic-gate 		}
2720Sstevel@tonic-gate 		exit(2);
2730Sstevel@tonic-gate 	}
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate 	/*
2760Sstevel@tonic-gate 	 * If the user requested periodic behavior, calculate the rest time
2770Sstevel@tonic-gate 	 * between cycles.
2780Sstevel@tonic-gate 	 */
2790Sstevel@tonic-gate 	if (opts->doperiod) {
2800Sstevel@tonic-gate 		opts->mseconds_rest = (uint_t)((period * 1000.0) -
2810Sstevel@tonic-gate 		    (opts->mseconds * opts->nsets));
2820Sstevel@tonic-gate 		if ((int)opts->mseconds_rest < 0)
2830Sstevel@tonic-gate 			opts->mseconds_rest = 0;
2840Sstevel@tonic-gate 		if (opts->nsamples != UINT_MAX)
2850Sstevel@tonic-gate 			opts->nsamples *= opts->nsets;
2860Sstevel@tonic-gate 	}
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	cpc_setgrp_reset(opts->master);
2890Sstevel@tonic-gate 	(void) setvbuf(stdout, NULL, _IOLBF, 0);
2900Sstevel@tonic-gate 
2910Sstevel@tonic-gate 	/*
2920Sstevel@tonic-gate 	 * If no system-mode only sets were created, no soaker threads will be
2930Sstevel@tonic-gate 	 * needed.
2940Sstevel@tonic-gate 	 */
2950Sstevel@tonic-gate 	if (opts->dosoaker == 1 && cpc_setgrp_has_sysonly(opts->master) == 0)
2960Sstevel@tonic-gate 		opts->dosoaker = 0;
2970Sstevel@tonic-gate 
2980Sstevel@tonic-gate 	ret = cpustat();
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate 	(void) cpc_close(cpc);
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	return (ret);
3030Sstevel@tonic-gate }
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate static void
print_title(cpc_setgrp_t * sgrp)3060Sstevel@tonic-gate print_title(cpc_setgrp_t *sgrp)
3070Sstevel@tonic-gate {
3080Sstevel@tonic-gate 	(void) printf("%7s %3s %5s ", "time", "cpu", "event");
3090Sstevel@tonic-gate 	if (opts->dotick)
3100Sstevel@tonic-gate 		(void) printf("%9s ", CPC_TICKREG_NAME);
3110Sstevel@tonic-gate 	(void) printf("%s\n", cpc_setgrp_gethdr(sgrp));
3120Sstevel@tonic-gate }
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate static void
print_sample(processorid_t cpuid,cpc_buf_t * buf,int nreq,const char * setname,int sibling)3150Sstevel@tonic-gate print_sample(processorid_t cpuid, cpc_buf_t *buf, int nreq, const char *setname,
3160Sstevel@tonic-gate     int sibling)
3170Sstevel@tonic-gate {
3180Sstevel@tonic-gate 	char		line[1024];
3190Sstevel@tonic-gate 	int		ccnt;
3200Sstevel@tonic-gate 	int		i;
3210Sstevel@tonic-gate 	uint64_t	val;
3220Sstevel@tonic-gate 	uint64_t	tick;
3230Sstevel@tonic-gate 	hrtime_t	hrtime;
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	hrtime = cpc_buf_hrtime(cpc, buf);
3260Sstevel@tonic-gate 	tick = cpc_buf_tick(cpc, buf);
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 	ccnt = snprintf(line, sizeof (line), "%7.3f %3d %5s ",
3290Sstevel@tonic-gate 	    mstimestamp(hrtime), (int)cpuid, "tick");
3300Sstevel@tonic-gate 	if (opts->dotick)
3310Sstevel@tonic-gate 		ccnt += snprintf(line + ccnt, sizeof (line) - ccnt,
3320Sstevel@tonic-gate 		    "%9" PRId64 " ", tick);
3330Sstevel@tonic-gate 	for (i = 0; i < nreq; i++) {
3340Sstevel@tonic-gate 		(void) cpc_buf_get(cpc, buf, i, &val);
3350Sstevel@tonic-gate 		ccnt += snprintf(line + ccnt, sizeof (line) - ccnt,
3360Sstevel@tonic-gate 		    "%9" PRId64 " ", val);
3370Sstevel@tonic-gate 	}
3380Sstevel@tonic-gate 	if (opts->nsets > 1)
3390Sstevel@tonic-gate 		ccnt += snprintf(line + ccnt, sizeof (line) - ccnt,
3400Sstevel@tonic-gate 		    " # %s\n", setname);
3410Sstevel@tonic-gate 	else
3420Sstevel@tonic-gate 		ccnt += snprintf(line + ccnt, sizeof (line) - ccnt, "\n");
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 	if (sibling) {
3450Sstevel@tonic-gate 		/*
3460Sstevel@tonic-gate 		 * This sample is being printed for a "sibling" CPU -- that is,
3470Sstevel@tonic-gate 		 * a CPU which does not have its own CPC set bound. It is being
3480Sstevel@tonic-gate 		 * measured via a set bound to another CPU sharing its physical
3490Sstevel@tonic-gate 		 * processor.
3500Sstevel@tonic-gate 		 */
3510Sstevel@tonic-gate 		int designee = chip_designees[gstate[cpuid].chip_id];
3520Sstevel@tonic-gate 		char *p;
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate 		if ((p = strrchr(line, '#')) == NULL)
3550Sstevel@tonic-gate 			p = strrchr(line, '\n');
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 		if (p != NULL) {
3580Sstevel@tonic-gate 			*p = '\0';
3590Sstevel@tonic-gate 			ccnt = strlen(line);
3600Sstevel@tonic-gate 			ccnt += snprintf(line + ccnt, sizeof (line) - ccnt,
3610Sstevel@tonic-gate 			    "# counter shared with CPU %d\n", designee);
3620Sstevel@tonic-gate 		}
3630Sstevel@tonic-gate 	}
3640Sstevel@tonic-gate 
365*10265SKrishnendu.Sadhukhan@Sun.COM 	if (timestamp_fmt != NODATE)
366*10265SKrishnendu.Sadhukhan@Sun.COM 		print_timestamp(timestamp_fmt);
3670Sstevel@tonic-gate 	if (ccnt > sizeof (line))
3680Sstevel@tonic-gate 		ccnt = sizeof (line);
3690Sstevel@tonic-gate 	if (ccnt > 0)
3700Sstevel@tonic-gate 		(void) write(1, line, ccnt);
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate 	/*
3730Sstevel@tonic-gate 	 * If this CPU is the chip designee for any other CPUs, print a line for
3740Sstevel@tonic-gate 	 * them here.
3750Sstevel@tonic-gate 	 */
3760Sstevel@tonic-gate 	if (smt && (sibling == 0)) {
3770Sstevel@tonic-gate 		for (i = 0; i < ncpus; i++) {
3780Sstevel@tonic-gate 			if ((i != cpuid) && (gstate[i].cpuid != -1) &&
3790Sstevel@tonic-gate 			    (chip_designees[gstate[i].chip_id] == cpuid))
3800Sstevel@tonic-gate 				print_sample(i, buf, nreq, setname, 1);
3810Sstevel@tonic-gate 		}
3820Sstevel@tonic-gate 	}
3830Sstevel@tonic-gate }
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate static void
print_total(int ncpus,cpc_buf_t * buf,int nreq,const char * setname)3860Sstevel@tonic-gate print_total(int ncpus, cpc_buf_t *buf, int nreq, const char *setname)
3870Sstevel@tonic-gate {
3880Sstevel@tonic-gate 	int		i;
3890Sstevel@tonic-gate 	uint64_t	val;
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate 	(void) printf("%7.3f %3d %5s ", mstimestamp(cpc_buf_hrtime(cpc, buf)),
3920Sstevel@tonic-gate 	    ncpus, "total");
3930Sstevel@tonic-gate 	if (opts->dotick)
3940Sstevel@tonic-gate 		(void) printf("%9" PRId64 " ", cpc_buf_tick(cpc, buf));
3950Sstevel@tonic-gate 	for (i = 0; i < nreq; i++) {
3960Sstevel@tonic-gate 		(void) cpc_buf_get(cpc, buf, i, &val);
3970Sstevel@tonic-gate 		(void) printf("%9" PRId64 " ", val);
3980Sstevel@tonic-gate 	}
3990Sstevel@tonic-gate 	if (opts->nsets > 1)
4000Sstevel@tonic-gate 		(void) printf(" # %s", setname);
4010Sstevel@tonic-gate 	(void) fputc('\n', stdout);
4020Sstevel@tonic-gate }
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate #define	NSECS_PER_MSEC	1000000ll
4050Sstevel@tonic-gate #define	NSECS_PER_SEC	1000000000ll
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate static void *
gtick(void * arg)4080Sstevel@tonic-gate gtick(void *arg)
4090Sstevel@tonic-gate {
4100Sstevel@tonic-gate 	struct tstate		*state = arg;
4110Sstevel@tonic-gate 	char			*errstr;
4120Sstevel@tonic-gate 	uint_t			nsamples;
4130Sstevel@tonic-gate 	uint_t			sample_cnt = 1;
4140Sstevel@tonic-gate 	hrtime_t		ht, htdelta, restdelta;
4150Sstevel@tonic-gate 	cpc_setgrp_t		*sgrp = state->sgrp;
4160Sstevel@tonic-gate 	cpc_set_t		*this = cpc_setgrp_getset(sgrp);
4170Sstevel@tonic-gate 	const char		*name = cpc_setgrp_getname(sgrp);
4180Sstevel@tonic-gate 	cpc_buf_t		**data1, **data2, **scratch;
4190Sstevel@tonic-gate 	cpc_buf_t		*tmp;
4200Sstevel@tonic-gate 	int			nreqs;
4210Sstevel@tonic-gate 	thread_t		tid;
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate 	htdelta = NSECS_PER_MSEC * opts->mseconds;
4240Sstevel@tonic-gate 	restdelta = NSECS_PER_MSEC * opts->mseconds_rest;
4250Sstevel@tonic-gate 	ht = gethrtime();
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	/*
4280Sstevel@tonic-gate 	 * If this CPU is SMT, we run one gtick() thread per _physical_ CPU,
4290Sstevel@tonic-gate 	 * instead of per cpu_t. The following check returns if it detects that
4300Sstevel@tonic-gate 	 * this cpu_t has not been designated to do the counting for this
4310Sstevel@tonic-gate 	 * physical CPU.
4320Sstevel@tonic-gate 	 */
4330Sstevel@tonic-gate 	if (smt && chip_designees[state->chip_id] != state->cpuid)
4340Sstevel@tonic-gate 		return (NULL);
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	/*
4370Sstevel@tonic-gate 	 * If we need to run a soaker thread on this CPU, start it here.
4380Sstevel@tonic-gate 	 */
4390Sstevel@tonic-gate 	if (opts->dosoaker) {
4400Sstevel@tonic-gate 		if (cond_init(&state->soak_cv, USYNC_THREAD, NULL) != 0)
4410Sstevel@tonic-gate 			goto bad;
4420Sstevel@tonic-gate 		if (mutex_init(&state->soak_lock, USYNC_THREAD,
4430Sstevel@tonic-gate 		    NULL) != 0)
4440Sstevel@tonic-gate 			goto bad;
4450Sstevel@tonic-gate 		(void) mutex_lock(&state->soak_lock);
4460Sstevel@tonic-gate 		state->soak_state = SOAK_PAUSE;
4470Sstevel@tonic-gate 		if (thr_create(NULL, 0, soaker, state, NULL, &tid) != 0)
4480Sstevel@tonic-gate 			goto bad;
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 		while (state->soak_state == SOAK_PAUSE)
4510Sstevel@tonic-gate 			(void) cond_wait(&state->soak_cv,
4520Sstevel@tonic-gate 			    &state->soak_lock);
4530Sstevel@tonic-gate 		(void) mutex_unlock(&state->soak_lock);
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 		/*
4560Sstevel@tonic-gate 		 * If the soaker needs to pause for the first set, stop it now.
4570Sstevel@tonic-gate 		 */
4580Sstevel@tonic-gate 		if (cpc_setgrp_sysonly(sgrp) == 0) {
4590Sstevel@tonic-gate 			(void) mutex_lock(&state->soak_lock);
4600Sstevel@tonic-gate 			state->soak_state = SOAK_PAUSE;
4610Sstevel@tonic-gate 			(void) mutex_unlock(&state->soak_lock);
4620Sstevel@tonic-gate 		}
4630Sstevel@tonic-gate 	}
4640Sstevel@tonic-gate 	if (cpc_bind_cpu(cpc, state->cpuid, this, 0) == -1)
4650Sstevel@tonic-gate 		goto bad;
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate 	for (nsamples = opts->nsamples; nsamples; nsamples--, sample_cnt++) {
4680Sstevel@tonic-gate 		hrtime_t htnow;
4690Sstevel@tonic-gate 		struct timespec ts;
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 		nreqs = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch);
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 		ht += htdelta;
4740Sstevel@tonic-gate 		htnow = gethrtime();
4750Sstevel@tonic-gate 		if (ht <= htnow)
4760Sstevel@tonic-gate 			continue;
4770Sstevel@tonic-gate 		ts.tv_sec = (time_t)((ht - htnow) / NSECS_PER_SEC);
4780Sstevel@tonic-gate 		ts.tv_nsec = (suseconds_t)((ht - htnow) % NSECS_PER_SEC);
4790Sstevel@tonic-gate 
4800Sstevel@tonic-gate 		(void) nanosleep(&ts, NULL);
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 		if (opts->nsets == 1) {
4830Sstevel@tonic-gate 			/*
4840Sstevel@tonic-gate 			 * If we're dealing with one set, buffer usage is:
4850Sstevel@tonic-gate 			 *
4860Sstevel@tonic-gate 			 * data1 = most recent data snapshot
4870Sstevel@tonic-gate 			 * data2 = previous data snapshot
4880Sstevel@tonic-gate 			 * scratch = used for diffing data1 and data2
4890Sstevel@tonic-gate 			 *
4900Sstevel@tonic-gate 			 * Save the snapshot from the previous sample in data2
4910Sstevel@tonic-gate 			 * before putting the current sample in data1.
4920Sstevel@tonic-gate 			 */
4930Sstevel@tonic-gate 			tmp = *data1;
4940Sstevel@tonic-gate 			*data1 = *data2;
4950Sstevel@tonic-gate 			*data2 = tmp;
4960Sstevel@tonic-gate 			if (cpc_set_sample(cpc, this, *data1) != 0)
4970Sstevel@tonic-gate 				goto bad;
4980Sstevel@tonic-gate 			cpc_buf_sub(cpc, *scratch, *data1, *data2);
4990Sstevel@tonic-gate 
5000Sstevel@tonic-gate 			print_sample(state->cpuid, *scratch, nreqs, name, 0);
5010Sstevel@tonic-gate 		} else {
5020Sstevel@tonic-gate 			/*
5030Sstevel@tonic-gate 			 * More than one set is in use (multiple -c options
5040Sstevel@tonic-gate 			 * given). Buffer usage in this case is:
5050Sstevel@tonic-gate 			 *
5060Sstevel@tonic-gate 			 * data1 = total counts for this set since program began
5070Sstevel@tonic-gate 			 * data2 = unused
5080Sstevel@tonic-gate 			 * scratch = most recent data snapshot
5090Sstevel@tonic-gate 			 */
5100Sstevel@tonic-gate 			name = cpc_setgrp_getname(sgrp);
5110Sstevel@tonic-gate 			nreqs = cpc_setgrp_getbufs(sgrp, &data1, &data2,
5120Sstevel@tonic-gate 			    &scratch);
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate 			if (cpc_set_sample(cpc, this, *scratch) != 0)
5150Sstevel@tonic-gate 				goto bad;
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate 			cpc_buf_add(cpc, *data1, *data1, *scratch);
5180Sstevel@tonic-gate 
5190Sstevel@tonic-gate 			if (cpc_unbind(cpc, this) != 0)
5200Sstevel@tonic-gate 				(void) fprintf(stderr, gettext("%s: error "
5210Sstevel@tonic-gate 				    "unbinding on cpu %d - %s\n"),
5220Sstevel@tonic-gate 				    opts->pgmname, state->cpuid,
5230Sstevel@tonic-gate 				    strerror(errno));
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 			this = cpc_setgrp_nextset(sgrp);
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 			print_sample(state->cpuid, *scratch, nreqs, name, 0);
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate 			/*
5300Sstevel@tonic-gate 			 * If periodic behavior was requested, rest here.
5310Sstevel@tonic-gate 			 */
5320Sstevel@tonic-gate 			if (opts->doperiod && opts->mseconds_rest > 0 &&
533*10265SKrishnendu.Sadhukhan@Sun.COM 			    (sample_cnt % opts->nsets) == 0) {
5340Sstevel@tonic-gate 				/*
5350Sstevel@tonic-gate 				 * Stop the soaker while the tool rests.
5360Sstevel@tonic-gate 				 */
5370Sstevel@tonic-gate 				if (opts->dosoaker) {
5380Sstevel@tonic-gate 					(void) mutex_lock(&state->soak_lock);
5390Sstevel@tonic-gate 					if (state->soak_state == SOAK_RUN)
5400Sstevel@tonic-gate 						state->soak_state = SOAK_PAUSE;
5410Sstevel@tonic-gate 					(void) mutex_unlock(&state->soak_lock);
5420Sstevel@tonic-gate 				}
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 				htnow = gethrtime();
5450Sstevel@tonic-gate 				ht += restdelta;
5460Sstevel@tonic-gate 				ts.tv_sec = (time_t)((ht - htnow) /
5470Sstevel@tonic-gate 				    NSECS_PER_SEC);
5480Sstevel@tonic-gate 				ts.tv_nsec = (suseconds_t)((ht - htnow) %
5490Sstevel@tonic-gate 				    NSECS_PER_SEC);
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate 				(void) nanosleep(&ts, NULL);
5520Sstevel@tonic-gate 			}
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 			/*
5550Sstevel@tonic-gate 			 * Start or stop the soaker if needed.
5560Sstevel@tonic-gate 			 */
5570Sstevel@tonic-gate 			if (opts->dosoaker) {
5580Sstevel@tonic-gate 				(void) mutex_lock(&state->soak_lock);
5590Sstevel@tonic-gate 				if (cpc_setgrp_sysonly(sgrp) &&
5600Sstevel@tonic-gate 				    state->soak_state == SOAK_PAUSE) {
5610Sstevel@tonic-gate 					/*
5620Sstevel@tonic-gate 					 * Soaker is paused but the next set is
5630Sstevel@tonic-gate 					 * sysonly: start the soaker.
5640Sstevel@tonic-gate 					 */
5650Sstevel@tonic-gate 					state->soak_state = SOAK_RUN;
5660Sstevel@tonic-gate 					(void) cond_signal(&state->soak_cv);
5670Sstevel@tonic-gate 				} else if (cpc_setgrp_sysonly(sgrp) == 0 &&
5680Sstevel@tonic-gate 				    state->soak_state == SOAK_RUN)
5690Sstevel@tonic-gate 					/*
5700Sstevel@tonic-gate 					 * Soaker is running but the next set
5710Sstevel@tonic-gate 					 * counts user events: stop the soaker.
5720Sstevel@tonic-gate 					 */
5730Sstevel@tonic-gate 					state->soak_state = SOAK_PAUSE;
5740Sstevel@tonic-gate 				(void) mutex_unlock(&state->soak_lock);
5750Sstevel@tonic-gate 			}
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 			if (cpc_bind_cpu(cpc, state->cpuid, this, 0) != 0)
5780Sstevel@tonic-gate 				goto bad;
5790Sstevel@tonic-gate 		}
5800Sstevel@tonic-gate 	}
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 	if (cpc_unbind(cpc, this) != 0)
5830Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: error unbinding on"
5840Sstevel@tonic-gate 		    " cpu %d - %s\n"), opts->pgmname,
5850Sstevel@tonic-gate 		    state->cpuid, strerror(errno));
5860Sstevel@tonic-gate 
5870Sstevel@tonic-gate 	/*
5880Sstevel@tonic-gate 	 * We're done, so stop the soaker if needed.
5890Sstevel@tonic-gate 	 */
5900Sstevel@tonic-gate 	if (opts->dosoaker) {
5910Sstevel@tonic-gate 		(void) mutex_lock(&state->soak_lock);
5920Sstevel@tonic-gate 		if (state->soak_state == SOAK_RUN)
5930Sstevel@tonic-gate 			state->soak_state = SOAK_PAUSE;
5940Sstevel@tonic-gate 		(void) mutex_unlock(&state->soak_lock);
5950Sstevel@tonic-gate 	}
5960Sstevel@tonic-gate 
5970Sstevel@tonic-gate 	return (NULL);
5980Sstevel@tonic-gate bad:
5990Sstevel@tonic-gate 	state->status = 3;
6000Sstevel@tonic-gate 	errstr = strerror(errno);
6010Sstevel@tonic-gate 	(void) fprintf(stderr, gettext("%s: cpu%d - %s\n"),
6020Sstevel@tonic-gate 	    opts->pgmname, state->cpuid, errstr);
6030Sstevel@tonic-gate 	return (NULL);
6040Sstevel@tonic-gate }
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate static int
cpustat(void)6070Sstevel@tonic-gate cpustat(void)
6080Sstevel@tonic-gate {
6090Sstevel@tonic-gate 	cpc_setgrp_t	*accum;
6100Sstevel@tonic-gate 	cpc_set_t	*start;
6110Sstevel@tonic-gate 	int		c, i, retval;
6120Sstevel@tonic-gate 	int		lwps = 0;
6130Sstevel@tonic-gate 	psetid_t	mypset, cpupset;
6140Sstevel@tonic-gate 	char		*errstr;
6150Sstevel@tonic-gate 	cpc_buf_t	**data1, **data2, **scratch;
6160Sstevel@tonic-gate 	int		nreqs;
6170Sstevel@tonic-gate 	kstat_ctl_t	*kc;
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate 	ncpus = (int)sysconf(_SC_NPROCESSORS_CONF);
6200Sstevel@tonic-gate 	if ((gstate = calloc(ncpus, sizeof (*gstate))) == NULL) {
6210Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
6220Sstevel@tonic-gate 		    "%s: out of heap\n"), opts->pgmname);
6230Sstevel@tonic-gate 		return (1);
6240Sstevel@tonic-gate 	}
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 	max_chip_id = sysconf(_SC_CPUID_MAX);
6270Sstevel@tonic-gate 	if ((chip_designees = malloc(max_chip_id * sizeof (int))) == NULL) {
6280Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
629*10265SKrishnendu.Sadhukhan@Sun.COM 		    "%s: out of heap\n"), opts->pgmname);
6300Sstevel@tonic-gate 		return (1);
6310Sstevel@tonic-gate 	}
6320Sstevel@tonic-gate 	for (i = 0; i < max_chip_id; i++)
6330Sstevel@tonic-gate 		chip_designees[i] = -1;
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	if (smt) {
6360Sstevel@tonic-gate 		if ((kc = kstat_open()) == NULL) {
6370Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
638*10265SKrishnendu.Sadhukhan@Sun.COM 			    "%s: kstat_open() failed: %s\n"), opts->pgmname,
6390Sstevel@tonic-gate 			    strerror(errno));
640*10265SKrishnendu.Sadhukhan@Sun.COM 			return (1);
6410Sstevel@tonic-gate 		}
6420Sstevel@tonic-gate 	}
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	if (opts->dosoaker)
6450Sstevel@tonic-gate 		if (priocntl(0, 0, PC_GETCID, &fxinfo) == -1) {
6460Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
647*10265SKrishnendu.Sadhukhan@Sun.COM 			    "%s: couldn't get FX scheduler class: %s\n"),
6480Sstevel@tonic-gate 			    opts->pgmname, strerror(errno));
6490Sstevel@tonic-gate 			return (1);
6500Sstevel@tonic-gate 		}
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate 	/*
6530Sstevel@tonic-gate 	 * Only include processors that are participating in the system
6540Sstevel@tonic-gate 	 */
6550Sstevel@tonic-gate 	for (c = 0, i = 0; i < ncpus; c++) {
6560Sstevel@tonic-gate 		switch (p_online(c, P_STATUS)) {
6570Sstevel@tonic-gate 		case P_ONLINE:
6580Sstevel@tonic-gate 		case P_NOINTR:
6590Sstevel@tonic-gate 			if (smt) {
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate 				gstate[i].chip_id = get_chipid(kc, c);
6620Sstevel@tonic-gate 				if (gstate[i].chip_id != -1 &&
6630Sstevel@tonic-gate 				    chip_designees[gstate[i].chip_id] == -1)
6640Sstevel@tonic-gate 					chip_designees[gstate[i].chip_id] = c;
6650Sstevel@tonic-gate 			}
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate 			gstate[i++].cpuid = c;
6680Sstevel@tonic-gate 			break;
6690Sstevel@tonic-gate 		case P_OFFLINE:
6700Sstevel@tonic-gate 		case P_POWEROFF:
6710Sstevel@tonic-gate 		case P_FAULTED:
6720Sstevel@tonic-gate 		case P_SPARE:
6730Sstevel@tonic-gate 			gstate[i++].cpuid = -1;
6740Sstevel@tonic-gate 			break;
6750Sstevel@tonic-gate 		default:
6760Sstevel@tonic-gate 			gstate[i++].cpuid = -1;
6770Sstevel@tonic-gate 			(void) fprintf(stderr,
6780Sstevel@tonic-gate 			    gettext("%s: cpu%d in unknown state\n"),
6790Sstevel@tonic-gate 			    opts->pgmname, c);
6800Sstevel@tonic-gate 			break;
6810Sstevel@tonic-gate 		case -1:
6820Sstevel@tonic-gate 			break;
6830Sstevel@tonic-gate 		}
6840Sstevel@tonic-gate 	}
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 	/*
6870Sstevel@tonic-gate 	 * Examine the processor sets; if we're in one, only attempt
6880Sstevel@tonic-gate 	 * to report on the set we're in.
6890Sstevel@tonic-gate 	 */
6900Sstevel@tonic-gate 	if (pset_bind(PS_QUERY, P_PID, P_MYID, &mypset) == -1) {
6910Sstevel@tonic-gate 		errstr = strerror(errno);
6920Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: pset_bind - %s\n"),
6930Sstevel@tonic-gate 		    opts->pgmname, errstr);
6940Sstevel@tonic-gate 	} else {
6950Sstevel@tonic-gate 		for (i = 0; i < ncpus; i++) {
6960Sstevel@tonic-gate 			struct tstate *this = &gstate[i];
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate 			if (this->cpuid == -1)
6990Sstevel@tonic-gate 				continue;
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 			if (pset_assign(PS_QUERY,
7020Sstevel@tonic-gate 			    this->cpuid, &cpupset) == -1) {
7030Sstevel@tonic-gate 				errstr = strerror(errno);
7040Sstevel@tonic-gate 				(void) fprintf(stderr,
7050Sstevel@tonic-gate 				    gettext("%s: pset_assign - %s\n"),
7060Sstevel@tonic-gate 				    opts->pgmname, errstr);
7070Sstevel@tonic-gate 				continue;
7080Sstevel@tonic-gate 			}
7090Sstevel@tonic-gate 
7100Sstevel@tonic-gate 			if (mypset != cpupset)
7110Sstevel@tonic-gate 				this->cpuid = -1;
7120Sstevel@tonic-gate 		}
7130Sstevel@tonic-gate 	}
7140Sstevel@tonic-gate 
7150Sstevel@tonic-gate 	if (opts->dotitle)
7160Sstevel@tonic-gate 		print_title(opts->master);
7170Sstevel@tonic-gate 	zerotime();
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate 	for (i = 0; i < ncpus; i++) {
7200Sstevel@tonic-gate 		struct tstate *this = &gstate[i];
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate 		if (this->cpuid == -1)
7230Sstevel@tonic-gate 			continue;
7240Sstevel@tonic-gate 		this->sgrp = cpc_setgrp_clone(opts->master);
7250Sstevel@tonic-gate 		if (this->sgrp == NULL) {
7260Sstevel@tonic-gate 			this->cpuid = -1;
7270Sstevel@tonic-gate 			continue;
7280Sstevel@tonic-gate 		}
7290Sstevel@tonic-gate 		if (thr_create(NULL, 0, gtick, this,
7300Sstevel@tonic-gate 		    THR_BOUND|THR_NEW_LWP, &this->tid) == 0)
7310Sstevel@tonic-gate 			lwps++;
7320Sstevel@tonic-gate 		else {
7330Sstevel@tonic-gate 			(void) fprintf(stderr,
7340Sstevel@tonic-gate 			    gettext("%s: cannot create thread for cpu%d\n"),
7350Sstevel@tonic-gate 			    opts->pgmname, this->cpuid);
7360Sstevel@tonic-gate 			this->status = 4;
7370Sstevel@tonic-gate 		}
7380Sstevel@tonic-gate 	}
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate 	if (lwps != 0)
7410Sstevel@tonic-gate 		for (i = 0; i < ncpus; i++)
7420Sstevel@tonic-gate 			(void) thr_join(gstate[i].tid, NULL, NULL);
7430Sstevel@tonic-gate 
7440Sstevel@tonic-gate 	if ((accum = cpc_setgrp_clone(opts->master)) == NULL) {
7450Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: out of heap\n"),
7460Sstevel@tonic-gate 		    opts->pgmname);
7470Sstevel@tonic-gate 		return (1);
7480Sstevel@tonic-gate 	}
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate 	retval = 0;
7510Sstevel@tonic-gate 	for (i = 0; i < ncpus; i++) {
7520Sstevel@tonic-gate 		struct tstate *this = &gstate[i];
7530Sstevel@tonic-gate 
7540Sstevel@tonic-gate 		if (this->cpuid == -1)
7550Sstevel@tonic-gate 			continue;
7560Sstevel@tonic-gate 		cpc_setgrp_accum(accum, this->sgrp);
7570Sstevel@tonic-gate 		cpc_setgrp_free(this->sgrp);
7580Sstevel@tonic-gate 		this->sgrp = NULL;
7590Sstevel@tonic-gate 		if (this->status != 0)
7600Sstevel@tonic-gate 			retval = 1;
7610Sstevel@tonic-gate 	}
7620Sstevel@tonic-gate 
7630Sstevel@tonic-gate 	cpc_setgrp_reset(accum);
7640Sstevel@tonic-gate 	start = cpc_setgrp_getset(accum);
7650Sstevel@tonic-gate 	do {
7660Sstevel@tonic-gate 		nreqs = cpc_setgrp_getbufs(accum, &data1, &data2, &scratch);
7670Sstevel@tonic-gate 		print_total(lwps, *data1, nreqs, cpc_setgrp_getname(accum));
7680Sstevel@tonic-gate 	} while (cpc_setgrp_nextset(accum) != start);
7690Sstevel@tonic-gate 
7700Sstevel@tonic-gate 	cpc_setgrp_free(accum);
7710Sstevel@tonic-gate 	accum = NULL;
7720Sstevel@tonic-gate 
7730Sstevel@tonic-gate 	free(gstate);
7740Sstevel@tonic-gate 	return (retval);
7750Sstevel@tonic-gate }
7760Sstevel@tonic-gate 
7770Sstevel@tonic-gate static int
get_chipid(kstat_ctl_t * kc,processorid_t cpuid)7780Sstevel@tonic-gate get_chipid(kstat_ctl_t *kc, processorid_t cpuid)
7790Sstevel@tonic-gate {
7800Sstevel@tonic-gate 	kstat_t		*ksp;
7810Sstevel@tonic-gate 	kstat_named_t	*k;
7820Sstevel@tonic-gate 
7830Sstevel@tonic-gate 	if ((ksp = kstat_lookup(kc, "cpu_info", cpuid, NULL)) == NULL)
7840Sstevel@tonic-gate 		return (-1);
7850Sstevel@tonic-gate 
7860Sstevel@tonic-gate 	if (kstat_read(kc, ksp, NULL) == -1) {
7870Sstevel@tonic-gate 		(void) fprintf(stderr,
7880Sstevel@tonic-gate 		    gettext("%s: kstat_read() failed for cpu %d: %s\n"),
7890Sstevel@tonic-gate 		    opts->pgmname, cpuid, strerror(errno));
7900Sstevel@tonic-gate 		return (-1);
7910Sstevel@tonic-gate 	}
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 	if ((k = (kstat_named_t *)kstat_data_lookup(ksp, "chip_id")) == NULL) {
7940Sstevel@tonic-gate 		(void) fprintf(stderr,
7950Sstevel@tonic-gate 		    gettext("%s: chip_id not found for cpu %d: %s\n"),
7960Sstevel@tonic-gate 		    opts->pgmname, cpuid, strerror(errno));
7970Sstevel@tonic-gate 		return (-1);
7980Sstevel@tonic-gate 	}
7990Sstevel@tonic-gate 
8000Sstevel@tonic-gate 	return (k->value.i32);
8010Sstevel@tonic-gate }
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate static void *
soaker(void * arg)8040Sstevel@tonic-gate soaker(void *arg)
8050Sstevel@tonic-gate {
8060Sstevel@tonic-gate 	struct tstate	*state = arg;
8070Sstevel@tonic-gate 	pcparms_t	pcparms;
8080Sstevel@tonic-gate 	fxparms_t	*fx = (fxparms_t *)pcparms.pc_clparms;
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate 	if (processor_bind(P_LWPID, P_MYID, state->cpuid, NULL) != 0)
8110Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: couldn't bind soaker "
8120Sstevel@tonic-gate 		    "thread to cpu%d: %s\n"), opts->pgmname, state->cpuid,
8130Sstevel@tonic-gate 		    strerror(errno));
8140Sstevel@tonic-gate 
8150Sstevel@tonic-gate 	/*
8160Sstevel@tonic-gate 	 * Put the soaker thread in the fixed priority (FX) class so it runs
8170Sstevel@tonic-gate 	 * at the lowest possible global priority.
8180Sstevel@tonic-gate 	 */
8190Sstevel@tonic-gate 	pcparms.pc_cid = fxinfo.pc_cid;
8200Sstevel@tonic-gate 	fx->fx_upri = 0;
8210Sstevel@tonic-gate 	fx->fx_uprilim = 0;
8220Sstevel@tonic-gate 	fx->fx_tqsecs = fx->fx_tqnsecs = FX_TQDEF;
8230Sstevel@tonic-gate 
8240Sstevel@tonic-gate 	if (priocntl(P_LWPID, P_MYID, PC_SETPARMS, &pcparms) != 0)
8250Sstevel@tonic-gate 		(void) fprintf(stderr, gettext("%s: couldn't put soaker "
8260Sstevel@tonic-gate 		    "thread in FX sched class: %s\n"), opts->pgmname,
8270Sstevel@tonic-gate 		    strerror(errno));
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate 	/*
8300Sstevel@tonic-gate 	 * Let the parent thread know we're ready to roll.
8310Sstevel@tonic-gate 	 */
8320Sstevel@tonic-gate 	(void) mutex_lock(&state->soak_lock);
8330Sstevel@tonic-gate 	state->soak_state = SOAK_RUN;
8340Sstevel@tonic-gate 	(void) cond_signal(&state->soak_cv);
8350Sstevel@tonic-gate 	(void) mutex_unlock(&state->soak_lock);
8360Sstevel@tonic-gate 
8370Sstevel@tonic-gate 	for (;;) {
8380Sstevel@tonic-gate spin:
8390Sstevel@tonic-gate 		(void) mutex_lock(&state->soak_lock);
8400Sstevel@tonic-gate 		if (state->soak_state == SOAK_RUN) {
8410Sstevel@tonic-gate 			(void) mutex_unlock(&state->soak_lock);
8420Sstevel@tonic-gate 			goto spin;
8430Sstevel@tonic-gate 		}
8440Sstevel@tonic-gate 
8450Sstevel@tonic-gate 		while (state->soak_state == SOAK_PAUSE)
8460Sstevel@tonic-gate 			(void) cond_wait(&state->soak_cv,
8470Sstevel@tonic-gate 			    &state->soak_lock);
8480Sstevel@tonic-gate 		(void) mutex_unlock(&state->soak_lock);
8490Sstevel@tonic-gate 	}
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 	/*NOTREACHED*/
8520Sstevel@tonic-gate 	return (NULL);
8530Sstevel@tonic-gate }
854