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