cputrack.c revision 3732:c8e62d396494
1102782Skan/*
2102782Skan * CDDL HEADER START
3102782Skan *
4102782Skan * The contents of this file are subject to the terms of the
5102782Skan * Common Development and Distribution License (the "License").
6102782Skan * You may not use this file except in compliance with the License.
7102782Skan *
8102782Skan * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9102782Skan * or http://www.opensolaris.org/os/licensing.
10102782Skan * See the License for the specific language governing permissions
11102782Skan * and limitations under the License.
12102782Skan *
13102782Skan * When distributing Covered Code, include this CDDL HEADER in each
14102782Skan * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15102782Skan * If applicable, add the following below this CDDL HEADER, with the
16102782Skan * fields enclosed by brackets "[]" replaced with your own identifying
17102782Skan * information: Portions Copyright [yyyy] [name of copyright owner]
18169691Skan *
19102782Skan * CDDL HEADER END
20102782Skan */
21102782Skan/*
22102782Skan * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23102782Skan * Use is subject to license terms.
24102782Skan */
25102782Skan
26102782Skan#pragma ident	"%Z%%M%	%I%	%E% SMI"
27102782Skan
28102782Skan#include <sys/time.h>
29102782Skan#include <stdio.h>
30132720Skan#include <stdlib.h>
31132720Skan#include <inttypes.h>
32102782Skan#include <unistd.h>
33102782Skan#include <string.h>
34102782Skan#include <strings.h>
35132720Skan#include <limits.h>
36#include <libintl.h>
37#include <locale.h>
38#include <errno.h>
39#include <kstat.h>
40#include <libcpc.h>
41
42#include "cpucmds.h"
43
44static struct options {
45	int debug;
46	int verbose;
47	int dotitle;
48	int dohelp;
49	int dotick;
50	int cpuver;
51	char *pgmname;
52	uint_t mseconds;
53	uint_t nsamples;
54	uint_t nsets;
55	cpc_setgrp_t *master;
56	int followfork;
57	int followexec;
58	pid_t pid;
59	FILE *log;
60} __options;
61
62static const struct options *opts = (const struct options *)&__options;
63
64static cpc_t *cpc;
65
66/*ARGSUSED*/
67static void
68cputrack_errfn(const char *fn, int subcode, const char *fmt, va_list ap)
69{
70	(void) fprintf(stderr, "%s: ", opts->pgmname);
71	if (opts->debug)
72		(void) fprintf(stderr, "%s: ", fn);
73	(void) vfprintf(stderr, fmt, ap);
74}
75
76static void
77cputrack_pctx_errfn(const char *fn, const char *fmt, va_list ap)
78{
79	cputrack_errfn(fn, -1, fmt, ap);
80}
81
82static int cputrack(int argc, char *argv[], int optind);
83#if defined(__i386)
84static void p4_ht_error(void);
85#endif
86
87#if !defined(TEXT_DOMAIN)
88#define	TEXT_DOMAIN	"SYS_TEST"
89#endif
90
91int
92main(int argc, char *argv[])
93{
94	struct options *opts = &__options;
95	int c, errcnt = 0;
96	int nsamples;
97	cpc_setgrp_t *sgrp;
98	char *errstr;
99	int ret;
100
101	(void) setlocale(LC_ALL, "");
102	(void) textdomain(TEXT_DOMAIN);
103
104	if ((opts->pgmname = strrchr(argv[0], '/')) == NULL)
105		opts->pgmname = argv[0];
106	else
107		opts->pgmname++;
108
109	if ((cpc = cpc_open(CPC_VER_CURRENT)) == NULL) {
110		errstr = strerror(errno);
111		(void) fprintf(stderr, gettext("%s: cannot access performance "
112		    "counter library - %s\n"), opts->pgmname, errstr);
113		return (1);
114	}
115
116	(void) cpc_seterrhndlr(cpc, cputrack_errfn);
117	strtoset_errfn = cputrack_errfn;
118
119	/*
120	 * Establish (non-zero) defaults
121	 */
122	opts->mseconds = 1000;
123	opts->dotitle = 1;
124	opts->log = stdout;
125	if ((opts->master = cpc_setgrp_new(cpc, 0)) == NULL) {
126		(void) fprintf(stderr, gettext("%s: no memory available\n"),
127		    opts->pgmname);
128		exit(1);
129	}
130
131	while ((c = getopt(argc, argv, "T:N:Defhntvo:r:c:p:")) != EOF)
132		switch (c) {
133		case 'T':			/* sample time,	seconds */
134			opts->mseconds = (uint_t)(atof(optarg) * 1000.0);
135			break;
136		case 'N':			/* number of samples */
137			nsamples = atoi(optarg);
138			if (nsamples < 0)
139				errcnt++;
140			else
141				opts->nsamples = (uint_t)nsamples;
142			break;
143		case 'D':			/* enable debugging */
144			opts->debug++;
145			break;
146		case 'f':			/* follow fork */
147			opts->followfork++;
148			break;
149		case 'e':			/* follow exec */
150			opts->followexec++;
151			break;
152		case 'n':			/* no titles */
153			opts->dotitle = 0;
154			break;
155		case 't':			/* print %tick */
156			opts->dotick = 1;
157			break;
158		case 'v':
159			opts->verbose = 1;	/* more chatty */
160			break;
161		case 'o':
162			if (optarg == NULL) {
163				errcnt++;
164				break;
165			}
166			if ((opts->log = fopen(optarg, "w")) == NULL) {
167				(void) fprintf(stderr, gettext(
168				    "%s: cannot open '%s' for writing\n"),
169				    opts->pgmname, optarg);
170				return (1);
171			}
172			break;
173		case 'c':			/* specify statistics */
174			if ((sgrp = cpc_setgrp_newset(opts->master,
175			    optarg, &errcnt)) != NULL)
176				opts->master = sgrp;
177			break;
178		case 'p':			/* grab given pid */
179			if ((opts->pid = atoi(optarg)) <= 0)
180				errcnt++;
181			break;
182		case 'h':
183			opts->dohelp = 1;
184			break;
185		case '?':
186		default:
187			errcnt++;
188			break;
189		}
190
191	if (opts->nsamples == 0)
192		opts->nsamples = UINT_MAX;
193
194	if (errcnt != 0 ||
195	    opts->dohelp ||
196	    (argc == optind && opts->pid == 0) ||
197	    (argc > optind && opts->pid != 0) ||
198	    (opts->nsets = cpc_setgrp_numsets(opts->master)) == 0) {
199		(void) fprintf(opts->dohelp ? stdout : stderr, gettext(
200		    "Usage:\n\t%s [-T secs] [-N count] [-Defhnv] [-o file]\n"
201		    "\t\t-c events [command [args] | -p pid]\n\n"
202		    "\t-T secs\t  seconds between samples, default 1\n"
203		    "\t-N count  number of samples, default unlimited\n"
204		    "\t-D\t  enable debug mode\n"
205		    "\t-e\t  follow exec(2), and execve(2)\n"
206		    "\t-f\t  follow fork(2), fork1(2), and vfork(2)\n"
207		    "\t-h\t  print extended usage information\n"
208		    "\t-n\t  suppress titles\n"
209		    "\t-t\t  include virtualized %s register\n"
210		    "\t-v\t  verbose mode\n"
211		    "\t-o file\t  write cpu statistics to this file\n"
212		    "\t-c events specify processor events to be monitored\n"
213		    "\t-p pid\t  pid of existing process to capture\n\n"
214		    "\tUse cpustat(1M) to monitor system-wide statistics.\n"),
215		    opts->pgmname, CPC_TICKREG_NAME);
216		if (opts->dohelp) {
217			(void) putchar('\n');
218			(void) capabilities(cpc, stdout);
219			exit(0);
220		}
221		exit(2);
222	}
223
224	cpc_setgrp_reset(opts->master);
225	(void) setvbuf(opts->log, NULL, _IOLBF, 0);
226	ret = cputrack(argc, argv, optind);
227	(void) cpc_close(cpc);
228	return (ret);
229}
230
231static void
232print_title(cpc_setgrp_t *sgrp)
233{
234	(void) fprintf(opts->log, "%7s ", "time");
235	if (opts->followfork)
236		(void) fprintf(opts->log, "%6s ", "pid");
237	(void) fprintf(opts->log, "%3s %10s ", "lwp", "event");
238	if (opts->dotick)
239		(void) fprintf(opts->log, "%9s ", CPC_TICKREG_NAME);
240	(void) fprintf(opts->log, "%s\n", cpc_setgrp_gethdr(sgrp));
241	(void) fflush(opts->log);
242}
243
244static void
245print_exec(float now, pid_t pid, char *name)
246{
247	if (name == NULL)
248		name = "(unknown)";
249
250	(void) fprintf(opts->log, "%7.3f ", now);
251	if (opts->followfork)
252		(void) fprintf(opts->log, "%6d ", (int)pid);
253	(void) fprintf(opts->log, "%3d %10s ", 1, "exec");
254	if (opts->dotick)
255		(void) fprintf(opts->log, "%9s ", "");
256	(void) fprintf(opts->log, "%9s %9s # '%s'\n", "", "", name);
257	(void) fflush(opts->log);
258}
259
260static void
261print_fork(float now, pid_t newpid, id_t lwpid, pid_t oldpid)
262{
263	(void) fprintf(opts->log, "%7.3f ", now);
264	if (opts->followfork)
265		(void) fprintf(opts->log, "%6d ", (int)oldpid);
266	(void) fprintf(opts->log, "%3d %10s ", (int)lwpid, "fork");
267	if (opts->dotick)
268		(void) fprintf(opts->log, "%9s ", "");
269	(void) fprintf(opts->log, "%9s %9s # %d\n", "", "", (int)newpid);
270	(void) fflush(opts->log);
271}
272
273static void
274print_sample(pid_t pid, id_t lwpid,
275    char *pevent, cpc_buf_t *buf, int nreq, const char *evname)
276{
277	uint64_t	val;
278	int		i;
279
280	(void) fprintf(opts->log, "%7.3f ",
281	    mstimestamp(cpc_buf_hrtime(cpc, buf)));
282	if (opts->followfork)
283		(void) fprintf(opts->log, "%6d ", (int)pid);
284	(void) fprintf(opts->log, "%3d %10s ", (int)lwpid, pevent);
285	if (opts->dotick)
286		(void) fprintf(opts->log, "%9" PRId64 " ",
287		    cpc_buf_tick(cpc, buf));
288	for (i = 0; i < nreq; i++) {
289		(void) cpc_buf_get(cpc, buf, i, &val);
290		(void) fprintf(opts->log, "%9" PRId64 " ", val);
291	}
292	if (opts->nsets > 1)
293		(void) fprintf(opts->log, " # %s\n", evname);
294	else
295		(void) fputc('\n', opts->log);
296}
297
298struct pstate {
299	cpc_setgrp_t *accum;
300	cpc_setgrp_t **sgrps;
301	int maxlwpid;
302};
303
304static int
305pinit_lwp(pctx_t *pctx, pid_t pid, id_t lwpid, void *arg)
306{
307	struct pstate *state = arg;
308	cpc_setgrp_t *sgrp;
309	cpc_set_t *set;
310	cpc_buf_t **data1, **data2, **scratch;
311	char *errstr;
312	int nreq;
313
314	if (state->maxlwpid < lwpid) {
315		state->sgrps = realloc(state->sgrps,
316		    lwpid * sizeof (state->sgrps));
317		if (state->sgrps == NULL) {
318			(void) fprintf(stderr, gettext(
319			    "%6d: init_lwp: out of memory\n"), (int)pid);
320			return (-1);
321		}
322		while (state->maxlwpid < lwpid) {
323			state->sgrps[state->maxlwpid] = NULL;
324			state->maxlwpid++;
325		}
326	}
327
328	if ((sgrp = state->sgrps[lwpid-1]) == NULL) {
329		if ((sgrp = cpc_setgrp_clone(opts->master)) == NULL) {
330			(void) fprintf(stderr, gettext(
331			    "%6d: init_lwp: out of memory\n"), (int)pid);
332			return (-1);
333		}
334		state->sgrps[lwpid-1] = sgrp;
335		set = cpc_setgrp_getset(sgrp);
336	} else {
337		cpc_setgrp_reset(sgrp);
338		set = cpc_setgrp_getset(sgrp);
339	}
340
341	nreq = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch);
342
343	if (cpc_bind_pctx(cpc, pctx, lwpid, set, 0) != 0 ||
344	    cpc_set_sample(cpc, set, *data2) != 0) {
345		errstr = strerror(errno);
346		if (errno == EAGAIN)
347			(void) cpc_unbind(cpc, set);
348#if defined(__i386)
349		if (errno == EACCES)
350			p4_ht_error();
351		else
352#endif
353			(void) fprintf(stderr, gettext(
354				"%6d: init_lwp: can't bind perf counters "
355				    "to lwp%d - %s\n"), (int)pid, (int)lwpid,
356			    errstr);
357		return (-1);
358	}
359
360	if (opts->verbose)
361		print_sample(pid, lwpid, "init_lwp",
362		    *data2, nreq, cpc_setgrp_getname(sgrp));
363	return (0);
364}
365
366/*ARGSUSED*/
367static int
368pfini_lwp(pctx_t *pctx, pid_t pid, id_t lwpid, void *arg)
369{
370	struct pstate *state = arg;
371	cpc_setgrp_t *sgrp = state->sgrps[lwpid-1];
372	cpc_set_t *set;
373	char *errstr;
374	cpc_buf_t **data1, **data2, **scratch;
375	int nreq;
376
377	set = cpc_setgrp_getset(sgrp);
378	nreq = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch);
379	if (cpc_set_sample(cpc, set, *data1) == 0) {
380		if (opts->verbose)
381			print_sample(pid, lwpid, "fini_lwp",
382			    *data1, nreq, cpc_setgrp_getname(sgrp));
383		cpc_setgrp_accum(state->accum, sgrp);
384		if (cpc_unbind(cpc, set) == 0)
385			return (0);
386	}
387
388	switch (errno) {
389	case EAGAIN:
390		(void) fprintf(stderr, gettext("%6d: fini_lwp: "
391		    "lwp%d: perf counter contents invalidated\n"),
392		    (int)pid, (int)lwpid);
393		break;
394	default:
395		errstr = strerror(errno);
396		(void) fprintf(stderr, gettext("%6d: fini_lwp: "
397		    "lwp%d: can't access perf counters - %s\n"),
398		    (int)pid, (int)lwpid, errstr);
399		break;
400	}
401	return (-1);
402}
403
404/*ARGSUSED*/
405static int
406plwp_create(pctx_t *pctx, pid_t pid, id_t lwpid, void *arg)
407{
408	cpc_setgrp_t	*sgrp = opts->master;
409	cpc_buf_t	**data1, **data2, **scratch;
410	int		nreq;
411
412	nreq = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch);
413
414	print_sample(pid, lwpid, "lwp_create",
415	    *data1, nreq, cpc_setgrp_getname(sgrp));
416
417	return (0);
418}
419
420/*ARGSUSED*/
421static int
422plwp_exit(pctx_t *pctx, pid_t pid, id_t lwpid, void *arg)
423{
424	struct pstate	*state = arg;
425	cpc_setgrp_t	*sgrp = state->sgrps[lwpid-1];
426	cpc_set_t	*start;
427	int		nreq;
428	cpc_buf_t	**data1, **data2, **scratch;
429
430	start = cpc_setgrp_getset(sgrp);
431	do {
432		nreq = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch);
433		if (cpc_buf_hrtime(cpc, *data1) == 0)
434			continue;
435		print_sample(pid, lwpid, "lwp_exit",
436		    *data1, nreq, cpc_setgrp_getname(sgrp));
437	} while (cpc_setgrp_nextset(sgrp) != start);
438
439	return (0);
440}
441
442/*ARGSUSED*/
443static int
444pexec(pctx_t *pctx, pid_t pid, id_t lwpid, char *name, void *arg)
445{
446	struct pstate	*state = arg;
447	float		now = 0.0;
448	cpc_set_t	*start;
449	int		nreq;
450	cpc_buf_t	**data1, **data2, **scratch;
451	hrtime_t	hrt;
452
453	/*
454	 * Print the accumulated results from the previous program image
455	 */
456	cpc_setgrp_reset(state->accum);
457	start = cpc_setgrp_getset(state->accum);
458	do {
459		nreq = cpc_setgrp_getbufs(state->accum, &data1, &data2,
460		    &scratch);
461		hrt = cpc_buf_hrtime(cpc, *data1);
462		if (hrt == 0)
463			continue;
464		print_sample(pid, lwpid, "exec",
465		    *data1, nreq, cpc_setgrp_getname(state->accum));
466		if (now < mstimestamp(hrt))
467			now = mstimestamp(hrt);
468	} while (cpc_setgrp_nextset(state->accum) != start);
469
470	print_exec(now, pid, name);
471
472	if (state->accum != NULL) {
473		cpc_setgrp_free(state->accum);
474		state->accum = NULL;
475	}
476
477	if (opts->followexec) {
478		state->accum = cpc_setgrp_clone(opts->master);
479		return (0);
480	}
481	return (-1);
482}
483
484/*ARGSUSED*/
485static void
486pexit(pctx_t *pctx, pid_t pid, id_t lwpid, int status, void *arg)
487{
488	struct pstate	*state = arg;
489	cpc_set_t	*start;
490	int		nreq;
491	cpc_buf_t	**data1, **data2, **scratch;
492
493	cpc_setgrp_reset(state->accum);
494	start = cpc_setgrp_getset(state->accum);
495	do {
496		nreq = cpc_setgrp_getbufs(state->accum, &data1, &data2,
497		    &scratch);
498		if (cpc_buf_hrtime(cpc, *data1) == 0)
499			continue;
500		print_sample(pid, lwpid, "exit",
501		    *data1, nreq, cpc_setgrp_getname(state->accum));
502	} while (cpc_setgrp_nextset(state->accum) != start);
503
504	cpc_setgrp_free(state->accum);
505	state->accum = NULL;
506
507	for (lwpid = 1; lwpid < state->maxlwpid; lwpid++)
508		if (state->sgrps[lwpid-1] != NULL) {
509			cpc_setgrp_free(state->sgrps[lwpid-1]);
510			state->sgrps[lwpid-1] = NULL;
511		}
512	free(state->sgrps);
513	state->sgrps = NULL;
514}
515
516static int
517ptick(pctx_t *pctx, pid_t pid, id_t lwpid, void *arg)
518{
519	struct pstate *state = arg;
520	cpc_setgrp_t *sgrp = state->sgrps[lwpid-1];
521	cpc_set_t *this = cpc_setgrp_getset(sgrp);
522	const char *name = cpc_setgrp_getname(sgrp);
523	cpc_buf_t **data1, **data2, **scratch, *tmp;
524	char *errstr;
525	int nreqs;
526
527	nreqs = cpc_setgrp_getbufs(sgrp, &data1, &data2, &scratch);
528
529	if (opts->nsets == 1) {
530		/*
531		 * If we're dealing with one set, buffer usage is:
532		 *
533		 * data1 = most recent data snapshot
534		 * data2 = previous data snapshot
535		 * scratch = used for diffing data1 and data2
536		 *
537		 * Save the snapshot from the previous sample in data2
538		 * before putting the current sample in data1.
539		 */
540		tmp = *data1;
541		*data1 = *data2;
542		*data2 = tmp;
543		if (cpc_set_sample(cpc, this, *data1) != 0)
544			goto broken;
545		cpc_buf_sub(cpc, *scratch, *data1, *data2);
546	} else {
547		cpc_set_t *next = cpc_setgrp_nextset(sgrp);
548		/*
549		 * If there is more than set in use, we will need to
550		 * unbind and re-bind on each go-around because each
551		 * time a counter is bound, it is preset to 0 (as it was
552		 * specified when the requests were added to the set).
553		 *
554		 * Buffer usage in this case is:
555		 *
556		 * data1 = total counts for this set since program began
557		 * data2 = unused
558		 * scratch = most recent data snapshot
559		 */
560
561		if (cpc_set_sample(cpc, this, *scratch) != 0)
562			goto broken;
563		cpc_buf_add(cpc, *data1, *data1, *scratch);
564
565		/*
566		 * No need to unbind the previous set, as binding another set
567		 * automatically unbinds the most recently bound set.
568		 */
569		if (cpc_bind_pctx(cpc, pctx, lwpid, next, 0) != 0)
570			goto broken;
571	}
572	print_sample(pid, lwpid, "tick", *scratch, nreqs, name);
573
574	return (0);
575
576broken:
577	switch (errno) {
578	case EAGAIN:
579		(void) fprintf(stderr, gettext(
580		    "%6d: tick: lwp%d: perf counter contents invalidated\n"),
581		    (int)pid, (int)lwpid);
582		break;
583	default:
584		errstr = strerror(errno);
585		(void) fprintf(stderr, gettext(
586		    "%6d: tick: lwp%d: can't access perf counter - %s\n"),
587		    (int)pid, (int)lwpid, errstr);
588		break;
589	}
590	(void) cpc_unbind(cpc, this);
591	return (-1);
592}
593
594/*
595 * The system has just created a new address space that has a new pid.
596 * We're running in a child of the controlling process, with a new
597 * pctx handle already opened on the child of the original controlled process.
598 */
599static void
600pfork(pctx_t *pctx, pid_t oldpid, pid_t pid, id_t lwpid, void *arg)
601{
602	struct pstate *state = arg;
603
604	print_fork(mstimestamp(0), pid, lwpid, oldpid);
605
606	if (!opts->followfork)
607		return;
608
609	if (pctx_set_events(pctx,
610	    PCTX_SYSC_EXEC_EVENT, pexec,
611	    PCTX_SYSC_FORK_EVENT, pfork,
612	    PCTX_SYSC_EXIT_EVENT, pexit,
613	    PCTX_SYSC_LWP_CREATE_EVENT, plwp_create,
614	    PCTX_INIT_LWP_EVENT, pinit_lwp,
615	    PCTX_FINI_LWP_EVENT, pfini_lwp,
616	    PCTX_SYSC_LWP_EXIT_EVENT, plwp_exit,
617	    PCTX_NULL_EVENT) == 0) {
618		state->accum = cpc_setgrp_clone(opts->master);
619		(void) pctx_run(pctx, opts->mseconds, opts->nsamples, ptick);
620		if (state->accum) {
621			free(state->accum);
622			state->accum = NULL;
623		}
624	}
625}
626
627/*
628 * Translate the incoming options into actions, and get the
629 * tool and the process to control running.
630 */
631static int
632cputrack(int argc, char *argv[], int optind)
633{
634	struct pstate __state, *state = &__state;
635	pctx_t *pctx;
636	int err;
637
638	bzero(state, sizeof (*state));
639
640	if (opts->pid == 0) {
641		if (argc <= optind) {
642			(void) fprintf(stderr, "%s: %s\n",
643			    opts->pgmname,
644			    gettext("no program to start"));
645			return (1);
646		}
647		pctx = pctx_create(argv[optind],
648		    &argv[optind], state, 1, cputrack_pctx_errfn);
649		if (pctx == NULL) {
650			(void) fprintf(stderr, "%s: %s '%s'\n",
651			    opts->pgmname,
652			    gettext("failed to start program"),
653			    argv[optind]);
654			return (1);
655		}
656	} else {
657		pctx = pctx_capture(opts->pid, state, 1, cputrack_pctx_errfn);
658		if (pctx == NULL) {
659			(void) fprintf(stderr, "%s: %s %d\n",
660			    opts->pgmname,
661			    gettext("failed to capture pid"),
662			    (int)opts->pid);
663			return (1);
664		}
665	}
666
667	err = pctx_set_events(pctx,
668	    PCTX_SYSC_EXEC_EVENT, pexec,
669	    PCTX_SYSC_FORK_EVENT, pfork,
670	    PCTX_SYSC_EXIT_EVENT, pexit,
671	    PCTX_SYSC_LWP_CREATE_EVENT, plwp_create,
672	    PCTX_INIT_LWP_EVENT, pinit_lwp,
673	    PCTX_FINI_LWP_EVENT, pfini_lwp,
674	    PCTX_SYSC_LWP_EXIT_EVENT, plwp_exit,
675	    PCTX_NULL_EVENT);
676
677	if (err != 0) {
678		(void) fprintf(stderr, "%s: %s\n",
679		    opts->pgmname,
680		    gettext("can't bind process context ops to process"));
681	} else {
682		if (opts->dotitle)
683			print_title(opts->master);
684		state->accum = cpc_setgrp_clone(opts->master);
685		zerotime();
686		err = pctx_run(pctx, opts->mseconds, opts->nsamples, ptick);
687		if (state->accum) {
688			cpc_setgrp_free(state->accum);
689			state->accum = NULL;
690		}
691	}
692	pctx_release(pctx);
693
694	return (err != 0 ? 1 : 0);
695}
696
697#if defined(__i386)
698
699#define	OFFLINE_CMD	"/usr/sbin/psradm -f "
700#define	BUFSIZE		5	/* enough for "n " where n is a cpuid */
701
702/*
703 * cpc_bind_pctx() failed with EACCES, which means the user must first offline
704 * all but one logical processor on each physical processor. Print to stderr the
705 * psradm command string to do this.
706 */
707static void
708p4_ht_error(void)
709{
710	kstat_ctl_t	*kc;
711	kstat_t		*ksp;
712	kstat_named_t	*k;
713	int		i;
714	int		max;
715	int		stat;
716	int		*designees;
717	int		*must_offline;
718	char		buf[BUFSIZE];
719	char		*cmd;
720	int		noffline = 0;
721	int		ndone = 0;
722
723	(void) fprintf(stderr, "%s\n",
724	    gettext("Pentium 4 processors with HyperThreading present.\nOffline"
725		" all but one logical processor on each physical processor in"
726		" order to use\ncputrack.\n"));
727
728
729	if ((kc = kstat_open()) == NULL)
730		return;
731
732	max = sysconf(_SC_CPUID_MAX);
733	if ((designees = malloc(max * sizeof (*designees))) == NULL) {
734		(void) fprintf(stderr, gettext("%s: no memory available\n"),
735		    opts->pgmname);
736		exit(0);
737	}
738
739	if ((must_offline = malloc(max * sizeof (*designees))) == NULL) {
740		(void) fprintf(stderr, gettext("%s: no memory available\n"),
741		    opts->pgmname);
742		exit(0);
743	}
744
745	for (i = 0; i < max; i++) {
746		designees[i] = -1;
747		must_offline[i] = 0;
748	}
749
750	for (i = 0; i < max; i++) {
751		stat = p_online(i, P_STATUS);
752		if (stat != P_ONLINE && stat != P_NOINTR)
753			continue;
754
755		if ((ksp = kstat_lookup(kc, "cpu_info", i, NULL)) == NULL) {
756			free(designees);
757			free(must_offline);
758			return;
759		}
760
761		if (kstat_read(kc, ksp, NULL) == -1) {
762			free(designees);
763			free(must_offline);
764			return;
765		}
766
767		if ((k = (kstat_named_t *)kstat_data_lookup(ksp, "chip_id"))
768		    == NULL) {
769			free(designees);
770			free(must_offline);
771			return;
772		}
773
774		if (designees[k->value.i32] == -1)
775			/*
776			 * This chip doesn't yet have a CPU designated to remain
777			 * online; let this one be it.
778			 */
779			designees[k->value.i32] = i;
780		else {
781			/*
782			 * This chip already has a designated CPU; this CPU must
783			 * go offline.
784			 */
785			must_offline[i] = 1;
786			noffline++;
787		}
788	}
789
790	/*
791	 * Now construct a string containing the command line used to offline
792	 * the appropriate processors.
793	 */
794
795	if ((cmd = malloc(strlen(OFFLINE_CMD) + (noffline * BUFSIZE) + 1))
796	    == NULL) {
797		(void) fprintf(stderr, gettext("%s: no memory available\n"),
798		    opts->pgmname);
799		exit(0);
800	}
801
802	(void) strcpy(cmd, OFFLINE_CMD);
803
804	for (i = 0; i < max; i++) {
805		if (must_offline[i] == 0)
806			continue;
807
808		ndone++;
809		(void) snprintf(buf, BUFSIZE, "%d", i);
810		if (ndone < noffline)
811			(void) strcat(buf, " ");
812		(void) strcat(cmd, buf);
813	}
814
815	(void) fprintf(stderr, "%s:\n%s\n", gettext("The following command "
816	    "will configure the system appropriately"), cmd);
817
818	exit(1);
819}
820
821#endif /* defined(__i386) */
822