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