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