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