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