xref: /illumos-gate/usr/src/cmd/cpc/common/cpustat.c (revision b8767451d156f585534afac0bf22721810d0dc63)
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 /*
27  * Copyright 2019 Joyent, Inc.
28  */
29 
30 #include <sys/types.h>
31 #include <sys/processor.h>
32 #include <sys/pset.h>
33 #include <sys/lwp.h>
34 #include <sys/priocntl.h>
35 #include <sys/fxpriocntl.h>
36 #include <time.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <inttypes.h>
40 #include <unistd.h>
41 #include <limits.h>
42 #include <string.h>
43 #include <strings.h>
44 #include <thread.h>
45 #include <errno.h>
46 #include <libintl.h>
47 #include <locale.h>
48 #include <kstat.h>
49 #include <synch.h>
50 #include <libcpc.h>
51 #include <sys/resource.h>
52 
53 #include "cpucmds.h"
54 #include "statcommon.h"
55 
56 static struct options {
57 	int debug;
58 	int dotitle;
59 	int dohelp;
60 	int dotick;
61 	int dosoaker;
62 	int doperiod;
63 	char *pgmname;
64 	uint_t mseconds;
65 	uint_t nsamples;
66 	uint_t nsets;
67 	uint_t mseconds_rest;
68 	cpc_setgrp_t *master;
69 } __options;
70 
71 /*
72  * States for soaker threads.
73  */
74 #define	SOAK_PAUSE	0
75 #define	SOAK_RUN	1
76 
77 struct tstate {
78 	processorid_t	cpuid;
79 	int		chip_id;
80 	cpc_setgrp_t	*sgrp;
81 	int		status;
82 	thread_t	tid;
83 	int		soak_state;
84 	mutex_t		soak_lock;
85 	cond_t		soak_cv;
86 };
87 
88 static const struct options *opts = (const struct options *)&__options;
89 
90 static cpc_t *cpc;
91 
92 struct tstate	*gstate;
93 static int	ncpus;
94 static int	max_chip_id;
95 static int	*chip_designees;    /* cpuid of CPU which counts for phs chip */
96 static int	smt = 0;	    /* If set, cpustat needs to be SMT-aware. */
97 static pcinfo_t	fxinfo = { 0, "FX", 0 }; /* FX scheduler class info */
98 
99 static uint_t timestamp_fmt = NODATE;
100 
101 /*ARGSUSED*/
102 static void
103 cpustat_errfn(const char *fn, int subcode, const char *fmt, va_list ap)
104 {
105 	(void) fprintf(stderr, "%s: ", opts->pgmname);
106 	if (opts->debug)
107 		(void) fprintf(stderr, "%s: ", fn);
108 	(void) vfprintf(stderr, fmt, ap);
109 }
110 
111 static int cpustat(void);
112 static int get_chipid(kstat_ctl_t *kc, processorid_t cpuid);
113 static void *soaker(void *arg);
114 
115 
116 #if !defined(TEXT_DOMAIN)
117 #define	TEXT_DOMAIN	"SYS_TEST"
118 #endif
119 
120 int
121 main(int argc, char *argv[])
122 {
123 	struct options	*opts = &__options;
124 	int		c, errcnt = 0, ret;
125 	cpc_setgrp_t	*sgrp;
126 	char		*errstr;
127 	double		period;
128 	char		*endp;
129 	struct rlimit	rl;
130 
131 	(void) setlocale(LC_ALL, "");
132 	(void) textdomain(TEXT_DOMAIN);
133 
134 	if ((opts->pgmname = strrchr(argv[0], '/')) == NULL)
135 		opts->pgmname = argv[0];
136 	else
137 		opts->pgmname++;
138 
139 	/* Make sure we can open enough files */
140 	rl.rlim_max = rl.rlim_cur = RLIM_INFINITY;
141 	if (setrlimit(RLIMIT_NOFILE, &rl) != 0) {
142 		errstr = strerror(errno);
143 		(void) fprintf(stderr,
144 		    gettext("%s: setrlimit failed - %s\n"),
145 		    opts->pgmname, errstr);
146 	}
147 
148 	if ((cpc = cpc_open(CPC_VER_CURRENT)) == NULL) {
149 		errstr = strerror(errno);
150 		(void) fprintf(stderr, gettext("%s: cannot access performance "
151 		    "counters - %s\n"), opts->pgmname, errstr);
152 		return (1);
153 	}
154 
155 	(void) cpc_seterrhndlr(cpc, cpustat_errfn);
156 	strtoset_errfn = cpustat_errfn;
157 
158 	/*
159 	 * Check to see if cpustat needs to be SMT-aware.
160 	 */
161 	smt = smt_limited_cpc_hw(cpc);
162 
163 	/*
164 	 * Establish some defaults
165 	 */
166 	opts->mseconds = 5000;
167 	opts->nsamples = UINT_MAX;
168 	opts->dotitle = 1;
169 	if ((opts->master = cpc_setgrp_new(cpc, smt)) == NULL) {
170 		(void) fprintf(stderr, gettext("%s: out of heap\n"),
171 		    opts->pgmname);
172 		return (1);
173 	}
174 
175 	while ((c = getopt(argc, argv, "Dc:hntT:sp:")) != EOF && errcnt == 0)
176 		switch (c) {
177 		case 'D':			/* enable debugging */
178 			opts->debug++;
179 			break;
180 		case 'c':			/* specify statistics */
181 			if ((sgrp = cpc_setgrp_newset(opts->master,
182 			    optarg, &errcnt)) != NULL)
183 				opts->master = sgrp;
184 			break;
185 		case 'n':			/* no titles */
186 			opts->dotitle = 0;
187 			break;
188 		case 'p':			/* periodic behavior */
189 			opts->doperiod = 1;
190 			period = strtod(optarg, &endp);
191 			if (*endp != '\0') {
192 				(void) fprintf(stderr, gettext("%s: invalid "
193 				    "parameter \"%s\"\n"), opts->pgmname,
194 				    optarg);
195 				errcnt++;
196 			}
197 			break;
198 		case 's':			/* run soaker thread */
199 			opts->dosoaker = 1;
200 			break;
201 		case 't':			/* print %tick */
202 			opts->dotick = 1;
203 			break;
204 		case 'T':
205 			if (optarg) {
206 				if (*optarg == 'u')
207 					timestamp_fmt = UDATE;
208 				else if (*optarg == 'd')
209 					timestamp_fmt = DDATE;
210 				else
211 					errcnt++;
212 			} else {
213 				errcnt++;
214 			}
215 			break;
216 		case 'h':			/* help */
217 			opts->dohelp = 1;
218 			break;
219 		case '?':
220 		default:
221 			errcnt++;
222 			break;
223 		}
224 
225 	switch (argc - optind) {
226 	case 0:
227 		break;
228 	case 2:
229 		opts->nsamples = strtol(argv[optind + 1], &endp, 10);
230 		if (*endp != '\0') {
231 			(void) fprintf(stderr,
232 			    gettext("%s: invalid argument \"%s\"\n"),
233 			    opts->pgmname, argv[optind + 1]);
234 			errcnt++;
235 			break;
236 		}
237 		/*FALLTHROUGH*/
238 	case 1:
239 		opts->mseconds = (uint_t)(strtod(argv[optind], &endp) * 1000.0);
240 		if (*endp != '\0') {
241 			(void) fprintf(stderr,
242 			    gettext("%s: invalid argument \"%s\"\n"),
243 			    opts->pgmname, argv[optind]);
244 			errcnt++;
245 		}
246 		break;
247 	default:
248 		errcnt++;
249 		break;
250 	}
251 
252 	if (opts->nsamples == 0 || opts->mseconds == 0)
253 		errcnt++;
254 
255 	if (errcnt != 0 || opts->dohelp ||
256 	    (opts->nsets = cpc_setgrp_numsets(opts->master)) == 0) {
257 		(void) fprintf(opts->dohelp ? stdout : stderr, gettext(
258 		    "Usage:\n\t%s -c spec [-c spec]... [-p period] [-T u|d]\n"
259 		    "\t\t[-sntD] [interval [count]]\n\n"
260 		    "\t-c spec\t  specify processor events to be monitored\n"
261 		    "\t-n\t  suppress titles\n"
262 		    "\t-p period cycle through event list periodically\n"
263 		    "\t-s\t  run user soaker thread for system-only events\n"
264 		    "\t-t\t  include %s register\n"
265 		    "\t-T d|u\t  Display a timestamp in date (d) or unix "
266 		    "time_t (u)\n"
267 		    "\t-D\t  enable debug mode\n"
268 		    "\t-h\t  print extended usage information\n\n"
269 		    "\tUse cputrack(1) to monitor per-process statistics.\n"),
270 		    opts->pgmname, CPC_TICKREG_NAME);
271 		if (opts->dohelp) {
272 			(void) putchar('\n');
273 			(void) capabilities(cpc, stdout);
274 			exit(0);
275 		}
276 		exit(2);
277 	}
278 
279 	/*
280 	 * If the user requested periodic behavior, calculate the rest time
281 	 * between cycles.
282 	 */
283 	if (opts->doperiod) {
284 		opts->mseconds_rest = (uint_t)((period * 1000.0) -
285 		    (opts->mseconds * opts->nsets));
286 		if ((int)opts->mseconds_rest < 0)
287 			opts->mseconds_rest = 0;
288 		if (opts->nsamples != UINT_MAX)
289 			opts->nsamples *= opts->nsets;
290 	}
291 
292 	cpc_setgrp_reset(opts->master);
293 	(void) setvbuf(stdout, NULL, _IOLBF, 0);
294 
295 	/*
296 	 * By design, cpustat (regrettably) has multiple threads racing in
297 	 * write(2) to generate output.  As there are no guarantees made with
298 	 * respect to the atomicity of concurrent writes on non-O_APPEND file
299 	 * descriptors, we must set O_APPEND on stdout to assure that no output
300 	 * is lost.  If cpustat is rearchitected such that only one thread is
301 	 * generating output (which would also assure that the output is always
302 	 * in a consistent order), this code should be removed.
303 	 */
304 	if (fcntl(1, F_SETFL, fcntl(1, F_GETFL) | O_APPEND) == -1) {
305 		(void) fprintf(stderr, gettext("%s: cannot set output to be "
306 		    "append-only - %s\n"), opts->pgmname, strerror(errno));
307 		return (1);
308 	}
309 
310 	/*
311 	 * If no system-mode only sets were created, no soaker threads will be
312 	 * needed.
313 	 */
314 	if (opts->dosoaker == 1 && cpc_setgrp_has_sysonly(opts->master) == 0)
315 		opts->dosoaker = 0;
316 
317 	ret = cpustat();
318 
319 	(void) cpc_close(cpc);
320 
321 	return (ret);
322 }
323 
324 static void
325 print_title(cpc_setgrp_t *sgrp)
326 {
327 	(void) printf("%7s %3s %5s ", "time", "cpu", "event");
328 	if (opts->dotick)
329 		(void) printf("%9s ", CPC_TICKREG_NAME);
330 	(void) printf("%s\n", cpc_setgrp_gethdr(sgrp));
331 }
332 
333 static void
334 print_sample(processorid_t cpuid, cpc_buf_t *buf, int nreq, const char *setname,
335     int sibling)
336 {
337 	char		line[1024];
338 	int		ccnt;
339 	int		i;
340 	uint64_t	val;
341 	uint64_t	tick;
342 	hrtime_t	hrtime;
343 
344 	hrtime = cpc_buf_hrtime(cpc, buf);
345 	tick = cpc_buf_tick(cpc, buf);
346 
347 	ccnt = snprintf(line, sizeof (line), "%7.3f %3d %5s ",
348 	    mstimestamp(hrtime), (int)cpuid, "tick");
349 	if (opts->dotick)
350 		ccnt += snprintf(line + ccnt, sizeof (line) - ccnt,
351 		    "%9" PRId64 " ", tick);
352 	for (i = 0; i < nreq; i++) {
353 		(void) cpc_buf_get(cpc, buf, i, &val);
354 		ccnt += snprintf(line + ccnt, sizeof (line) - ccnt,
355 		    "%9" PRId64 " ", val);
356 	}
357 	if (opts->nsets > 1)
358 		ccnt += snprintf(line + ccnt, sizeof (line) - ccnt,
359 		    " # %s\n", setname);
360 	else
361 		ccnt += snprintf(line + ccnt, sizeof (line) - ccnt, "\n");
362 
363 	if (sibling) {
364 		/*
365 		 * This sample is being printed for a "sibling" CPU -- that is,
366 		 * a CPU which does not have its own CPC set bound. It is being
367 		 * measured via a set bound to another CPU sharing its physical
368 		 * processor.
369 		 */
370 		int designee = chip_designees[gstate[cpuid].chip_id];
371 		char *p;
372 
373 		if ((p = strrchr(line, '#')) == NULL)
374 			p = strrchr(line, '\n');
375 
376 		if (p != NULL) {
377 			*p = '\0';
378 			ccnt = strlen(line);
379 			ccnt += snprintf(line + ccnt, sizeof (line) - ccnt,
380 			    "# counter shared with CPU %d\n", designee);
381 		}
382 	}
383 
384 	if (timestamp_fmt != NODATE)
385 		print_timestamp(timestamp_fmt);
386 	if (ccnt > sizeof (line))
387 		ccnt = sizeof (line);
388 	if (ccnt > 0)
389 		(void) write(1, line, ccnt);
390 
391 	/*
392 	 * If this CPU is the chip designee for any other CPUs, print a line for
393 	 * them here.
394 	 */
395 	if (smt && (sibling == 0)) {
396 		for (i = 0; i < ncpus; i++) {
397 			if ((i != cpuid) && (gstate[i].cpuid != -1) &&
398 			    (chip_designees[gstate[i].chip_id] == cpuid))
399 				print_sample(i, buf, nreq, setname, 1);
400 		}
401 	}
402 }
403 
404 static void
405 print_total(int ncpus, cpc_buf_t *buf, int nreq, const char *setname)
406 {
407 	int		i;
408 	uint64_t	val;
409 
410 	(void) printf("%7.3f %3d %5s ", mstimestamp(cpc_buf_hrtime(cpc, buf)),
411 	    ncpus, "total");
412 	if (opts->dotick)
413 		(void) printf("%9" PRId64 " ", cpc_buf_tick(cpc, buf));
414 	for (i = 0; i < nreq; i++) {
415 		(void) cpc_buf_get(cpc, buf, i, &val);
416 		(void) printf("%9" PRId64 " ", val);
417 	}
418 	if (opts->nsets > 1)
419 		(void) printf(" # %s", setname);
420 	(void) fputc('\n', stdout);
421 }
422 
423 #define	NSECS_PER_MSEC	1000000ll
424 #define	NSECS_PER_SEC	1000000000ll
425 
426 static void *
427 gtick(void *arg)
428 {
429 	struct tstate		*state = arg;
430 	char			*errstr;
431 	uint_t			nsamples;
432 	uint_t			sample_cnt = 1;
433 	hrtime_t		ht, htdelta, restdelta;
434 	cpc_setgrp_t		*sgrp = state->sgrp;
435 	cpc_set_t		*this = cpc_setgrp_getset(sgrp);
436 	const char		*name = cpc_setgrp_getname(sgrp);
437 	cpc_buf_t		**data1, **data2, **scratch;
438 	cpc_buf_t		*tmp;
439 	int			nreqs;
440 	thread_t		tid;
441 
442 	htdelta = NSECS_PER_MSEC * opts->mseconds;
443 	restdelta = NSECS_PER_MSEC * opts->mseconds_rest;
444 	ht = gethrtime();
445 
446 	/*
447 	 * If this CPU is SMT, we run one gtick() thread per _physical_ CPU,
448 	 * instead of per cpu_t. The following check returns if it detects that
449 	 * this cpu_t has not been designated to do the counting for this
450 	 * physical CPU.
451 	 */
452 	if (smt && chip_designees[state->chip_id] != state->cpuid)
453 		return (NULL);
454 
455 	/*
456 	 * If we need to run a soaker thread on this CPU, start it here.
457 	 */
458 	if (opts->dosoaker) {
459 		if (cond_init(&state->soak_cv, USYNC_THREAD, NULL) != 0)
460 			goto bad;
461 		if (mutex_init(&state->soak_lock, USYNC_THREAD,
462 		    NULL) != 0)
463 			goto bad;
464 		(void) mutex_lock(&state->soak_lock);
465 		state->soak_state = SOAK_PAUSE;
466 		if (thr_create(NULL, 0, soaker, state, 0, &tid) != 0)
467 			goto bad;
468 
469 		while (state->soak_state == SOAK_PAUSE)
470 			(void) cond_wait(&state->soak_cv,
471 			    &state->soak_lock);
472 		(void) mutex_unlock(&state->soak_lock);
473 
474 		/*
475 		 * If the soaker needs to pause for the first set, stop it now.
476 		 */
477 		if (cpc_setgrp_sysonly(sgrp) == 0) {
478 			(void) mutex_lock(&state->soak_lock);
479 			state->soak_state = SOAK_PAUSE;
480 			(void) mutex_unlock(&state->soak_lock);
481 		}
482 	}
483 	if (cpc_bind_cpu(cpc, state->cpuid, this, 0) == -1)
484 		goto bad;
485 
486 	for (nsamples = opts->nsamples; nsamples; nsamples--, sample_cnt++) {
487 		hrtime_t htnow;
488 		struct timespec ts;
489 
490 		nreqs = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch);
491 
492 		ht += htdelta;
493 		htnow = gethrtime();
494 		if (ht <= htnow)
495 			continue;
496 		ts.tv_sec = (time_t)((ht - htnow) / NSECS_PER_SEC);
497 		ts.tv_nsec = (suseconds_t)((ht - htnow) % NSECS_PER_SEC);
498 
499 		(void) nanosleep(&ts, NULL);
500 
501 		if (opts->nsets == 1) {
502 			/*
503 			 * If we're dealing with one set, buffer usage is:
504 			 *
505 			 * data1 = most recent data snapshot
506 			 * data2 = previous data snapshot
507 			 * scratch = used for diffing data1 and data2
508 			 *
509 			 * Save the snapshot from the previous sample in data2
510 			 * before putting the current sample in data1.
511 			 */
512 			tmp = *data1;
513 			*data1 = *data2;
514 			*data2 = tmp;
515 			if (cpc_set_sample(cpc, this, *data1) != 0)
516 				goto bad;
517 			cpc_buf_sub(cpc, *scratch, *data1, *data2);
518 
519 			print_sample(state->cpuid, *scratch, nreqs, name, 0);
520 		} else {
521 			/*
522 			 * More than one set is in use (multiple -c options
523 			 * given). Buffer usage in this case is:
524 			 *
525 			 * data1 = total counts for this set since program began
526 			 * data2 = unused
527 			 * scratch = most recent data snapshot
528 			 */
529 			name = cpc_setgrp_getname(sgrp);
530 			nreqs = cpc_setgrp_getbufs(sgrp, &data1, &data2,
531 			    &scratch);
532 
533 			if (cpc_set_sample(cpc, this, *scratch) != 0)
534 				goto bad;
535 
536 			cpc_buf_add(cpc, *data1, *data1, *scratch);
537 
538 			if (cpc_unbind(cpc, this) != 0)
539 				(void) fprintf(stderr, gettext("%s: error "
540 				    "unbinding on cpu %d - %s\n"),
541 				    opts->pgmname, state->cpuid,
542 				    strerror(errno));
543 
544 			this = cpc_setgrp_nextset(sgrp);
545 
546 			print_sample(state->cpuid, *scratch, nreqs, name, 0);
547 
548 			/*
549 			 * If periodic behavior was requested, rest here.
550 			 */
551 			if (opts->doperiod && opts->mseconds_rest > 0 &&
552 			    (sample_cnt % opts->nsets) == 0) {
553 				/*
554 				 * Stop the soaker while the tool rests.
555 				 */
556 				if (opts->dosoaker) {
557 					(void) mutex_lock(&state->soak_lock);
558 					if (state->soak_state == SOAK_RUN)
559 						state->soak_state = SOAK_PAUSE;
560 					(void) mutex_unlock(&state->soak_lock);
561 				}
562 
563 				htnow = gethrtime();
564 				ht += restdelta;
565 				ts.tv_sec = (time_t)((ht - htnow) /
566 				    NSECS_PER_SEC);
567 				ts.tv_nsec = (suseconds_t)((ht - htnow) %
568 				    NSECS_PER_SEC);
569 
570 				(void) nanosleep(&ts, NULL);
571 			}
572 
573 			/*
574 			 * Start or stop the soaker if needed.
575 			 */
576 			if (opts->dosoaker) {
577 				(void) mutex_lock(&state->soak_lock);
578 				if (cpc_setgrp_sysonly(sgrp) &&
579 				    state->soak_state == SOAK_PAUSE) {
580 					/*
581 					 * Soaker is paused but the next set is
582 					 * sysonly: start the soaker.
583 					 */
584 					state->soak_state = SOAK_RUN;
585 					(void) cond_signal(&state->soak_cv);
586 				} else if (cpc_setgrp_sysonly(sgrp) == 0 &&
587 				    state->soak_state == SOAK_RUN)
588 					/*
589 					 * Soaker is running but the next set
590 					 * counts user events: stop the soaker.
591 					 */
592 					state->soak_state = SOAK_PAUSE;
593 				(void) mutex_unlock(&state->soak_lock);
594 			}
595 
596 			if (cpc_bind_cpu(cpc, state->cpuid, this, 0) != 0)
597 				goto bad;
598 		}
599 	}
600 
601 	if (cpc_unbind(cpc, this) != 0)
602 		(void) fprintf(stderr, gettext("%s: error unbinding on"
603 		    " cpu %d - %s\n"), opts->pgmname,
604 		    state->cpuid, strerror(errno));
605 
606 	/*
607 	 * We're done, so stop the soaker if needed.
608 	 */
609 	if (opts->dosoaker) {
610 		(void) mutex_lock(&state->soak_lock);
611 		if (state->soak_state == SOAK_RUN)
612 			state->soak_state = SOAK_PAUSE;
613 		(void) mutex_unlock(&state->soak_lock);
614 	}
615 
616 	return (NULL);
617 bad:
618 	state->status = 3;
619 	errstr = strerror(errno);
620 	(void) fprintf(stderr, gettext("%s: cpu%d - %s\n"),
621 	    opts->pgmname, state->cpuid, errstr);
622 	return (NULL);
623 }
624 
625 static int
626 cpustat(void)
627 {
628 	cpc_setgrp_t	*accum;
629 	cpc_set_t	*start;
630 	int		c, i, retval;
631 	int		lwps = 0;
632 	psetid_t	mypset, cpupset;
633 	char		*errstr;
634 	cpc_buf_t	**data1, **data2, **scratch;
635 	int		nreqs;
636 	kstat_ctl_t	*kc;
637 
638 	ncpus = (int)sysconf(_SC_NPROCESSORS_CONF);
639 	if ((gstate = calloc(ncpus, sizeof (*gstate))) == NULL) {
640 		(void) fprintf(stderr, gettext(
641 		    "%s: out of heap\n"), opts->pgmname);
642 		return (1);
643 	}
644 
645 	max_chip_id = sysconf(_SC_CPUID_MAX);
646 	if ((chip_designees = malloc(max_chip_id * sizeof (int))) == NULL) {
647 		(void) fprintf(stderr, gettext(
648 		    "%s: out of heap\n"), opts->pgmname);
649 		return (1);
650 	}
651 	for (i = 0; i < max_chip_id; i++)
652 		chip_designees[i] = -1;
653 
654 	if (smt) {
655 		if ((kc = kstat_open()) == NULL) {
656 			(void) fprintf(stderr, gettext(
657 			    "%s: kstat_open() failed: %s\n"), opts->pgmname,
658 			    strerror(errno));
659 			return (1);
660 		}
661 	}
662 
663 	if (opts->dosoaker)
664 		if (priocntl(0, 0, PC_GETCID, &fxinfo) == -1) {
665 			(void) fprintf(stderr, gettext(
666 			    "%s: couldn't get FX scheduler class: %s\n"),
667 			    opts->pgmname, strerror(errno));
668 			return (1);
669 		}
670 
671 	/*
672 	 * Only include processors that are participating in the system
673 	 */
674 	for (c = 0, i = 0; i < ncpus; c++) {
675 		switch (p_online(c, P_STATUS)) {
676 		case P_ONLINE:
677 		case P_NOINTR:
678 			if (smt) {
679 
680 				gstate[i].chip_id = get_chipid(kc, c);
681 				if (gstate[i].chip_id != -1 &&
682 				    chip_designees[gstate[i].chip_id] == -1)
683 					chip_designees[gstate[i].chip_id] = c;
684 			}
685 
686 			gstate[i++].cpuid = c;
687 			break;
688 		case P_OFFLINE:
689 		case P_POWEROFF:
690 		case P_FAULTED:
691 		case P_SPARE:
692 		case P_DISABLED:
693 			gstate[i++].cpuid = -1;
694 			break;
695 		default:
696 			gstate[i++].cpuid = -1;
697 			(void) fprintf(stderr,
698 			    gettext("%s: cpu%d in unknown state\n"),
699 			    opts->pgmname, c);
700 			break;
701 		case -1:
702 			break;
703 		}
704 	}
705 
706 	/*
707 	 * Examine the processor sets; if we're in one, only attempt
708 	 * to report on the set we're in.
709 	 */
710 	if (pset_bind(PS_QUERY, P_PID, P_MYID, &mypset) == -1) {
711 		errstr = strerror(errno);
712 		(void) fprintf(stderr, gettext("%s: pset_bind - %s\n"),
713 		    opts->pgmname, errstr);
714 	} else {
715 		for (i = 0; i < ncpus; i++) {
716 			struct tstate *this = &gstate[i];
717 
718 			if (this->cpuid == -1)
719 				continue;
720 
721 			if (pset_assign(PS_QUERY,
722 			    this->cpuid, &cpupset) == -1) {
723 				errstr = strerror(errno);
724 				(void) fprintf(stderr,
725 				    gettext("%s: pset_assign - %s\n"),
726 				    opts->pgmname, errstr);
727 				continue;
728 			}
729 
730 			if (mypset != cpupset)
731 				this->cpuid = -1;
732 		}
733 	}
734 
735 	if (opts->dotitle)
736 		print_title(opts->master);
737 	zerotime();
738 
739 	for (i = 0; i < ncpus; i++) {
740 		struct tstate *this = &gstate[i];
741 
742 		if (this->cpuid == -1)
743 			continue;
744 		this->sgrp = cpc_setgrp_clone(opts->master);
745 		if (this->sgrp == NULL) {
746 			this->cpuid = -1;
747 			continue;
748 		}
749 		if (thr_create(NULL, 0, gtick, this,
750 		    THR_BOUND|THR_NEW_LWP, &this->tid) == 0)
751 			lwps++;
752 		else {
753 			(void) fprintf(stderr,
754 			    gettext("%s: cannot create thread for cpu%d\n"),
755 			    opts->pgmname, this->cpuid);
756 			this->status = 4;
757 		}
758 	}
759 
760 	if (lwps != 0)
761 		for (i = 0; i < ncpus; i++)
762 			(void) thr_join(gstate[i].tid, NULL, NULL);
763 
764 	if ((accum = cpc_setgrp_clone(opts->master)) == NULL) {
765 		(void) fprintf(stderr, gettext("%s: out of heap\n"),
766 		    opts->pgmname);
767 		return (1);
768 	}
769 
770 	retval = 0;
771 	for (i = 0; i < ncpus; i++) {
772 		struct tstate *this = &gstate[i];
773 
774 		if (this->cpuid == -1)
775 			continue;
776 		cpc_setgrp_accum(accum, this->sgrp);
777 		cpc_setgrp_free(this->sgrp);
778 		this->sgrp = NULL;
779 		if (this->status != 0)
780 			retval = 1;
781 	}
782 
783 	cpc_setgrp_reset(accum);
784 	start = cpc_setgrp_getset(accum);
785 	do {
786 		nreqs = cpc_setgrp_getbufs(accum, &data1, &data2, &scratch);
787 		print_total(lwps, *data1, nreqs, cpc_setgrp_getname(accum));
788 	} while (cpc_setgrp_nextset(accum) != start);
789 
790 	cpc_setgrp_free(accum);
791 	accum = NULL;
792 
793 	free(gstate);
794 	return (retval);
795 }
796 
797 static int
798 get_chipid(kstat_ctl_t *kc, processorid_t cpuid)
799 {
800 	kstat_t		*ksp;
801 	kstat_named_t	*k;
802 
803 	if ((ksp = kstat_lookup(kc, "cpu_info", cpuid, NULL)) == NULL)
804 		return (-1);
805 
806 	if (kstat_read(kc, ksp, NULL) == -1) {
807 		(void) fprintf(stderr,
808 		    gettext("%s: kstat_read() failed for cpu %d: %s\n"),
809 		    opts->pgmname, cpuid, strerror(errno));
810 		return (-1);
811 	}
812 
813 	if ((k = (kstat_named_t *)kstat_data_lookup(ksp, "chip_id")) == NULL) {
814 		(void) fprintf(stderr,
815 		    gettext("%s: chip_id not found for cpu %d: %s\n"),
816 		    opts->pgmname, cpuid, strerror(errno));
817 		return (-1);
818 	}
819 
820 	return (k->value.i32);
821 }
822 
823 static void *
824 soaker(void *arg)
825 {
826 	struct tstate	*state = arg;
827 	pcparms_t	pcparms;
828 	fxparms_t	*fx = (fxparms_t *)pcparms.pc_clparms;
829 
830 	if (processor_bind(P_LWPID, P_MYID, state->cpuid, NULL) != 0)
831 		(void) fprintf(stderr, gettext("%s: couldn't bind soaker "
832 		    "thread to cpu%d: %s\n"), opts->pgmname, state->cpuid,
833 		    strerror(errno));
834 
835 	/*
836 	 * Put the soaker thread in the fixed priority (FX) class so it runs
837 	 * at the lowest possible global priority.
838 	 */
839 	pcparms.pc_cid = fxinfo.pc_cid;
840 	fx->fx_upri = 0;
841 	fx->fx_uprilim = 0;
842 	fx->fx_tqsecs = fx->fx_tqnsecs = FX_TQDEF;
843 
844 	if (priocntl(P_LWPID, P_MYID, PC_SETPARMS, &pcparms) != 0)
845 		(void) fprintf(stderr, gettext("%s: couldn't put soaker "
846 		    "thread in FX sched class: %s\n"), opts->pgmname,
847 		    strerror(errno));
848 
849 	/*
850 	 * Let the parent thread know we're ready to roll.
851 	 */
852 	(void) mutex_lock(&state->soak_lock);
853 	state->soak_state = SOAK_RUN;
854 	(void) cond_signal(&state->soak_cv);
855 	(void) mutex_unlock(&state->soak_lock);
856 
857 	for (;;) {
858 spin:
859 		(void) mutex_lock(&state->soak_lock);
860 		if (state->soak_state == SOAK_RUN) {
861 			(void) mutex_unlock(&state->soak_lock);
862 			goto spin;
863 		}
864 
865 		while (state->soak_state == SOAK_PAUSE)
866 			(void) cond_wait(&state->soak_cv,
867 			    &state->soak_lock);
868 		(void) mutex_unlock(&state->soak_lock);
869 	}
870 
871 	/*NOTREACHED*/
872 	return (NULL);
873 }
874