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