1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate /*
30*7c478bd9Sstevel@tonic-gate  * This file contains a set of generic routines for periodically
31*7c478bd9Sstevel@tonic-gate  * sampling the state of another process, or tree of processes.
32*7c478bd9Sstevel@tonic-gate  *
33*7c478bd9Sstevel@tonic-gate  * It is built upon the infrastructure provided by libproc.
34*7c478bd9Sstevel@tonic-gate  */
35*7c478bd9Sstevel@tonic-gate 
36*7c478bd9Sstevel@tonic-gate #include <sys/wait.h>
37*7c478bd9Sstevel@tonic-gate #include <sys/syscall.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/time.h>
39*7c478bd9Sstevel@tonic-gate #include <libproc.h>
40*7c478bd9Sstevel@tonic-gate #include <stdio.h>
41*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
42*7c478bd9Sstevel@tonic-gate #include <errno.h>
43*7c478bd9Sstevel@tonic-gate #include <unistd.h>
44*7c478bd9Sstevel@tonic-gate #include <signal.h>
45*7c478bd9Sstevel@tonic-gate #include <string.h>
46*7c478bd9Sstevel@tonic-gate #include <strings.h>
47*7c478bd9Sstevel@tonic-gate #include <limits.h>
48*7c478bd9Sstevel@tonic-gate #include <ctype.h>
49*7c478bd9Sstevel@tonic-gate #include <libintl.h>
50*7c478bd9Sstevel@tonic-gate #include <libcpc.h>
51*7c478bd9Sstevel@tonic-gate #include <sys/cpc_impl.h>
52*7c478bd9Sstevel@tonic-gate 
53*7c478bd9Sstevel@tonic-gate #include "libpctx.h"
54*7c478bd9Sstevel@tonic-gate 
55*7c478bd9Sstevel@tonic-gate struct __pctx {
56*7c478bd9Sstevel@tonic-gate 	pctx_errfn_t *errfn;
57*7c478bd9Sstevel@tonic-gate 	struct ps_prochandle *Pr;
58*7c478bd9Sstevel@tonic-gate 	void *uarg;
59*7c478bd9Sstevel@tonic-gate 	pctx_sysc_execfn_t *exec;
60*7c478bd9Sstevel@tonic-gate 	pctx_sysc_forkfn_t *fork;
61*7c478bd9Sstevel@tonic-gate 	pctx_sysc_exitfn_t *exit;
62*7c478bd9Sstevel@tonic-gate 	pctx_sysc_lwp_createfn_t *lwp_create;
63*7c478bd9Sstevel@tonic-gate 	pctx_init_lwpfn_t *init_lwp;
64*7c478bd9Sstevel@tonic-gate 	pctx_fini_lwpfn_t *fini_lwp;
65*7c478bd9Sstevel@tonic-gate 	pctx_sysc_lwp_exitfn_t *lwp_exit;
66*7c478bd9Sstevel@tonic-gate 	int verbose;
67*7c478bd9Sstevel@tonic-gate 	int created;
68*7c478bd9Sstevel@tonic-gate 	int sigblocked;
69*7c478bd9Sstevel@tonic-gate 	sigset_t savedset;
70*7c478bd9Sstevel@tonic-gate 	cpc_t *cpc;
71*7c478bd9Sstevel@tonic-gate };
72*7c478bd9Sstevel@tonic-gate 
73*7c478bd9Sstevel@tonic-gate static void (*pctx_cpc_callback)(cpc_t *cpc, struct __pctx *pctx);
74*7c478bd9Sstevel@tonic-gate 
75*7c478bd9Sstevel@tonic-gate static void
76*7c478bd9Sstevel@tonic-gate pctx_default_errfn(const char *fn, const char *fmt, va_list ap)
77*7c478bd9Sstevel@tonic-gate {
78*7c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "libpctx: pctx_%s: ", fn);
79*7c478bd9Sstevel@tonic-gate 	(void) vfprintf(stderr, fmt, ap);
80*7c478bd9Sstevel@tonic-gate }
81*7c478bd9Sstevel@tonic-gate 
82*7c478bd9Sstevel@tonic-gate /*PRINTFLIKE3*/
83*7c478bd9Sstevel@tonic-gate static void
84*7c478bd9Sstevel@tonic-gate pctx_error(pctx_t *pctx, const char *fn, const char *fmt, ...)
85*7c478bd9Sstevel@tonic-gate {
86*7c478bd9Sstevel@tonic-gate 	va_list ap;
87*7c478bd9Sstevel@tonic-gate 
88*7c478bd9Sstevel@tonic-gate 	va_start(ap, fmt);
89*7c478bd9Sstevel@tonic-gate 	pctx->errfn(fn, fmt, ap);
90*7c478bd9Sstevel@tonic-gate 	va_end(ap);
91*7c478bd9Sstevel@tonic-gate }
92*7c478bd9Sstevel@tonic-gate 
93*7c478bd9Sstevel@tonic-gate /*
94*7c478bd9Sstevel@tonic-gate  * Create a new process and bind the user args for it
95*7c478bd9Sstevel@tonic-gate  */
96*7c478bd9Sstevel@tonic-gate pctx_t *
97*7c478bd9Sstevel@tonic-gate pctx_create(
98*7c478bd9Sstevel@tonic-gate     const char *filename,
99*7c478bd9Sstevel@tonic-gate     char *const *argv,
100*7c478bd9Sstevel@tonic-gate     void *arg,
101*7c478bd9Sstevel@tonic-gate     int verbose,
102*7c478bd9Sstevel@tonic-gate     pctx_errfn_t *errfn)
103*7c478bd9Sstevel@tonic-gate {
104*7c478bd9Sstevel@tonic-gate 	static const char fn[] = "create";
105*7c478bd9Sstevel@tonic-gate 	int err;
106*7c478bd9Sstevel@tonic-gate 	pctx_t *pctx;
107*7c478bd9Sstevel@tonic-gate 
108*7c478bd9Sstevel@tonic-gate 	pctx = calloc(1, sizeof (*pctx));
109*7c478bd9Sstevel@tonic-gate 	pctx->uarg = arg;
110*7c478bd9Sstevel@tonic-gate 	pctx->verbose = verbose;
111*7c478bd9Sstevel@tonic-gate 	pctx->errfn = errfn ? errfn : pctx_default_errfn;
112*7c478bd9Sstevel@tonic-gate 
113*7c478bd9Sstevel@tonic-gate 	if ((pctx->Pr = Pcreate(filename, argv, &err, 0, 0)) == NULL) {
114*7c478bd9Sstevel@tonic-gate 		switch (err) {
115*7c478bd9Sstevel@tonic-gate 		case C_PERM:
116*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot trace set-id or "
117*7c478bd9Sstevel@tonic-gate 			    "unreadable program '%s'\n"), filename);
118*7c478bd9Sstevel@tonic-gate 			break;
119*7c478bd9Sstevel@tonic-gate 		case C_LP64:
120*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot control LP64 "
121*7c478bd9Sstevel@tonic-gate 			    "program '%s'\n"), filename);
122*7c478bd9Sstevel@tonic-gate 			break;
123*7c478bd9Sstevel@tonic-gate 		case C_NOEXEC:
124*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot execute "
125*7c478bd9Sstevel@tonic-gate 			    "program '%s'\n"), filename);
126*7c478bd9Sstevel@tonic-gate 			break;
127*7c478bd9Sstevel@tonic-gate 		case C_NOENT:
128*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot find"
129*7c478bd9Sstevel@tonic-gate 			    "program '%s'\n"), filename);
130*7c478bd9Sstevel@tonic-gate 			break;
131*7c478bd9Sstevel@tonic-gate 		case C_FORK:
132*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot fork, "
133*7c478bd9Sstevel@tonic-gate 			    "program '%s'\n"), filename);
134*7c478bd9Sstevel@tonic-gate 			break;
135*7c478bd9Sstevel@tonic-gate 		default:
136*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("%s, program '%s'\n"),
137*7c478bd9Sstevel@tonic-gate 			    Pcreate_error(err), filename);
138*7c478bd9Sstevel@tonic-gate 			break;
139*7c478bd9Sstevel@tonic-gate 		}
140*7c478bd9Sstevel@tonic-gate 		free(pctx);
141*7c478bd9Sstevel@tonic-gate 		return (NULL);
142*7c478bd9Sstevel@tonic-gate 	}
143*7c478bd9Sstevel@tonic-gate 
144*7c478bd9Sstevel@tonic-gate 	if (Psysentry(pctx->Pr, SYS_exit, 1) == -1) {
145*7c478bd9Sstevel@tonic-gate 		pctx_error(pctx, fn,
146*7c478bd9Sstevel@tonic-gate 		    gettext("can't stop-on-exit() program '%s'\n"), filename);
147*7c478bd9Sstevel@tonic-gate 		Prelease(pctx->Pr, PRELEASE_KILL);
148*7c478bd9Sstevel@tonic-gate 		free(pctx);
149*7c478bd9Sstevel@tonic-gate 		return (NULL);
150*7c478bd9Sstevel@tonic-gate 	}
151*7c478bd9Sstevel@tonic-gate 	/*
152*7c478bd9Sstevel@tonic-gate 	 * Set kill-on-last-close so the controlled process
153*7c478bd9Sstevel@tonic-gate 	 * dies if we die.
154*7c478bd9Sstevel@tonic-gate 	 */
155*7c478bd9Sstevel@tonic-gate 	pctx->created = 1;
156*7c478bd9Sstevel@tonic-gate 	(void) Psetflags(pctx->Pr, PR_KLC);
157*7c478bd9Sstevel@tonic-gate 	(void) pctx_set_events(pctx, PCTX_NULL_EVENT);
158*7c478bd9Sstevel@tonic-gate 
159*7c478bd9Sstevel@tonic-gate 	return (pctx);
160*7c478bd9Sstevel@tonic-gate }
161*7c478bd9Sstevel@tonic-gate 
162*7c478bd9Sstevel@tonic-gate /*
163*7c478bd9Sstevel@tonic-gate  * Capture an existing process and bind the user args for it
164*7c478bd9Sstevel@tonic-gate  */
165*7c478bd9Sstevel@tonic-gate pctx_t *
166*7c478bd9Sstevel@tonic-gate pctx_capture(pid_t pid, void *arg, int verbose, pctx_errfn_t *errfn)
167*7c478bd9Sstevel@tonic-gate {
168*7c478bd9Sstevel@tonic-gate 	static const char fn[] = "capture";
169*7c478bd9Sstevel@tonic-gate 	int err;
170*7c478bd9Sstevel@tonic-gate 	pctx_t *pctx;
171*7c478bd9Sstevel@tonic-gate 
172*7c478bd9Sstevel@tonic-gate 	pctx = calloc(1, sizeof (*pctx));
173*7c478bd9Sstevel@tonic-gate 	pctx->uarg = arg;
174*7c478bd9Sstevel@tonic-gate 	pctx->verbose = verbose;
175*7c478bd9Sstevel@tonic-gate 	pctx->errfn = errfn ? errfn : pctx_default_errfn;
176*7c478bd9Sstevel@tonic-gate 
177*7c478bd9Sstevel@tonic-gate 	if ((pctx->Pr = Pgrab(pid, 0, &err)) == NULL) {
178*7c478bd9Sstevel@tonic-gate 		switch (err) {
179*7c478bd9Sstevel@tonic-gate 		case G_NOPROC:
180*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn,
181*7c478bd9Sstevel@tonic-gate 			    gettext("pid %d doesn't exist\n"), (int)pid);
182*7c478bd9Sstevel@tonic-gate 			break;
183*7c478bd9Sstevel@tonic-gate 		case G_ZOMB:
184*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn,
185*7c478bd9Sstevel@tonic-gate 			    gettext("pid %d is a zombie\n"), (int)pid);
186*7c478bd9Sstevel@tonic-gate 			break;
187*7c478bd9Sstevel@tonic-gate 		case G_PERM:
188*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn,
189*7c478bd9Sstevel@tonic-gate 			    gettext("pid %d: permission denied\n"), (int)pid);
190*7c478bd9Sstevel@tonic-gate 			break;
191*7c478bd9Sstevel@tonic-gate 		case G_BUSY:
192*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn,
193*7c478bd9Sstevel@tonic-gate 			    gettext("pid %d is already being traced\n"),
194*7c478bd9Sstevel@tonic-gate 			    (int)pid);
195*7c478bd9Sstevel@tonic-gate 			break;
196*7c478bd9Sstevel@tonic-gate 		case G_SYS:
197*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn,
198*7c478bd9Sstevel@tonic-gate 			    gettext("pid %d is a system process\n"), (int)pid);
199*7c478bd9Sstevel@tonic-gate 			break;
200*7c478bd9Sstevel@tonic-gate 		case G_SELF:
201*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn,
202*7c478bd9Sstevel@tonic-gate 			    gettext("cannot capture self!\n"));
203*7c478bd9Sstevel@tonic-gate 			break;
204*7c478bd9Sstevel@tonic-gate 		case G_LP64:
205*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("cannot control LP64 "
206*7c478bd9Sstevel@tonic-gate 			    "process, pid %d\n"), (int)pid);
207*7c478bd9Sstevel@tonic-gate 			break;
208*7c478bd9Sstevel@tonic-gate 		default:
209*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn, gettext("%s: pid %d\n"),
210*7c478bd9Sstevel@tonic-gate 			    Pgrab_error(err), (int)pid);
211*7c478bd9Sstevel@tonic-gate 			break;
212*7c478bd9Sstevel@tonic-gate 		}
213*7c478bd9Sstevel@tonic-gate 		free(pctx);
214*7c478bd9Sstevel@tonic-gate 		return (NULL);
215*7c478bd9Sstevel@tonic-gate 	}
216*7c478bd9Sstevel@tonic-gate 
217*7c478bd9Sstevel@tonic-gate 	if (Psysentry(pctx->Pr, SYS_exit, 1) == -1) {
218*7c478bd9Sstevel@tonic-gate 		pctx_error(pctx, fn,
219*7c478bd9Sstevel@tonic-gate 		    gettext("can't stop-on-exit() pid %d\n"), (int)pid);
220*7c478bd9Sstevel@tonic-gate 		Prelease(pctx->Pr, PRELEASE_CLEAR);
221*7c478bd9Sstevel@tonic-gate 		free(pctx);
222*7c478bd9Sstevel@tonic-gate 		return (NULL);
223*7c478bd9Sstevel@tonic-gate 	}
224*7c478bd9Sstevel@tonic-gate 
225*7c478bd9Sstevel@tonic-gate 	/*
226*7c478bd9Sstevel@tonic-gate 	 * Set run-on-last-close so the controlled process
227*7c478bd9Sstevel@tonic-gate 	 * runs even if we die on a signal.  This is because
228*7c478bd9Sstevel@tonic-gate 	 * we grabbed an existing process - it would be impolite
229*7c478bd9Sstevel@tonic-gate 	 * to cause it to die if we exit prematurely.
230*7c478bd9Sstevel@tonic-gate 	 */
231*7c478bd9Sstevel@tonic-gate 	pctx->created = 0;
232*7c478bd9Sstevel@tonic-gate 	(void) Psetflags(pctx->Pr, PR_RLC);
233*7c478bd9Sstevel@tonic-gate 	(void) pctx_set_events(pctx, PCTX_NULL_EVENT);
234*7c478bd9Sstevel@tonic-gate 
235*7c478bd9Sstevel@tonic-gate 	return (pctx);
236*7c478bd9Sstevel@tonic-gate }
237*7c478bd9Sstevel@tonic-gate 
238*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
239*7c478bd9Sstevel@tonic-gate static void
240*7c478bd9Sstevel@tonic-gate default_void(pctx_t *pctx)
241*7c478bd9Sstevel@tonic-gate {}
242*7c478bd9Sstevel@tonic-gate 
243*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
244*7c478bd9Sstevel@tonic-gate static int
245*7c478bd9Sstevel@tonic-gate default_int(pctx_t *pctx)
246*7c478bd9Sstevel@tonic-gate {
247*7c478bd9Sstevel@tonic-gate 	return (0);
248*7c478bd9Sstevel@tonic-gate }
249*7c478bd9Sstevel@tonic-gate 
250*7c478bd9Sstevel@tonic-gate int
251*7c478bd9Sstevel@tonic-gate pctx_set_events(pctx_t *pctx, ...)
252*7c478bd9Sstevel@tonic-gate {
253*7c478bd9Sstevel@tonic-gate 	static const char fn[] = "set_events";
254*7c478bd9Sstevel@tonic-gate 	va_list pvar;
255*7c478bd9Sstevel@tonic-gate 	int error = 0;
256*7c478bd9Sstevel@tonic-gate 	pctx_event_t event;
257*7c478bd9Sstevel@tonic-gate 
258*7c478bd9Sstevel@tonic-gate 	va_start(pvar, pctx);
259*7c478bd9Sstevel@tonic-gate 	do {
260*7c478bd9Sstevel@tonic-gate 		switch (event = (pctx_event_t)va_arg(pvar, pctx_event_t)) {
261*7c478bd9Sstevel@tonic-gate 		case PCTX_NULL_EVENT:
262*7c478bd9Sstevel@tonic-gate 			break;
263*7c478bd9Sstevel@tonic-gate 		case PCTX_SYSC_EXEC_EVENT:
264*7c478bd9Sstevel@tonic-gate 			pctx->exec = (pctx_sysc_execfn_t *)
265*7c478bd9Sstevel@tonic-gate 			    va_arg(pvar, pctx_sysc_execfn_t *);
266*7c478bd9Sstevel@tonic-gate 			break;
267*7c478bd9Sstevel@tonic-gate 		case PCTX_SYSC_FORK_EVENT:
268*7c478bd9Sstevel@tonic-gate 			pctx->fork = (pctx_sysc_forkfn_t *)
269*7c478bd9Sstevel@tonic-gate 			    va_arg(pvar, pctx_sysc_forkfn_t *);
270*7c478bd9Sstevel@tonic-gate 			break;
271*7c478bd9Sstevel@tonic-gate 		case PCTX_SYSC_EXIT_EVENT:	/* always intercepted */
272*7c478bd9Sstevel@tonic-gate 			pctx->exit = (pctx_sysc_exitfn_t *)
273*7c478bd9Sstevel@tonic-gate 			    va_arg(pvar, pctx_sysc_exitfn_t *);
274*7c478bd9Sstevel@tonic-gate 			break;
275*7c478bd9Sstevel@tonic-gate 		case PCTX_SYSC_LWP_CREATE_EVENT:
276*7c478bd9Sstevel@tonic-gate 			pctx->lwp_create = (pctx_sysc_lwp_createfn_t *)
277*7c478bd9Sstevel@tonic-gate 			    va_arg(pvar, pctx_sysc_lwp_createfn_t *);
278*7c478bd9Sstevel@tonic-gate 			break;
279*7c478bd9Sstevel@tonic-gate 		case PCTX_INIT_LWP_EVENT:
280*7c478bd9Sstevel@tonic-gate 			pctx->init_lwp = (pctx_init_lwpfn_t *)
281*7c478bd9Sstevel@tonic-gate 			    va_arg(pvar, pctx_init_lwpfn_t *);
282*7c478bd9Sstevel@tonic-gate 			break;
283*7c478bd9Sstevel@tonic-gate 		case PCTX_FINI_LWP_EVENT:
284*7c478bd9Sstevel@tonic-gate 			pctx->fini_lwp = (pctx_fini_lwpfn_t *)
285*7c478bd9Sstevel@tonic-gate 			    va_arg(pvar, pctx_fini_lwpfn_t *);
286*7c478bd9Sstevel@tonic-gate 			break;
287*7c478bd9Sstevel@tonic-gate 		case PCTX_SYSC_LWP_EXIT_EVENT:
288*7c478bd9Sstevel@tonic-gate 			pctx->lwp_exit = (pctx_sysc_lwp_exitfn_t *)
289*7c478bd9Sstevel@tonic-gate 			    va_arg(pvar, pctx_sysc_lwp_exitfn_t *);
290*7c478bd9Sstevel@tonic-gate 			break;
291*7c478bd9Sstevel@tonic-gate 		default:
292*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn,
293*7c478bd9Sstevel@tonic-gate 			    gettext("unknown event type %x\n"), event);
294*7c478bd9Sstevel@tonic-gate 			error = -1;
295*7c478bd9Sstevel@tonic-gate 			break;
296*7c478bd9Sstevel@tonic-gate 		}
297*7c478bd9Sstevel@tonic-gate 	} while (event != PCTX_NULL_EVENT && error == 0);
298*7c478bd9Sstevel@tonic-gate 	va_end(pvar);
299*7c478bd9Sstevel@tonic-gate 
300*7c478bd9Sstevel@tonic-gate 	if (error != 0)
301*7c478bd9Sstevel@tonic-gate 		return (error);
302*7c478bd9Sstevel@tonic-gate 
303*7c478bd9Sstevel@tonic-gate 	if (pctx->exec == NULL)
304*7c478bd9Sstevel@tonic-gate 		pctx->exec = (pctx_sysc_execfn_t *)default_int;
305*7c478bd9Sstevel@tonic-gate 	if (pctx->fork == NULL)
306*7c478bd9Sstevel@tonic-gate 		pctx->fork = (pctx_sysc_forkfn_t *)default_void;
307*7c478bd9Sstevel@tonic-gate 	if (pctx->exit == NULL)
308*7c478bd9Sstevel@tonic-gate 		pctx->exit = (pctx_sysc_exitfn_t *)default_void;
309*7c478bd9Sstevel@tonic-gate 	if (pctx->lwp_create == NULL)
310*7c478bd9Sstevel@tonic-gate 		pctx->lwp_create = (pctx_sysc_lwp_createfn_t *)default_int;
311*7c478bd9Sstevel@tonic-gate 	if (pctx->init_lwp == NULL)
312*7c478bd9Sstevel@tonic-gate 		pctx->init_lwp = (pctx_init_lwpfn_t *)default_int;
313*7c478bd9Sstevel@tonic-gate 	if (pctx->fini_lwp == NULL)
314*7c478bd9Sstevel@tonic-gate 		pctx->fini_lwp = (pctx_fini_lwpfn_t *)default_int;
315*7c478bd9Sstevel@tonic-gate 	if (pctx->lwp_exit == NULL)
316*7c478bd9Sstevel@tonic-gate 		pctx->lwp_exit = (pctx_sysc_lwp_exitfn_t *)default_int;
317*7c478bd9Sstevel@tonic-gate 
318*7c478bd9Sstevel@tonic-gate 	if (pctx->fork != (pctx_sysc_forkfn_t *)default_void) {
319*7c478bd9Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_forkall, 1);
320*7c478bd9Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_vfork, 1);
321*7c478bd9Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_fork1, 1);
322*7c478bd9Sstevel@tonic-gate 		if (Psetflags(pctx->Pr, PR_FORK) == -1)
323*7c478bd9Sstevel@tonic-gate 			error = -1;
324*7c478bd9Sstevel@tonic-gate 	} else {
325*7c478bd9Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_forkall, 0);
326*7c478bd9Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_vfork, 0);
327*7c478bd9Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_fork1, 0);
328*7c478bd9Sstevel@tonic-gate 		if (Punsetflags(pctx->Pr, PR_FORK) == -1)
329*7c478bd9Sstevel@tonic-gate 			error = -1;
330*7c478bd9Sstevel@tonic-gate 	}
331*7c478bd9Sstevel@tonic-gate 
332*7c478bd9Sstevel@tonic-gate 	/*
333*7c478bd9Sstevel@tonic-gate 	 * exec causes termination of all but the exec-ing lwp,
334*7c478bd9Sstevel@tonic-gate 	 * and resets the lwpid to one in the new address space.
335*7c478bd9Sstevel@tonic-gate 	 */
336*7c478bd9Sstevel@tonic-gate 	if (pctx->exec != (pctx_sysc_execfn_t *)default_int ||
337*7c478bd9Sstevel@tonic-gate 	    pctx->fini_lwp != (pctx_fini_lwpfn_t *)default_int ||
338*7c478bd9Sstevel@tonic-gate 	    pctx->init_lwp != (pctx_init_lwpfn_t *)default_int) {
339*7c478bd9Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_exec, 1);
340*7c478bd9Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_execve, 1);
341*7c478bd9Sstevel@tonic-gate 		(void) Psysentry(pctx->Pr, SYS_exec, 1);
342*7c478bd9Sstevel@tonic-gate 		(void) Psysentry(pctx->Pr, SYS_execve, 1);
343*7c478bd9Sstevel@tonic-gate 	} else {
344*7c478bd9Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_exec, 0);
345*7c478bd9Sstevel@tonic-gate 		(void) Psysexit(pctx->Pr, SYS_execve, 0);
346*7c478bd9Sstevel@tonic-gate 		(void) Psysentry(pctx->Pr, SYS_exec, 0);
347*7c478bd9Sstevel@tonic-gate 		(void) Psysentry(pctx->Pr, SYS_execve, 0);
348*7c478bd9Sstevel@tonic-gate 	}
349*7c478bd9Sstevel@tonic-gate 
350*7c478bd9Sstevel@tonic-gate 	(void) Psysexit(pctx->Pr, SYS_lwp_create,
351*7c478bd9Sstevel@tonic-gate 	    pctx->lwp_create != (pctx_sysc_lwp_createfn_t *)default_int ||
352*7c478bd9Sstevel@tonic-gate 	    pctx->init_lwp != (pctx_init_lwpfn_t *)default_int);
353*7c478bd9Sstevel@tonic-gate 
354*7c478bd9Sstevel@tonic-gate 	(void) Psysentry(pctx->Pr, SYS_lwp_exit,
355*7c478bd9Sstevel@tonic-gate 	    pctx->lwp_exit != (pctx_sysc_lwp_exitfn_t *)default_int ||
356*7c478bd9Sstevel@tonic-gate 	    pctx->fini_lwp != (pctx_fini_lwpfn_t *)default_int);
357*7c478bd9Sstevel@tonic-gate 
358*7c478bd9Sstevel@tonic-gate 	return (0);
359*7c478bd9Sstevel@tonic-gate }
360*7c478bd9Sstevel@tonic-gate 
361*7c478bd9Sstevel@tonic-gate static sigset_t termsig;
362*7c478bd9Sstevel@tonic-gate 
363*7c478bd9Sstevel@tonic-gate static void
364*7c478bd9Sstevel@tonic-gate __libpctx_init(void)
365*7c478bd9Sstevel@tonic-gate {
366*7c478bd9Sstevel@tonic-gate 	/*
367*7c478bd9Sstevel@tonic-gate 	 * Initialize the signal set used to shield ourselves from
368*7c478bd9Sstevel@tonic-gate 	 * death-by-terminal-signal while the agent lwp is running.
369*7c478bd9Sstevel@tonic-gate 	 */
370*7c478bd9Sstevel@tonic-gate 	(void) sigemptyset(&termsig);
371*7c478bd9Sstevel@tonic-gate 	(void) sigaddset(&termsig, SIGHUP);
372*7c478bd9Sstevel@tonic-gate 	(void) sigaddset(&termsig, SIGTERM);
373*7c478bd9Sstevel@tonic-gate 	(void) sigaddset(&termsig, SIGINT);
374*7c478bd9Sstevel@tonic-gate 	(void) sigaddset(&termsig, SIGQUIT);
375*7c478bd9Sstevel@tonic-gate }
376*7c478bd9Sstevel@tonic-gate 
377*7c478bd9Sstevel@tonic-gate #pragma init(__libpctx_init)
378*7c478bd9Sstevel@tonic-gate 
379*7c478bd9Sstevel@tonic-gate static void
380*7c478bd9Sstevel@tonic-gate pctx_begin_syscalls(pctx_t *pctx)
381*7c478bd9Sstevel@tonic-gate {
382*7c478bd9Sstevel@tonic-gate 	if (pctx->Pr == NULL)
383*7c478bd9Sstevel@tonic-gate 		return;
384*7c478bd9Sstevel@tonic-gate 	if (pctx->sigblocked++ == 0) {
385*7c478bd9Sstevel@tonic-gate 		(void) sigprocmask(SIG_BLOCK, &termsig, &pctx->savedset);
386*7c478bd9Sstevel@tonic-gate 		(void) Pcreate_agent(pctx->Pr);
387*7c478bd9Sstevel@tonic-gate 	}
388*7c478bd9Sstevel@tonic-gate }
389*7c478bd9Sstevel@tonic-gate 
390*7c478bd9Sstevel@tonic-gate static void
391*7c478bd9Sstevel@tonic-gate pctx_end_syscalls(pctx_t *pctx)
392*7c478bd9Sstevel@tonic-gate {
393*7c478bd9Sstevel@tonic-gate 	if (pctx->Pr == NULL)
394*7c478bd9Sstevel@tonic-gate 		return;
395*7c478bd9Sstevel@tonic-gate 	if (--pctx->sigblocked == 0) {
396*7c478bd9Sstevel@tonic-gate 		(void) Pdestroy_agent(pctx->Pr);
397*7c478bd9Sstevel@tonic-gate 		(void) sigprocmask(SIG_SETMASK, &pctx->savedset, NULL);
398*7c478bd9Sstevel@tonic-gate 	}
399*7c478bd9Sstevel@tonic-gate }
400*7c478bd9Sstevel@tonic-gate 
401*7c478bd9Sstevel@tonic-gate /*
402*7c478bd9Sstevel@tonic-gate  * Iterate over the valid lwpids in the process, invoking the
403*7c478bd9Sstevel@tonic-gate  * action function on each one.
404*7c478bd9Sstevel@tonic-gate  */
405*7c478bd9Sstevel@tonic-gate static int
406*7c478bd9Sstevel@tonic-gate pctx_lwpiterate(pctx_t *pctx, int (*action)(pctx_t *, pid_t, id_t, void *))
407*7c478bd9Sstevel@tonic-gate {
408*7c478bd9Sstevel@tonic-gate 	const pstatus_t *pstatus;
409*7c478bd9Sstevel@tonic-gate 	char lstatus[64];
410*7c478bd9Sstevel@tonic-gate 	struct stat statb;
411*7c478bd9Sstevel@tonic-gate 	lwpstatus_t *lwps;
412*7c478bd9Sstevel@tonic-gate 	prheader_t *prh;
413*7c478bd9Sstevel@tonic-gate 	int fd, nlwp;
414*7c478bd9Sstevel@tonic-gate 	int ret = 0;
415*7c478bd9Sstevel@tonic-gate 
416*7c478bd9Sstevel@tonic-gate 	if (action == (int (*)(pctx_t *, pid_t, id_t, void *))default_int)
417*7c478bd9Sstevel@tonic-gate 		return (0);
418*7c478bd9Sstevel@tonic-gate 
419*7c478bd9Sstevel@tonic-gate 	pstatus = Pstatus(pctx->Pr);
420*7c478bd9Sstevel@tonic-gate 	if (pstatus->pr_nlwp <= 1) {
421*7c478bd9Sstevel@tonic-gate 		pctx_begin_syscalls(pctx);
422*7c478bd9Sstevel@tonic-gate 		ret = action(pctx, pstatus->pr_pid, 1, pctx->uarg);
423*7c478bd9Sstevel@tonic-gate 		pctx_end_syscalls(pctx);
424*7c478bd9Sstevel@tonic-gate 		return (ret);
425*7c478bd9Sstevel@tonic-gate 	}
426*7c478bd9Sstevel@tonic-gate 
427*7c478bd9Sstevel@tonic-gate 	(void) snprintf(lstatus, sizeof (lstatus),
428*7c478bd9Sstevel@tonic-gate 	    "/proc/%d/lstatus", (int)pstatus->pr_pid);
429*7c478bd9Sstevel@tonic-gate 
430*7c478bd9Sstevel@tonic-gate 	if ((fd = open(lstatus, O_RDONLY)) < 0 ||
431*7c478bd9Sstevel@tonic-gate 	    fstat(fd, &statb) != 0) {
432*7c478bd9Sstevel@tonic-gate 		if (fd >= 0)
433*7c478bd9Sstevel@tonic-gate 			(void) close(fd);
434*7c478bd9Sstevel@tonic-gate 		return (-1);
435*7c478bd9Sstevel@tonic-gate 	}
436*7c478bd9Sstevel@tonic-gate 
437*7c478bd9Sstevel@tonic-gate 	prh = malloc(statb.st_size);
438*7c478bd9Sstevel@tonic-gate 	if (read(fd, prh, statb.st_size) <
439*7c478bd9Sstevel@tonic-gate 	    sizeof (prheader_t) + sizeof (lwpstatus_t)) {
440*7c478bd9Sstevel@tonic-gate 		(void) close(fd);
441*7c478bd9Sstevel@tonic-gate 		free(prh);
442*7c478bd9Sstevel@tonic-gate 		return (-1);
443*7c478bd9Sstevel@tonic-gate 	}
444*7c478bd9Sstevel@tonic-gate 	(void) close(fd);
445*7c478bd9Sstevel@tonic-gate 
446*7c478bd9Sstevel@tonic-gate 	/* LINTED pointer cast may result in improper alignment */
447*7c478bd9Sstevel@tonic-gate 	lwps = (lwpstatus_t *)(prh + 1);
448*7c478bd9Sstevel@tonic-gate 	pctx_begin_syscalls(pctx);
449*7c478bd9Sstevel@tonic-gate 	for (nlwp = prh->pr_nent; nlwp > 0; nlwp--) {
450*7c478bd9Sstevel@tonic-gate 		if (action(pctx,
451*7c478bd9Sstevel@tonic-gate 		    pstatus->pr_pid, lwps->pr_lwpid, pctx->uarg) != 0)
452*7c478bd9Sstevel@tonic-gate 			ret = -1;
453*7c478bd9Sstevel@tonic-gate 		/* LINTED pointer cast may result in improper alignment */
454*7c478bd9Sstevel@tonic-gate 		lwps = (lwpstatus_t *)((char *)lwps + prh->pr_entsize);
455*7c478bd9Sstevel@tonic-gate 	}
456*7c478bd9Sstevel@tonic-gate 	pctx_end_syscalls(pctx);
457*7c478bd9Sstevel@tonic-gate 	free(prh);
458*7c478bd9Sstevel@tonic-gate 	return (ret);
459*7c478bd9Sstevel@tonic-gate }
460*7c478bd9Sstevel@tonic-gate 
461*7c478bd9Sstevel@tonic-gate /*
462*7c478bd9Sstevel@tonic-gate  * Free any associated state, but leave the process stopped if it
463*7c478bd9Sstevel@tonic-gate  * is still under our control.  (If it isn't under our control,
464*7c478bd9Sstevel@tonic-gate  * it should just run to completion when we do our last close)
465*7c478bd9Sstevel@tonic-gate  */
466*7c478bd9Sstevel@tonic-gate static void
467*7c478bd9Sstevel@tonic-gate pctx_free(pctx_t *pctx)
468*7c478bd9Sstevel@tonic-gate {
469*7c478bd9Sstevel@tonic-gate 	if (pctx->cpc != NULL && pctx_cpc_callback != NULL)
470*7c478bd9Sstevel@tonic-gate 		(*pctx_cpc_callback)(pctx->cpc, pctx);
471*7c478bd9Sstevel@tonic-gate 	if (pctx->Pr) {
472*7c478bd9Sstevel@tonic-gate 		Pfree(pctx->Pr);
473*7c478bd9Sstevel@tonic-gate 		pctx->Pr = NULL;
474*7c478bd9Sstevel@tonic-gate 	}
475*7c478bd9Sstevel@tonic-gate 	pctx->errfn = pctx_default_errfn;
476*7c478bd9Sstevel@tonic-gate }
477*7c478bd9Sstevel@tonic-gate 
478*7c478bd9Sstevel@tonic-gate /*
479*7c478bd9Sstevel@tonic-gate  * Completely release the process from our control and discard all our state
480*7c478bd9Sstevel@tonic-gate  */
481*7c478bd9Sstevel@tonic-gate void
482*7c478bd9Sstevel@tonic-gate pctx_release(pctx_t *pctx)
483*7c478bd9Sstevel@tonic-gate {
484*7c478bd9Sstevel@tonic-gate 	if (pctx->Pr) {
485*7c478bd9Sstevel@tonic-gate 		Prelease(pctx->Pr, PRELEASE_CLEAR);
486*7c478bd9Sstevel@tonic-gate 		pctx->Pr = NULL;
487*7c478bd9Sstevel@tonic-gate 	}
488*7c478bd9Sstevel@tonic-gate 	pctx_free(pctx);
489*7c478bd9Sstevel@tonic-gate 	bzero(pctx, sizeof (*pctx));
490*7c478bd9Sstevel@tonic-gate 	free(pctx);
491*7c478bd9Sstevel@tonic-gate }
492*7c478bd9Sstevel@tonic-gate 
493*7c478bd9Sstevel@tonic-gate static void
494*7c478bd9Sstevel@tonic-gate msincr(struct timeval *tv, uint_t msec)
495*7c478bd9Sstevel@tonic-gate {
496*7c478bd9Sstevel@tonic-gate 	tv->tv_sec += msec / MILLISEC;
497*7c478bd9Sstevel@tonic-gate 	tv->tv_usec += (msec % MILLISEC) * MILLISEC;
498*7c478bd9Sstevel@tonic-gate 	if (tv->tv_usec > MICROSEC) {
499*7c478bd9Sstevel@tonic-gate 		tv->tv_sec++;
500*7c478bd9Sstevel@tonic-gate 		tv->tv_usec -= MICROSEC;
501*7c478bd9Sstevel@tonic-gate 	}
502*7c478bd9Sstevel@tonic-gate }
503*7c478bd9Sstevel@tonic-gate 
504*7c478bd9Sstevel@tonic-gate static uint_t
505*7c478bd9Sstevel@tonic-gate msdiff(struct timeval *tva, struct timeval *tvb)
506*7c478bd9Sstevel@tonic-gate {
507*7c478bd9Sstevel@tonic-gate 	time_t sdiff = tva->tv_sec - tvb->tv_sec;
508*7c478bd9Sstevel@tonic-gate 	suseconds_t udiff = tva->tv_usec - tvb->tv_usec;
509*7c478bd9Sstevel@tonic-gate 
510*7c478bd9Sstevel@tonic-gate 	if (sdiff < 0)
511*7c478bd9Sstevel@tonic-gate 		return (0);
512*7c478bd9Sstevel@tonic-gate 	if (udiff < 0) {
513*7c478bd9Sstevel@tonic-gate 		udiff += MICROSEC;
514*7c478bd9Sstevel@tonic-gate 		sdiff--;
515*7c478bd9Sstevel@tonic-gate 	}
516*7c478bd9Sstevel@tonic-gate 	if (sdiff < 0)
517*7c478bd9Sstevel@tonic-gate 		return (0);
518*7c478bd9Sstevel@tonic-gate 	if (sdiff >= (INT_MAX / MILLISEC))
519*7c478bd9Sstevel@tonic-gate 		return ((uint_t)INT_MAX);
520*7c478bd9Sstevel@tonic-gate 	return ((uint_t)(sdiff * MILLISEC + udiff / MILLISEC));
521*7c478bd9Sstevel@tonic-gate }
522*7c478bd9Sstevel@tonic-gate 
523*7c478bd9Sstevel@tonic-gate int
524*7c478bd9Sstevel@tonic-gate pctx_run(
525*7c478bd9Sstevel@tonic-gate 	pctx_t *pctx,
526*7c478bd9Sstevel@tonic-gate 	uint_t msec,
527*7c478bd9Sstevel@tonic-gate 	uint_t nsamples,
528*7c478bd9Sstevel@tonic-gate 	int (*tick)(pctx_t *, pid_t, id_t, void *))
529*7c478bd9Sstevel@tonic-gate {
530*7c478bd9Sstevel@tonic-gate 	static const char fn[] = "run";
531*7c478bd9Sstevel@tonic-gate 	struct timeval tvgoal, tvnow;
532*7c478bd9Sstevel@tonic-gate 	uint_t mswait = 0;
533*7c478bd9Sstevel@tonic-gate 	int running = 1;
534*7c478bd9Sstevel@tonic-gate 	const pstatus_t *pstatus;
535*7c478bd9Sstevel@tonic-gate 	psinfo_t psinfo;
536*7c478bd9Sstevel@tonic-gate 	void (*sigsaved)();
537*7c478bd9Sstevel@tonic-gate 	id_t lwpid;
538*7c478bd9Sstevel@tonic-gate 	pid_t pid = Pstatus(pctx->Pr)->pr_pid;
539*7c478bd9Sstevel@tonic-gate 	int pstate;
540*7c478bd9Sstevel@tonic-gate 
541*7c478bd9Sstevel@tonic-gate 	if (msec == 0)
542*7c478bd9Sstevel@tonic-gate 		nsamples = 0;
543*7c478bd9Sstevel@tonic-gate 	if (nsamples == 0)
544*7c478bd9Sstevel@tonic-gate 		nsamples = UINT_MAX;
545*7c478bd9Sstevel@tonic-gate 
546*7c478bd9Sstevel@tonic-gate 	/*
547*7c478bd9Sstevel@tonic-gate 	 * Casually discard any knowledge of the children we create
548*7c478bd9Sstevel@tonic-gate 	 */
549*7c478bd9Sstevel@tonic-gate 	sigsaved = signal(SIGCHLD, SIG_IGN);
550*7c478bd9Sstevel@tonic-gate 
551*7c478bd9Sstevel@tonic-gate 	/*
552*7c478bd9Sstevel@tonic-gate 	 * Since we've just "discovered" this process which might have
553*7c478bd9Sstevel@tonic-gate 	 * been running for weeks, deliver some init_lwp events so
554*7c478bd9Sstevel@tonic-gate 	 * that our caller gets a handle on the process.
555*7c478bd9Sstevel@tonic-gate 	 */
556*7c478bd9Sstevel@tonic-gate 	if (pctx_lwpiterate(pctx, pctx->init_lwp) != 0) {
557*7c478bd9Sstevel@tonic-gate 		if (pctx->verbose)
558*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn,
559*7c478bd9Sstevel@tonic-gate 			    gettext("%d: lwp discovery failed\n"), (int)pid);
560*7c478bd9Sstevel@tonic-gate 		goto bailout;
561*7c478bd9Sstevel@tonic-gate 	}
562*7c478bd9Sstevel@tonic-gate 
563*7c478bd9Sstevel@tonic-gate 	if (msec != 0) {
564*7c478bd9Sstevel@tonic-gate 		/*
565*7c478bd9Sstevel@tonic-gate 		 * tvgoal represents the time at which the sample
566*7c478bd9Sstevel@tonic-gate 		 * should next be taken.
567*7c478bd9Sstevel@tonic-gate 		 */
568*7c478bd9Sstevel@tonic-gate 		(void) gettimeofday(&tvgoal, 0);
569*7c478bd9Sstevel@tonic-gate 		msincr(&tvgoal, msec);
570*7c478bd9Sstevel@tonic-gate 	}
571*7c478bd9Sstevel@tonic-gate 
572*7c478bd9Sstevel@tonic-gate 	while (running) {
573*7c478bd9Sstevel@tonic-gate 
574*7c478bd9Sstevel@tonic-gate 		if (Psetrun(pctx->Pr, 0, 0) != 0) {
575*7c478bd9Sstevel@tonic-gate 			if (pctx->verbose)
576*7c478bd9Sstevel@tonic-gate 				pctx_error(pctx, fn,
577*7c478bd9Sstevel@tonic-gate 				    gettext("%d: Psetrun\n"), (int)pid);
578*7c478bd9Sstevel@tonic-gate 			break;
579*7c478bd9Sstevel@tonic-gate 		}
580*7c478bd9Sstevel@tonic-gate 
581*7c478bd9Sstevel@tonic-gate 		if (msec != 0) {
582*7c478bd9Sstevel@tonic-gate 			/*
583*7c478bd9Sstevel@tonic-gate 			 * This timing loop attempts to estimate the number
584*7c478bd9Sstevel@tonic-gate 			 * of milliseconds between our "goal" time (when
585*7c478bd9Sstevel@tonic-gate 			 * we should stop the process and run the tick
586*7c478bd9Sstevel@tonic-gate 			 * routine) and the current time.
587*7c478bd9Sstevel@tonic-gate 			 *
588*7c478bd9Sstevel@tonic-gate 			 * If we ever find ourselves running behind i.e. we
589*7c478bd9Sstevel@tonic-gate 			 * missed our goal, then we skip ahead to the next
590*7c478bd9Sstevel@tonic-gate 			 * goal instead.
591*7c478bd9Sstevel@tonic-gate 			 */
592*7c478bd9Sstevel@tonic-gate 			do {
593*7c478bd9Sstevel@tonic-gate 				(void) gettimeofday(&tvnow, 0);
594*7c478bd9Sstevel@tonic-gate 				if ((mswait = msdiff(&tvgoal, &tvnow)) == 0) {
595*7c478bd9Sstevel@tonic-gate 					msincr(&tvgoal, msec);
596*7c478bd9Sstevel@tonic-gate 					/*
597*7c478bd9Sstevel@tonic-gate 					 * Skip ahead to the next goal, unless
598*7c478bd9Sstevel@tonic-gate 					 * there is only one more sample left
599*7c478bd9Sstevel@tonic-gate 					 * to take.
600*7c478bd9Sstevel@tonic-gate 					 */
601*7c478bd9Sstevel@tonic-gate 					if (nsamples != 1)
602*7c478bd9Sstevel@tonic-gate 						nsamples--;
603*7c478bd9Sstevel@tonic-gate 				}
604*7c478bd9Sstevel@tonic-gate 			} while (mswait == 0);
605*7c478bd9Sstevel@tonic-gate 		}
606*7c478bd9Sstevel@tonic-gate 
607*7c478bd9Sstevel@tonic-gate 		(void) Pwait(pctx->Pr, mswait);
608*7c478bd9Sstevel@tonic-gate 
609*7c478bd9Sstevel@tonic-gate checkstate:
610*7c478bd9Sstevel@tonic-gate 		switch (pstate = Pstate(pctx->Pr)) {
611*7c478bd9Sstevel@tonic-gate 		case PS_RUN:
612*7c478bd9Sstevel@tonic-gate 			/*
613*7c478bd9Sstevel@tonic-gate 			 * Try again, but wait for up to 5 seconds.
614*7c478bd9Sstevel@tonic-gate 			 */
615*7c478bd9Sstevel@tonic-gate 			if (Pstop(pctx->Pr, 5 * MILLISEC) == -1 ||
616*7c478bd9Sstevel@tonic-gate 			    (pstate = Pstate(pctx->Pr)) != PS_STOP) {
617*7c478bd9Sstevel@tonic-gate 				pctx_error(pctx, fn,
618*7c478bd9Sstevel@tonic-gate 				    gettext("%d: won't stop\n"), (int)pid);
619*7c478bd9Sstevel@tonic-gate 			}
620*7c478bd9Sstevel@tonic-gate 			break;
621*7c478bd9Sstevel@tonic-gate 		case PS_STOP:
622*7c478bd9Sstevel@tonic-gate 			break;
623*7c478bd9Sstevel@tonic-gate 		case PS_LOST:
624*7c478bd9Sstevel@tonic-gate 			/*
625*7c478bd9Sstevel@tonic-gate 			 * Lost control - probably execed a setuid/setgid
626*7c478bd9Sstevel@tonic-gate 			 * executable.  Try and get control back again,
627*7c478bd9Sstevel@tonic-gate 			 * else bail ..
628*7c478bd9Sstevel@tonic-gate 			 */
629*7c478bd9Sstevel@tonic-gate 			(void) Preopen(pctx->Pr);
630*7c478bd9Sstevel@tonic-gate 			if ((pstate = Pstate(pctx->Pr)) != PS_LOST)
631*7c478bd9Sstevel@tonic-gate 				goto checkstate;
632*7c478bd9Sstevel@tonic-gate 			pctx_error(pctx, fn,
633*7c478bd9Sstevel@tonic-gate 			    gettext("%d: execed a program that cannot "
634*7c478bd9Sstevel@tonic-gate 			    "be tracked\n"), (int)pid);
635*7c478bd9Sstevel@tonic-gate 			running = 0;
636*7c478bd9Sstevel@tonic-gate 			break;
637*7c478bd9Sstevel@tonic-gate 		case PS_UNDEAD:
638*7c478bd9Sstevel@tonic-gate 		case PS_DEAD:
639*7c478bd9Sstevel@tonic-gate 			if (pctx->verbose)
640*7c478bd9Sstevel@tonic-gate 				pctx_error(pctx, fn,
641*7c478bd9Sstevel@tonic-gate 				    gettext("%d: process terminated\n"),
642*7c478bd9Sstevel@tonic-gate 				    (int)pid);
643*7c478bd9Sstevel@tonic-gate 			running = 0;
644*7c478bd9Sstevel@tonic-gate 			break;
645*7c478bd9Sstevel@tonic-gate 		default:
646*7c478bd9Sstevel@tonic-gate 			if (pctx->verbose)
647*7c478bd9Sstevel@tonic-gate 				pctx_error(pctx, fn,
648*7c478bd9Sstevel@tonic-gate 				    gettext("%d: process state 0x%x?\n"),
649*7c478bd9Sstevel@tonic-gate 				    (int)pid, pstate);
650*7c478bd9Sstevel@tonic-gate 			break;
651*7c478bd9Sstevel@tonic-gate 		}
652*7c478bd9Sstevel@tonic-gate 
653*7c478bd9Sstevel@tonic-gate 		if (pstate != PS_STOP)
654*7c478bd9Sstevel@tonic-gate 			break;
655*7c478bd9Sstevel@tonic-gate 
656*7c478bd9Sstevel@tonic-gate 		pstatus = Pstatus(pctx->Pr);
657*7c478bd9Sstevel@tonic-gate 		lwpid = pstatus->pr_lwp.pr_lwpid;
658*7c478bd9Sstevel@tonic-gate 		switch (pstatus->pr_lwp.pr_why) {
659*7c478bd9Sstevel@tonic-gate 		case PR_REQUESTED:
660*7c478bd9Sstevel@tonic-gate 			msincr(&tvgoal, msec);
661*7c478bd9Sstevel@tonic-gate 			if (pstatus->pr_flags & PR_VFORKP) {
662*7c478bd9Sstevel@tonic-gate 				/*
663*7c478bd9Sstevel@tonic-gate 				 * The process is in a vfork stupor until
664*7c478bd9Sstevel@tonic-gate 				 * its child releases it via an exec.
665*7c478bd9Sstevel@tonic-gate 				 * Don't sample it while it's in this state
666*7c478bd9Sstevel@tonic-gate 				 * - we won't be able to create the agent.
667*7c478bd9Sstevel@tonic-gate 				 */
668*7c478bd9Sstevel@tonic-gate 				break;
669*7c478bd9Sstevel@tonic-gate 			}
670*7c478bd9Sstevel@tonic-gate 			if (pctx_lwpiterate(pctx, tick) != 0)
671*7c478bd9Sstevel@tonic-gate 				running = 0;
672*7c478bd9Sstevel@tonic-gate 			if (--nsamples == 0)
673*7c478bd9Sstevel@tonic-gate 				running = 0;
674*7c478bd9Sstevel@tonic-gate 			break;
675*7c478bd9Sstevel@tonic-gate 		case PR_SYSENTRY:
676*7c478bd9Sstevel@tonic-gate 			switch (pstatus->pr_lwp.pr_what) {
677*7c478bd9Sstevel@tonic-gate 			case SYS_lwp_exit:
678*7c478bd9Sstevel@tonic-gate 				pctx_begin_syscalls(pctx);
679*7c478bd9Sstevel@tonic-gate 				(void) pctx->fini_lwp(pctx,
680*7c478bd9Sstevel@tonic-gate 				    pid, lwpid, pctx->uarg);
681*7c478bd9Sstevel@tonic-gate 				(void) pctx->lwp_exit(pctx,
682*7c478bd9Sstevel@tonic-gate 				    pid, lwpid, pctx->uarg);
683*7c478bd9Sstevel@tonic-gate 				pctx_end_syscalls(pctx);
684*7c478bd9Sstevel@tonic-gate 				break;
685*7c478bd9Sstevel@tonic-gate 			case SYS_exit:
686*7c478bd9Sstevel@tonic-gate 				(void) pctx_lwpiterate(pctx, pctx->fini_lwp);
687*7c478bd9Sstevel@tonic-gate 				pctx->exit(pctx, pid, lwpid,
688*7c478bd9Sstevel@tonic-gate 				    (int)pstatus->pr_lwp.pr_sysarg[0],
689*7c478bd9Sstevel@tonic-gate 				    pctx->uarg);
690*7c478bd9Sstevel@tonic-gate 				running = 0;
691*7c478bd9Sstevel@tonic-gate 				break;
692*7c478bd9Sstevel@tonic-gate 			case SYS_exec:
693*7c478bd9Sstevel@tonic-gate 			case SYS_execve:
694*7c478bd9Sstevel@tonic-gate 				(void) pctx_lwpiterate(pctx, pctx->fini_lwp);
695*7c478bd9Sstevel@tonic-gate 				break;
696*7c478bd9Sstevel@tonic-gate 			default:
697*7c478bd9Sstevel@tonic-gate 				pctx_error(pctx, fn,
698*7c478bd9Sstevel@tonic-gate 				    "warning - pid %d sysentry(%d)\n",
699*7c478bd9Sstevel@tonic-gate 				    (int)pid, pstatus->pr_lwp.pr_what);
700*7c478bd9Sstevel@tonic-gate 				break;
701*7c478bd9Sstevel@tonic-gate 			}
702*7c478bd9Sstevel@tonic-gate 			break;
703*7c478bd9Sstevel@tonic-gate 		case PR_SYSEXIT:
704*7c478bd9Sstevel@tonic-gate 			switch (pstatus->pr_lwp.pr_what) {
705*7c478bd9Sstevel@tonic-gate 			case SYS_exec:
706*7c478bd9Sstevel@tonic-gate 			case SYS_execve:
707*7c478bd9Sstevel@tonic-gate 				if (pstatus->pr_lwp.pr_errno) {
708*7c478bd9Sstevel@tonic-gate 					/*
709*7c478bd9Sstevel@tonic-gate 					 * The exec failed completely.
710*7c478bd9Sstevel@tonic-gate 					 * Reinstate the lwps we fini'd
711*7c478bd9Sstevel@tonic-gate 					 * at exec entrance
712*7c478bd9Sstevel@tonic-gate 					 */
713*7c478bd9Sstevel@tonic-gate 					running = pctx_lwpiterate(pctx,
714*7c478bd9Sstevel@tonic-gate 					    pctx->init_lwp) == 0;
715*7c478bd9Sstevel@tonic-gate 					break;
716*7c478bd9Sstevel@tonic-gate 				}
717*7c478bd9Sstevel@tonic-gate 				if (pctx->exec == (pctx_sysc_execfn_t *)
718*7c478bd9Sstevel@tonic-gate 				    default_int) {
719*7c478bd9Sstevel@tonic-gate 					running = 0;
720*7c478bd9Sstevel@tonic-gate 					break;
721*7c478bd9Sstevel@tonic-gate 				}
722*7c478bd9Sstevel@tonic-gate 				(void) memcpy(&psinfo,
723*7c478bd9Sstevel@tonic-gate 				    Ppsinfo(pctx->Pr), sizeof (psinfo));
724*7c478bd9Sstevel@tonic-gate 				proc_unctrl_psinfo(&psinfo);
725*7c478bd9Sstevel@tonic-gate 				pctx_begin_syscalls(pctx);
726*7c478bd9Sstevel@tonic-gate 				running = pctx->exec(pctx, pid,
727*7c478bd9Sstevel@tonic-gate 				    lwpid, psinfo.pr_psargs, pctx->uarg) == 0;
728*7c478bd9Sstevel@tonic-gate 				running = running && pctx->init_lwp(pctx,
729*7c478bd9Sstevel@tonic-gate 				    pid, 1, pctx->uarg) == 0;
730*7c478bd9Sstevel@tonic-gate 				pctx_end_syscalls(pctx);
731*7c478bd9Sstevel@tonic-gate 				break;
732*7c478bd9Sstevel@tonic-gate 			case SYS_lwp_create:
733*7c478bd9Sstevel@tonic-gate 				if (pstatus->pr_lwp.pr_errno ||
734*7c478bd9Sstevel@tonic-gate 				    pstatus->pr_lwp.pr_rval1)
735*7c478bd9Sstevel@tonic-gate 					break;
736*7c478bd9Sstevel@tonic-gate 				pctx_begin_syscalls(pctx);
737*7c478bd9Sstevel@tonic-gate 				running = pctx->init_lwp(pctx,
738*7c478bd9Sstevel@tonic-gate 				    pid, lwpid, pctx->uarg) == 0;
739*7c478bd9Sstevel@tonic-gate 				running = running && pctx->lwp_create(pctx,
740*7c478bd9Sstevel@tonic-gate 				    pid, lwpid, pctx->uarg) == 0;
741*7c478bd9Sstevel@tonic-gate 				pctx_end_syscalls(pctx);
742*7c478bd9Sstevel@tonic-gate 				break;
743*7c478bd9Sstevel@tonic-gate 			case SYS_forkall:
744*7c478bd9Sstevel@tonic-gate 			case SYS_vfork:
745*7c478bd9Sstevel@tonic-gate 			case SYS_fork1:
746*7c478bd9Sstevel@tonic-gate 				if (pstatus->pr_lwp.pr_errno)
747*7c478bd9Sstevel@tonic-gate 					break;
748*7c478bd9Sstevel@tonic-gate 				(void) fflush(NULL);
749*7c478bd9Sstevel@tonic-gate 				switch (fork1()) {
750*7c478bd9Sstevel@tonic-gate 					pid_t ppid;
751*7c478bd9Sstevel@tonic-gate 					int wascreated;
752*7c478bd9Sstevel@tonic-gate 					pctx_sysc_forkfn_t *forkfn;
753*7c478bd9Sstevel@tonic-gate 				case 0:
754*7c478bd9Sstevel@tonic-gate 					ppid = pid;
755*7c478bd9Sstevel@tonic-gate 					pid = pstatus->pr_lwp.pr_rval1;
756*7c478bd9Sstevel@tonic-gate 					wascreated = pctx->created;
757*7c478bd9Sstevel@tonic-gate 					forkfn = pctx->fork;
758*7c478bd9Sstevel@tonic-gate 					pctx_free(pctx);
759*7c478bd9Sstevel@tonic-gate 					pctx = pctx_capture(pid, pctx->uarg,
760*7c478bd9Sstevel@tonic-gate 					    pctx->verbose, pctx->errfn);
761*7c478bd9Sstevel@tonic-gate 					if (pctx != NULL) {
762*7c478bd9Sstevel@tonic-gate 						if (wascreated) {
763*7c478bd9Sstevel@tonic-gate 							/*
764*7c478bd9Sstevel@tonic-gate 							 * Set kill on last
765*7c478bd9Sstevel@tonic-gate 							 * close so -all-
766*7c478bd9Sstevel@tonic-gate 							 * children die.
767*7c478bd9Sstevel@tonic-gate 							 */
768*7c478bd9Sstevel@tonic-gate 							pctx->created = 1;
769*7c478bd9Sstevel@tonic-gate 							(void) Psetflags(
770*7c478bd9Sstevel@tonic-gate 							    pctx->Pr, PR_KLC);
771*7c478bd9Sstevel@tonic-gate 						}
772*7c478bd9Sstevel@tonic-gate 						(*forkfn)(pctx, ppid, pid,
773*7c478bd9Sstevel@tonic-gate 						    lwpid, pctx->uarg);
774*7c478bd9Sstevel@tonic-gate 						pctx_release(pctx);
775*7c478bd9Sstevel@tonic-gate 					}
776*7c478bd9Sstevel@tonic-gate 					_exit(0);
777*7c478bd9Sstevel@tonic-gate 					/*NOTREACHED*/
778*7c478bd9Sstevel@tonic-gate 				case -1:
779*7c478bd9Sstevel@tonic-gate 					pctx_error(pctx, fn,
780*7c478bd9Sstevel@tonic-gate 					    "cannot follow pid %d: %s\n",
781*7c478bd9Sstevel@tonic-gate 					    (int)pstatus->pr_lwp.pr_rval1,
782*7c478bd9Sstevel@tonic-gate 					    strerror(errno));
783*7c478bd9Sstevel@tonic-gate 					break;
784*7c478bd9Sstevel@tonic-gate 				default:
785*7c478bd9Sstevel@tonic-gate 					break;
786*7c478bd9Sstevel@tonic-gate 				}
787*7c478bd9Sstevel@tonic-gate 				break;
788*7c478bd9Sstevel@tonic-gate 			default:
789*7c478bd9Sstevel@tonic-gate 				pctx_error(pctx, fn, gettext(
790*7c478bd9Sstevel@tonic-gate 				    "warning - pid %d sysexit(%d)\n"),
791*7c478bd9Sstevel@tonic-gate 				    (int)pid, pstatus->pr_lwp.pr_what);
792*7c478bd9Sstevel@tonic-gate 				break;
793*7c478bd9Sstevel@tonic-gate 			}
794*7c478bd9Sstevel@tonic-gate 			break;
795*7c478bd9Sstevel@tonic-gate 		case PR_SIGNALLED:
796*7c478bd9Sstevel@tonic-gate 			if (pctx->verbose)
797*7c478bd9Sstevel@tonic-gate 				pctx_error(pctx, fn,
798*7c478bd9Sstevel@tonic-gate 				    gettext("pid %d - signalled\n"), (int)pid);
799*7c478bd9Sstevel@tonic-gate 			break;
800*7c478bd9Sstevel@tonic-gate 		case PR_JOBCONTROL:
801*7c478bd9Sstevel@tonic-gate 			if (pctx->verbose)
802*7c478bd9Sstevel@tonic-gate 				pctx_error(pctx, fn,
803*7c478bd9Sstevel@tonic-gate 				    gettext("pid %d - job control stop\n"),
804*7c478bd9Sstevel@tonic-gate 				    (int)pid);
805*7c478bd9Sstevel@tonic-gate 			running = 0;
806*7c478bd9Sstevel@tonic-gate 			break;
807*7c478bd9Sstevel@tonic-gate 		case PR_FAULTED:
808*7c478bd9Sstevel@tonic-gate 			if (pctx->verbose)
809*7c478bd9Sstevel@tonic-gate 				pctx_error(pctx, fn,
810*7c478bd9Sstevel@tonic-gate 				    gettext("pid %d - faulted\n"), (int)pid);
811*7c478bd9Sstevel@tonic-gate 			break;
812*7c478bd9Sstevel@tonic-gate 		case PR_SUSPENDED:
813*7c478bd9Sstevel@tonic-gate 			if (pctx->verbose)
814*7c478bd9Sstevel@tonic-gate 				pctx_error(pctx, fn,
815*7c478bd9Sstevel@tonic-gate 				    gettext("pid %d - suspended\n"), (int)pid);
816*7c478bd9Sstevel@tonic-gate 			break;
817*7c478bd9Sstevel@tonic-gate 		case PR_CHECKPOINT:
818*7c478bd9Sstevel@tonic-gate 			if (pctx->verbose)
819*7c478bd9Sstevel@tonic-gate 				pctx_error(pctx, fn,
820*7c478bd9Sstevel@tonic-gate 				    gettext("pid %d - checkpoint\n"),
821*7c478bd9Sstevel@tonic-gate 				    (int)pid);
822*7c478bd9Sstevel@tonic-gate 			break;
823*7c478bd9Sstevel@tonic-gate 		default:
824*7c478bd9Sstevel@tonic-gate 			if (pctx->verbose)
825*7c478bd9Sstevel@tonic-gate 				pctx_error(pctx, fn,
826*7c478bd9Sstevel@tonic-gate 				    gettext("pid %d - reason %d\n"),
827*7c478bd9Sstevel@tonic-gate 				    (int)pid, pstatus->pr_lwp.pr_why);
828*7c478bd9Sstevel@tonic-gate 			running = 0;
829*7c478bd9Sstevel@tonic-gate 			break;
830*7c478bd9Sstevel@tonic-gate 		}
831*7c478bd9Sstevel@tonic-gate 	}
832*7c478bd9Sstevel@tonic-gate 
833*7c478bd9Sstevel@tonic-gate bailout:
834*7c478bd9Sstevel@tonic-gate 	(void) signal(SIGCHLD, sigsaved);
835*7c478bd9Sstevel@tonic-gate 	if (!running)
836*7c478bd9Sstevel@tonic-gate 		return (0);
837*7c478bd9Sstevel@tonic-gate 
838*7c478bd9Sstevel@tonic-gate 	pctx_error(pctx, fn, gettext("lost control of pid %d\n"), (int)pid);
839*7c478bd9Sstevel@tonic-gate 	pctx_free(pctx);
840*7c478bd9Sstevel@tonic-gate 	return (-1);
841*7c478bd9Sstevel@tonic-gate }
842*7c478bd9Sstevel@tonic-gate 
843*7c478bd9Sstevel@tonic-gate /*
844*7c478bd9Sstevel@tonic-gate  * Execute the private 'cpc' system call in the context of the
845*7c478bd9Sstevel@tonic-gate  * controlled process.
846*7c478bd9Sstevel@tonic-gate  */
847*7c478bd9Sstevel@tonic-gate int
848*7c478bd9Sstevel@tonic-gate __pctx_cpc(pctx_t *pctx, cpc_t *cpc,
849*7c478bd9Sstevel@tonic-gate     int cmd, id_t lwpid, void *data1, void *data2, void *data3, int bufsize)
850*7c478bd9Sstevel@tonic-gate {
851*7c478bd9Sstevel@tonic-gate 	sysret_t rval;
852*7c478bd9Sstevel@tonic-gate 	argdes_t argd[5];
853*7c478bd9Sstevel@tonic-gate 	argdes_t *adp = &argd[0];
854*7c478bd9Sstevel@tonic-gate 	int error;
855*7c478bd9Sstevel@tonic-gate 
856*7c478bd9Sstevel@tonic-gate 	/*
857*7c478bd9Sstevel@tonic-gate 	 * Keep track of the relationship between cpc_t and pctx_t here.
858*7c478bd9Sstevel@tonic-gate 	 * We store the last cpc_t used by libpctx, so that when this pctx is
859*7c478bd9Sstevel@tonic-gate 	 * destroyed, libpctx can notify libcpc.
860*7c478bd9Sstevel@tonic-gate 	 */
861*7c478bd9Sstevel@tonic-gate 	if (pctx->cpc != NULL && pctx->cpc != cpc && pctx_cpc_callback != NULL)
862*7c478bd9Sstevel@tonic-gate 		(*pctx_cpc_callback)(pctx->cpc, pctx);
863*7c478bd9Sstevel@tonic-gate 	pctx->cpc = cpc;
864*7c478bd9Sstevel@tonic-gate 
865*7c478bd9Sstevel@tonic-gate 	/*
866*7c478bd9Sstevel@tonic-gate 	 * cmd and lwpid are passed in by value no matter what the command is.
867*7c478bd9Sstevel@tonic-gate 	 */
868*7c478bd9Sstevel@tonic-gate 	adp->arg_value = cmd;
869*7c478bd9Sstevel@tonic-gate 	adp->arg_object = NULL;
870*7c478bd9Sstevel@tonic-gate 	adp->arg_type = AT_BYVAL;
871*7c478bd9Sstevel@tonic-gate 	adp->arg_inout = AI_INPUT;
872*7c478bd9Sstevel@tonic-gate 	adp->arg_size = 0;
873*7c478bd9Sstevel@tonic-gate 	adp++;
874*7c478bd9Sstevel@tonic-gate 
875*7c478bd9Sstevel@tonic-gate 	adp->arg_value = lwpid;
876*7c478bd9Sstevel@tonic-gate 	adp->arg_object = NULL;
877*7c478bd9Sstevel@tonic-gate 	adp->arg_type = AT_BYVAL;
878*7c478bd9Sstevel@tonic-gate 	adp->arg_inout = AI_INPUT;
879*7c478bd9Sstevel@tonic-gate 	adp->arg_size = 0;
880*7c478bd9Sstevel@tonic-gate 	adp++;
881*7c478bd9Sstevel@tonic-gate 
882*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
883*7c478bd9Sstevel@tonic-gate 	case CPC_BIND:
884*7c478bd9Sstevel@tonic-gate 		adp->arg_value = 0;
885*7c478bd9Sstevel@tonic-gate 		adp->arg_object = data1;
886*7c478bd9Sstevel@tonic-gate 		adp->arg_type = AT_BYREF;
887*7c478bd9Sstevel@tonic-gate 		adp->arg_inout = AI_INPUT;
888*7c478bd9Sstevel@tonic-gate 		adp->arg_size = (size_t)data2;
889*7c478bd9Sstevel@tonic-gate 		adp++;
890*7c478bd9Sstevel@tonic-gate 
891*7c478bd9Sstevel@tonic-gate 		adp->arg_value = (size_t)data2;
892*7c478bd9Sstevel@tonic-gate 		adp->arg_object = NULL;
893*7c478bd9Sstevel@tonic-gate 		adp->arg_type = AT_BYVAL;
894*7c478bd9Sstevel@tonic-gate 		adp->arg_inout = AI_INPUT;
895*7c478bd9Sstevel@tonic-gate 		adp->arg_size = 0;
896*7c478bd9Sstevel@tonic-gate 		adp++;
897*7c478bd9Sstevel@tonic-gate 
898*7c478bd9Sstevel@tonic-gate 		adp->arg_value = 0;
899*7c478bd9Sstevel@tonic-gate 		adp->arg_object = data3;
900*7c478bd9Sstevel@tonic-gate 		adp->arg_type = AT_BYREF;
901*7c478bd9Sstevel@tonic-gate 		adp->arg_inout = AI_INOUT;
902*7c478bd9Sstevel@tonic-gate 		adp->arg_size = sizeof (int);
903*7c478bd9Sstevel@tonic-gate 
904*7c478bd9Sstevel@tonic-gate 		break;
905*7c478bd9Sstevel@tonic-gate 	case CPC_SAMPLE:
906*7c478bd9Sstevel@tonic-gate 		adp->arg_value = 0;
907*7c478bd9Sstevel@tonic-gate 		adp->arg_object = data1;
908*7c478bd9Sstevel@tonic-gate 		adp->arg_type = AT_BYREF;
909*7c478bd9Sstevel@tonic-gate 		adp->arg_inout = AI_OUTPUT;
910*7c478bd9Sstevel@tonic-gate 		adp->arg_size = bufsize;
911*7c478bd9Sstevel@tonic-gate 		adp++;
912*7c478bd9Sstevel@tonic-gate 
913*7c478bd9Sstevel@tonic-gate 		adp->arg_value = 0;
914*7c478bd9Sstevel@tonic-gate 		adp->arg_object = data2;
915*7c478bd9Sstevel@tonic-gate 		adp->arg_type = AT_BYREF;
916*7c478bd9Sstevel@tonic-gate 		adp->arg_inout = AI_OUTPUT;
917*7c478bd9Sstevel@tonic-gate 		adp->arg_size = sizeof (hrtime_t);
918*7c478bd9Sstevel@tonic-gate 		adp++;
919*7c478bd9Sstevel@tonic-gate 
920*7c478bd9Sstevel@tonic-gate 		adp->arg_value = 0;
921*7c478bd9Sstevel@tonic-gate 		adp->arg_object = data3;
922*7c478bd9Sstevel@tonic-gate 		adp->arg_type = AT_BYREF;
923*7c478bd9Sstevel@tonic-gate 		adp->arg_inout = AI_OUTPUT;
924*7c478bd9Sstevel@tonic-gate 		adp->arg_size = sizeof (uint64_t);
925*7c478bd9Sstevel@tonic-gate 
926*7c478bd9Sstevel@tonic-gate 		break;
927*7c478bd9Sstevel@tonic-gate 	default:
928*7c478bd9Sstevel@tonic-gate 		adp->arg_value = 0;
929*7c478bd9Sstevel@tonic-gate 		adp->arg_object = 0;
930*7c478bd9Sstevel@tonic-gate 		adp->arg_type = AT_BYVAL;
931*7c478bd9Sstevel@tonic-gate 		adp->arg_inout = AI_INPUT;
932*7c478bd9Sstevel@tonic-gate 		adp->arg_size = 0;
933*7c478bd9Sstevel@tonic-gate 		adp++;
934*7c478bd9Sstevel@tonic-gate 
935*7c478bd9Sstevel@tonic-gate 		adp->arg_value = 0;
936*7c478bd9Sstevel@tonic-gate 		adp->arg_object = 0;
937*7c478bd9Sstevel@tonic-gate 		adp->arg_type = AT_BYVAL;
938*7c478bd9Sstevel@tonic-gate 		adp->arg_inout = AI_INPUT;
939*7c478bd9Sstevel@tonic-gate 		adp->arg_size = 0;
940*7c478bd9Sstevel@tonic-gate 		adp++;
941*7c478bd9Sstevel@tonic-gate 
942*7c478bd9Sstevel@tonic-gate 		adp->arg_value = 0;
943*7c478bd9Sstevel@tonic-gate 		adp->arg_object = 0;
944*7c478bd9Sstevel@tonic-gate 		adp->arg_type = AT_BYVAL;
945*7c478bd9Sstevel@tonic-gate 		adp->arg_inout = AI_INPUT;
946*7c478bd9Sstevel@tonic-gate 		adp->arg_size = 0;
947*7c478bd9Sstevel@tonic-gate 
948*7c478bd9Sstevel@tonic-gate 		break;
949*7c478bd9Sstevel@tonic-gate 	}
950*7c478bd9Sstevel@tonic-gate 
951*7c478bd9Sstevel@tonic-gate 	error = Psyscall(pctx->Pr, &rval, SYS_cpc, 5, &argd[0]);
952*7c478bd9Sstevel@tonic-gate 
953*7c478bd9Sstevel@tonic-gate 	if (error) {
954*7c478bd9Sstevel@tonic-gate 		errno = error > 0 ? error : ENOSYS;
955*7c478bd9Sstevel@tonic-gate 		return (-1);
956*7c478bd9Sstevel@tonic-gate 	}
957*7c478bd9Sstevel@tonic-gate 	return (rval.sys_rval1);
958*7c478bd9Sstevel@tonic-gate }
959*7c478bd9Sstevel@tonic-gate 
960*7c478bd9Sstevel@tonic-gate /*
961*7c478bd9Sstevel@tonic-gate  * libcpc-private hook used to register a callback. The callback is used to
962*7c478bd9Sstevel@tonic-gate  * notify libcpc when a pctx handle is invalidated.
963*7c478bd9Sstevel@tonic-gate  */
964*7c478bd9Sstevel@tonic-gate void
965*7c478bd9Sstevel@tonic-gate __pctx_cpc_register_callback(void (*arg)(struct __cpc *, struct __pctx *))
966*7c478bd9Sstevel@tonic-gate {
967*7c478bd9Sstevel@tonic-gate 	pctx_cpc_callback = arg;
968*7c478bd9Sstevel@tonic-gate }
969