xref: /netbsd-src/external/cddl/osnet/dist/lib/libdtrace/common/dt_proc.c (revision ba2539a9805a0544ff82c0003cc02fe1eee5603d)
1a864dc36Sdarran /*
2a864dc36Sdarran  * CDDL HEADER START
3a864dc36Sdarran  *
4a864dc36Sdarran  * The contents of this file are subject to the terms of the
5a864dc36Sdarran  * Common Development and Distribution License (the "License").
6a864dc36Sdarran  * You may not use this file except in compliance with the License.
7a864dc36Sdarran  *
8a864dc36Sdarran  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9a864dc36Sdarran  * or http://www.opensolaris.org/os/licensing.
10a864dc36Sdarran  * See the License for the specific language governing permissions
11a864dc36Sdarran  * and limitations under the License.
12a864dc36Sdarran  *
13a864dc36Sdarran  * When distributing Covered Code, include this CDDL HEADER in each
14a864dc36Sdarran  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15a864dc36Sdarran  * If applicable, add the following below this CDDL HEADER, with the
16a864dc36Sdarran  * fields enclosed by brackets "[]" replaced with your own identifying
17a864dc36Sdarran  * information: Portions Copyright [yyyy] [name of copyright owner]
18a864dc36Sdarran  *
19a864dc36Sdarran  * CDDL HEADER END
20a864dc36Sdarran  */
21a864dc36Sdarran 
22a864dc36Sdarran /*
23a252d550Shaad  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24a864dc36Sdarran  * Use is subject to license terms.
25a864dc36Sdarran  */
26a864dc36Sdarran 
27a864dc36Sdarran /*
28a864dc36Sdarran  * DTrace Process Control
29a864dc36Sdarran  *
30a864dc36Sdarran  * This file provides a set of routines that permit libdtrace and its clients
31a864dc36Sdarran  * to create and grab process handles using libproc, and to share these handles
32a864dc36Sdarran  * between library mechanisms that need libproc access, such as ustack(), and
33a864dc36Sdarran  * client mechanisms that need libproc access, such as dtrace(1M) -c and -p.
34a864dc36Sdarran  * The library provides several mechanisms in the libproc control layer:
35a864dc36Sdarran  *
36a864dc36Sdarran  * Reference Counting: The library code and client code can independently grab
37a864dc36Sdarran  * the same process handles without interfering with one another.  Only when
38a864dc36Sdarran  * the reference count drops to zero and the handle is not being cached (see
39a864dc36Sdarran  * below for more information on caching) will Prelease() be called on it.
40a864dc36Sdarran  *
41a864dc36Sdarran  * Handle Caching: If a handle is grabbed PGRAB_RDONLY (e.g. by ustack()) and
42a864dc36Sdarran  * the reference count drops to zero, the handle is not immediately released.
43a864dc36Sdarran  * Instead, libproc handles are maintained on dph_lrulist in order from most-
44a864dc36Sdarran  * recently accessed to least-recently accessed.  Idle handles are maintained
45a864dc36Sdarran  * until a pre-defined LRU cache limit is exceeded, permitting repeated calls
46a864dc36Sdarran  * to ustack() to avoid the overhead of releasing and re-grabbing processes.
47a864dc36Sdarran  *
48a864dc36Sdarran  * Process Control: For processes that are grabbed for control (~PGRAB_RDONLY)
49a864dc36Sdarran  * or created by dt_proc_create(), a control thread is created to provide
50a864dc36Sdarran  * callbacks on process exit and symbol table caching on dlopen()s.
51a864dc36Sdarran  *
52a864dc36Sdarran  * MT-Safety: Libproc is not MT-Safe, so dt_proc_lock() and dt_proc_unlock()
53a864dc36Sdarran  * are provided to synchronize access to the libproc handle between libdtrace
54a864dc36Sdarran  * code and client code and the control thread's use of the ps_prochandle.
55a864dc36Sdarran  *
56a864dc36Sdarran  * NOTE: MT-Safety is NOT provided for libdtrace itself, or for use of the
57a864dc36Sdarran  * dtrace_proc_grab/dtrace_proc_create mechanisms.  Like all exported libdtrace
58a864dc36Sdarran  * calls, these are assumed to be MT-Unsafe.  MT-Safety is ONLY provided for
59a864dc36Sdarran  * synchronization between libdtrace control threads and the client thread.
60a864dc36Sdarran  *
61a864dc36Sdarran  * The ps_prochandles themselves are maintained along with a dt_proc_t struct
62a864dc36Sdarran  * in a hash table indexed by PID.  This provides basic locking and reference
63a864dc36Sdarran  * counting.  The dt_proc_t is also maintained in LRU order on dph_lrulist.
64a864dc36Sdarran  * The dph_lrucnt and dph_lrulim count the number of cacheable processes and
65a864dc36Sdarran  * the current limit on the number of actively cached entries.
66a864dc36Sdarran  *
67a864dc36Sdarran  * The control thread for a process establishes breakpoints at the rtld_db
68a864dc36Sdarran  * locations of interest, updates mappings and symbol tables at these points,
69a864dc36Sdarran  * and handles exec and fork (by always following the parent).  The control
70a864dc36Sdarran  * thread automatically exits when the process dies or control is lost.
71a864dc36Sdarran  *
72a864dc36Sdarran  * A simple notification mechanism is provided for libdtrace clients using
73a864dc36Sdarran  * dtrace_handle_proc() for notification of PS_UNDEAD or PS_LOST events.  If
74a864dc36Sdarran  * such an event occurs, the dt_proc_t itself is enqueued on a notification
75a864dc36Sdarran  * list and the control thread broadcasts to dph_cv.  dtrace_sleep() will wake
76a864dc36Sdarran  * up using this condition and will then call the client handler as necessary.
77a864dc36Sdarran  */
78a864dc36Sdarran 
79a864dc36Sdarran #include <sys/wait.h>
80c0855460Schristos #ifdef illumos
81a864dc36Sdarran #include <sys/lwp.h>
82bb8023b5Sdarran #endif
83a864dc36Sdarran #include <strings.h>
84a864dc36Sdarran #include <signal.h>
85a864dc36Sdarran #include <assert.h>
86a864dc36Sdarran #include <errno.h>
87a864dc36Sdarran 
88a864dc36Sdarran #include <dt_proc.h>
89a864dc36Sdarran #include <dt_pid.h>
90a864dc36Sdarran #include <dt_impl.h>
91a864dc36Sdarran 
92c0855460Schristos #ifndef illumos
93c0855460Schristos #include <sys/syscall.h>
94c0855460Schristos #include <libproc_compat.h>
95c0855460Schristos #define	SYS_forksys SYS_fork
96c0855460Schristos #endif
9701c9547eSdarran 
98c0855460Schristos #define	IS_SYS_EXEC(w)	(w == SYS_execve)
99c0855460Schristos #define	IS_SYS_FORK(w)	(w == SYS_vfork || w == SYS_forksys)
10001c9547eSdarran 
101a864dc36Sdarran static dt_bkpt_t *
dt_proc_bpcreate(dt_proc_t * dpr,uintptr_t addr,dt_bkpt_f * func,void * data)102a864dc36Sdarran dt_proc_bpcreate(dt_proc_t *dpr, uintptr_t addr, dt_bkpt_f *func, void *data)
103a864dc36Sdarran {
104a864dc36Sdarran 	struct ps_prochandle *P = dpr->dpr_proc;
105a864dc36Sdarran 	dt_bkpt_t *dbp;
106a864dc36Sdarran 
107ac8906f7Sdarran 	assert(DT_MUTEX_HELD(&dpr->dpr_lock));
108a864dc36Sdarran 
109a864dc36Sdarran 	if ((dbp = dt_zalloc(dpr->dpr_hdl, sizeof (dt_bkpt_t))) != NULL) {
110a864dc36Sdarran 		dbp->dbp_func = func;
111a864dc36Sdarran 		dbp->dbp_data = data;
112a864dc36Sdarran 		dbp->dbp_addr = addr;
113a864dc36Sdarran 
114*ba2539a9Schs #ifdef __NetBSD__
115a864dc36Sdarran 		if (Psetbkpt(P, dbp->dbp_addr, &dbp->dbp_instr) == 0)
116*ba2539a9Schs #else
117*ba2539a9Schs 		if (Psetbkpt(P, dbp->dbp_addr, dbp->dbp_instr) == 0)
118*ba2539a9Schs #endif
119a864dc36Sdarran 			dbp->dbp_active = B_TRUE;
120a864dc36Sdarran 
121a864dc36Sdarran 		dt_list_append(&dpr->dpr_bps, dbp);
122a864dc36Sdarran 	}
123a864dc36Sdarran 
124a864dc36Sdarran 	return (dbp);
125a864dc36Sdarran }
126a864dc36Sdarran 
127a864dc36Sdarran static void
dt_proc_bpdestroy(dt_proc_t * dpr,int delbkpts)128a864dc36Sdarran dt_proc_bpdestroy(dt_proc_t *dpr, int delbkpts)
129a864dc36Sdarran {
130a864dc36Sdarran 	int state = Pstate(dpr->dpr_proc);
131a864dc36Sdarran 	dt_bkpt_t *dbp, *nbp;
132a864dc36Sdarran 
133ac8906f7Sdarran 	assert(DT_MUTEX_HELD(&dpr->dpr_lock));
134a864dc36Sdarran 
135a864dc36Sdarran 	for (dbp = dt_list_next(&dpr->dpr_bps); dbp != NULL; dbp = nbp) {
136a864dc36Sdarran 		if (delbkpts && dbp->dbp_active &&
137a864dc36Sdarran 		    state != PS_LOST && state != PS_UNDEAD) {
138a864dc36Sdarran 			(void) Pdelbkpt(dpr->dpr_proc,
1393a8db65dSchristos 			    dbp->dbp_addr, &dbp->dbp_instr);
140a864dc36Sdarran 		}
141a864dc36Sdarran 		nbp = dt_list_next(dbp);
142a864dc36Sdarran 		dt_list_delete(&dpr->dpr_bps, dbp);
143a864dc36Sdarran 		dt_free(dpr->dpr_hdl, dbp);
144a864dc36Sdarran 	}
145a864dc36Sdarran }
146a864dc36Sdarran 
147a864dc36Sdarran static void
dt_proc_bpmatch(dtrace_hdl_t * dtp,dt_proc_t * dpr)148a864dc36Sdarran dt_proc_bpmatch(dtrace_hdl_t *dtp, dt_proc_t *dpr)
149a864dc36Sdarran {
150c0855460Schristos #ifdef illumos
151a864dc36Sdarran 	const lwpstatus_t *psp = &Pstatus(dpr->dpr_proc)->pr_lwp;
152c0855460Schristos #else
153c0855460Schristos 	unsigned long pc;
154c0855460Schristos #endif
155a864dc36Sdarran 	dt_bkpt_t *dbp;
156a864dc36Sdarran 
157ac8906f7Sdarran 	assert(DT_MUTEX_HELD(&dpr->dpr_lock));
158a864dc36Sdarran 
159c0855460Schristos #ifndef illumos
160c0855460Schristos 	proc_regget(dpr->dpr_proc, REG_PC, &pc);
161c0855460Schristos 	proc_bkptregadj(&pc);
162c0855460Schristos #endif
163c0855460Schristos 
164a864dc36Sdarran 	for (dbp = dt_list_next(&dpr->dpr_bps);
165a864dc36Sdarran 	    dbp != NULL; dbp = dt_list_next(dbp)) {
166c0855460Schristos #ifdef illumos
167a864dc36Sdarran 		if (psp->pr_reg[R_PC] == dbp->dbp_addr)
168a864dc36Sdarran 			break;
169c0855460Schristos #else
170c0855460Schristos 		if (pc == dbp->dbp_addr)
171c0855460Schristos 			break;
172c0855460Schristos #endif
173a864dc36Sdarran 	}
174a864dc36Sdarran 
175a864dc36Sdarran 	if (dbp == NULL) {
176a864dc36Sdarran 		dt_dprintf("pid %d: spurious breakpoint wakeup for %lx\n",
177c0855460Schristos #ifdef illumos
178a864dc36Sdarran 		    (int)dpr->dpr_pid, (ulong_t)psp->pr_reg[R_PC]);
179c0855460Schristos #else
180c0855460Schristos 		    (int)dpr->dpr_pid, pc);
181c0855460Schristos #endif
182a864dc36Sdarran 		return;
183a864dc36Sdarran 	}
184a864dc36Sdarran 
185a864dc36Sdarran 	dt_dprintf("pid %d: hit breakpoint at %lx (%lu)\n",
186a864dc36Sdarran 	    (int)dpr->dpr_pid, (ulong_t)dbp->dbp_addr, ++dbp->dbp_hits);
187a864dc36Sdarran 
188a864dc36Sdarran 	dbp->dbp_func(dtp, dpr, dbp->dbp_data);
189*ba2539a9Schs #ifdef __NetBSD__
1903a8db65dSchristos 	(void) Pxecbkpt(dpr->dpr_proc, &dbp->dbp_instr);
191*ba2539a9Schs #else
192*ba2539a9Schs 	(void) Pxecbkpt(dpr->dpr_proc, dbp->dbp_instr);
193*ba2539a9Schs #endif
194a864dc36Sdarran }
195a864dc36Sdarran 
196a864dc36Sdarran static void
dt_proc_bpenable(dt_proc_t * dpr)197a864dc36Sdarran dt_proc_bpenable(dt_proc_t *dpr)
198a864dc36Sdarran {
199a864dc36Sdarran 	dt_bkpt_t *dbp;
200a864dc36Sdarran 
201ac8906f7Sdarran 	assert(DT_MUTEX_HELD(&dpr->dpr_lock));
202a864dc36Sdarran 
203a864dc36Sdarran 	for (dbp = dt_list_next(&dpr->dpr_bps);
204a864dc36Sdarran 	    dbp != NULL; dbp = dt_list_next(dbp)) {
205a864dc36Sdarran 		if (!dbp->dbp_active && Psetbkpt(dpr->dpr_proc,
206a864dc36Sdarran 		    dbp->dbp_addr, &dbp->dbp_instr) == 0)
207a864dc36Sdarran 			dbp->dbp_active = B_TRUE;
208a864dc36Sdarran 	}
209a864dc36Sdarran 
210a864dc36Sdarran 	dt_dprintf("breakpoints enabled\n");
211a864dc36Sdarran }
212a864dc36Sdarran 
213a864dc36Sdarran static void
dt_proc_bpdisable(dt_proc_t * dpr)214a864dc36Sdarran dt_proc_bpdisable(dt_proc_t *dpr)
215a864dc36Sdarran {
216a864dc36Sdarran 	dt_bkpt_t *dbp;
217a864dc36Sdarran 
218ac8906f7Sdarran 	assert(DT_MUTEX_HELD(&dpr->dpr_lock));
219a864dc36Sdarran 
220a864dc36Sdarran 	for (dbp = dt_list_next(&dpr->dpr_bps);
221a864dc36Sdarran 	    dbp != NULL; dbp = dt_list_next(dbp)) {
222a864dc36Sdarran 		if (dbp->dbp_active && Pdelbkpt(dpr->dpr_proc,
2233a8db65dSchristos 		    dbp->dbp_addr, &dbp->dbp_instr) == 0)
224a864dc36Sdarran 			dbp->dbp_active = B_FALSE;
225a864dc36Sdarran 	}
226a864dc36Sdarran 
227a864dc36Sdarran 	dt_dprintf("breakpoints disabled\n");
228a864dc36Sdarran }
229a864dc36Sdarran 
230a864dc36Sdarran static void
dt_proc_notify(dtrace_hdl_t * dtp,dt_proc_hash_t * dph,dt_proc_t * dpr,const char * msg)231a864dc36Sdarran dt_proc_notify(dtrace_hdl_t *dtp, dt_proc_hash_t *dph, dt_proc_t *dpr,
232a864dc36Sdarran     const char *msg)
233a864dc36Sdarran {
234a864dc36Sdarran 	dt_proc_notify_t *dprn = dt_alloc(dtp, sizeof (dt_proc_notify_t));
235a864dc36Sdarran 
236a864dc36Sdarran 	if (dprn == NULL) {
237a864dc36Sdarran 		dt_dprintf("failed to allocate notification for %d %s\n",
238a864dc36Sdarran 		    (int)dpr->dpr_pid, msg);
239a864dc36Sdarran 	} else {
240a864dc36Sdarran 		dprn->dprn_dpr = dpr;
241a864dc36Sdarran 		if (msg == NULL)
242a864dc36Sdarran 			dprn->dprn_errmsg[0] = '\0';
243a864dc36Sdarran 		else
244a864dc36Sdarran 			(void) strlcpy(dprn->dprn_errmsg, msg,
245a864dc36Sdarran 			    sizeof (dprn->dprn_errmsg));
246a864dc36Sdarran 
247a864dc36Sdarran 		(void) pthread_mutex_lock(&dph->dph_lock);
248a864dc36Sdarran 
249a864dc36Sdarran 		dprn->dprn_next = dph->dph_notify;
250a864dc36Sdarran 		dph->dph_notify = dprn;
251a864dc36Sdarran 
252a864dc36Sdarran 		(void) pthread_cond_broadcast(&dph->dph_cv);
253a864dc36Sdarran 		(void) pthread_mutex_unlock(&dph->dph_lock);
254a864dc36Sdarran 	}
255a864dc36Sdarran }
256a864dc36Sdarran 
257a864dc36Sdarran /*
258a864dc36Sdarran  * Check to see if the control thread was requested to stop when the victim
259a864dc36Sdarran  * process reached a particular event (why) rather than continuing the victim.
260a864dc36Sdarran  * If 'why' is set in the stop mask, we wait on dpr_cv for dt_proc_continue().
261a864dc36Sdarran  * If 'why' is not set, this function returns immediately and does nothing.
262a864dc36Sdarran  */
263a864dc36Sdarran static void
dt_proc_stop(dt_proc_t * dpr,uint8_t why)264a864dc36Sdarran dt_proc_stop(dt_proc_t *dpr, uint8_t why)
265a864dc36Sdarran {
266ac8906f7Sdarran 	assert(DT_MUTEX_HELD(&dpr->dpr_lock));
267a864dc36Sdarran 	assert(why != DT_PROC_STOP_IDLE);
268a864dc36Sdarran 
269a864dc36Sdarran 	if (dpr->dpr_stop & why) {
270a864dc36Sdarran 		dpr->dpr_stop |= DT_PROC_STOP_IDLE;
271a864dc36Sdarran 		dpr->dpr_stop &= ~why;
272a864dc36Sdarran 
273a864dc36Sdarran 		(void) pthread_cond_broadcast(&dpr->dpr_cv);
274a864dc36Sdarran 
275a864dc36Sdarran 		/*
276a864dc36Sdarran 		 * We disable breakpoints while stopped to preserve the
277a864dc36Sdarran 		 * integrity of the program text for both our own disassembly
278a864dc36Sdarran 		 * and that of the kernel.
279a864dc36Sdarran 		 */
280a864dc36Sdarran 		dt_proc_bpdisable(dpr);
281a864dc36Sdarran 
282a864dc36Sdarran 		while (dpr->dpr_stop & DT_PROC_STOP_IDLE)
283a864dc36Sdarran 			(void) pthread_cond_wait(&dpr->dpr_cv, &dpr->dpr_lock);
284a864dc36Sdarran 
285a864dc36Sdarran 		dt_proc_bpenable(dpr);
286a864dc36Sdarran 	}
287a864dc36Sdarran }
288a864dc36Sdarran 
289a864dc36Sdarran /*ARGSUSED*/
290a864dc36Sdarran static void
dt_proc_bpmain(dtrace_hdl_t * dtp,dt_proc_t * dpr,const char * fname)291a864dc36Sdarran dt_proc_bpmain(dtrace_hdl_t *dtp, dt_proc_t *dpr, const char *fname)
292a864dc36Sdarran {
293a864dc36Sdarran 	dt_dprintf("pid %d: breakpoint at %s()\n", (int)dpr->dpr_pid, fname);
294a864dc36Sdarran 	dt_proc_stop(dpr, DT_PROC_STOP_MAIN);
295a864dc36Sdarran }
296a864dc36Sdarran 
297a864dc36Sdarran static void
dt_proc_rdevent(dtrace_hdl_t * dtp,dt_proc_t * dpr,const char * evname)298a864dc36Sdarran dt_proc_rdevent(dtrace_hdl_t *dtp, dt_proc_t *dpr, const char *evname)
299a864dc36Sdarran {
300a864dc36Sdarran 	rd_event_msg_t rdm;
301a864dc36Sdarran 	rd_err_e err;
302a864dc36Sdarran 
303a864dc36Sdarran 	if ((err = rd_event_getmsg(dpr->dpr_rtld, &rdm)) != RD_OK) {
304a864dc36Sdarran 		dt_dprintf("pid %d: failed to get %s event message: %s\n",
305a864dc36Sdarran 		    (int)dpr->dpr_pid, evname, rd_errstr(err));
306a864dc36Sdarran 		return;
307a864dc36Sdarran 	}
308a864dc36Sdarran 
309a864dc36Sdarran 	dt_dprintf("pid %d: rtld event %s type=%d state %d\n",
310a864dc36Sdarran 	    (int)dpr->dpr_pid, evname, rdm.type, rdm.u.state);
311a864dc36Sdarran 
312a864dc36Sdarran 	switch (rdm.type) {
313c0855460Schristos 	case RD_NONE:
314c0855460Schristos 		break;
315a864dc36Sdarran 	case RD_DLACTIVITY:
316a864dc36Sdarran 		if (rdm.u.state != RD_CONSISTENT)
317a864dc36Sdarran 			break;
318a864dc36Sdarran 
319a864dc36Sdarran 		Pupdate_syms(dpr->dpr_proc);
320a864dc36Sdarran 		if (dt_pid_create_probes_module(dtp, dpr) != 0)
321a864dc36Sdarran 			dt_proc_notify(dtp, dtp->dt_procs, dpr,
322a864dc36Sdarran 			    dpr->dpr_errmsg);
323a864dc36Sdarran 
324a864dc36Sdarran 		break;
325a864dc36Sdarran 	case RD_PREINIT:
326a864dc36Sdarran 		Pupdate_syms(dpr->dpr_proc);
327a864dc36Sdarran 		dt_proc_stop(dpr, DT_PROC_STOP_PREINIT);
328a864dc36Sdarran 		break;
329a864dc36Sdarran 	case RD_POSTINIT:
330a864dc36Sdarran 		Pupdate_syms(dpr->dpr_proc);
331a864dc36Sdarran 		dt_proc_stop(dpr, DT_PROC_STOP_POSTINIT);
332a864dc36Sdarran 		break;
333a864dc36Sdarran 	}
334a864dc36Sdarran }
335a864dc36Sdarran 
336a864dc36Sdarran static void
dt_proc_rdwatch(dt_proc_t * dpr,rd_event_e event,const char * evname)337a864dc36Sdarran dt_proc_rdwatch(dt_proc_t *dpr, rd_event_e event, const char *evname)
338a864dc36Sdarran {
339a864dc36Sdarran 	rd_notify_t rdn;
340a864dc36Sdarran 	rd_err_e err;
341a864dc36Sdarran 
342a864dc36Sdarran 	if ((err = rd_event_addr(dpr->dpr_rtld, event, &rdn)) != RD_OK) {
343a864dc36Sdarran 		dt_dprintf("pid %d: failed to get event address for %s: %s\n",
344a864dc36Sdarran 		    (int)dpr->dpr_pid, evname, rd_errstr(err));
345a864dc36Sdarran 		return;
346a864dc36Sdarran 	}
347a864dc36Sdarran 
348a864dc36Sdarran 	if (rdn.type != RD_NOTIFY_BPT) {
349a864dc36Sdarran 		dt_dprintf("pid %d: event %s has unexpected type %d\n",
350a864dc36Sdarran 		    (int)dpr->dpr_pid, evname, rdn.type);
351a864dc36Sdarran 		return;
352a864dc36Sdarran 	}
353a864dc36Sdarran 
354a864dc36Sdarran 	(void) dt_proc_bpcreate(dpr, rdn.u.bptaddr,
355c0855460Schristos #ifdef illumos
356a864dc36Sdarran 	    (dt_bkpt_f *)dt_proc_rdevent, (void *)evname);
357c0855460Schristos #else
358c0855460Schristos 	    /* XXX ugly */
359c0855460Schristos 	    (dt_bkpt_f *)dt_proc_rdevent, __DECONST(void *, evname));
360c0855460Schristos #endif
361a864dc36Sdarran }
362a864dc36Sdarran 
363a864dc36Sdarran /*
364a864dc36Sdarran  * Common code for enabling events associated with the run-time linker after
365a864dc36Sdarran  * attaching to a process or after a victim process completes an exec(2).
366a864dc36Sdarran  */
367a864dc36Sdarran static void
dt_proc_attach(dt_proc_t * dpr,int exec)368a864dc36Sdarran dt_proc_attach(dt_proc_t *dpr, int exec)
369a864dc36Sdarran {
370c0855460Schristos #ifdef illumos
371a864dc36Sdarran 	const pstatus_t *psp = Pstatus(dpr->dpr_proc);
372c0855460Schristos #endif
373a864dc36Sdarran 	rd_err_e err;
374a864dc36Sdarran 	GElf_Sym sym;
375a864dc36Sdarran 
376ac8906f7Sdarran 	assert(DT_MUTEX_HELD(&dpr->dpr_lock));
377a864dc36Sdarran 
378a864dc36Sdarran 	if (exec) {
379c0855460Schristos #ifdef illumos
380a864dc36Sdarran 		if (psp->pr_lwp.pr_errno != 0)
381a864dc36Sdarran 			return; /* exec failed: nothing needs to be done */
382c0855460Schristos #endif
383a864dc36Sdarran 
384a864dc36Sdarran 		dt_proc_bpdestroy(dpr, B_FALSE);
385c0855460Schristos #ifdef illumos
386a864dc36Sdarran 		Preset_maps(dpr->dpr_proc);
387c0855460Schristos #endif
388a864dc36Sdarran 	}
389a864dc36Sdarran 	if ((dpr->dpr_rtld = Prd_agent(dpr->dpr_proc)) != NULL &&
390a864dc36Sdarran 	    (err = rd_event_enable(dpr->dpr_rtld, B_TRUE)) == RD_OK) {
391c0855460Schristos #ifdef illumos
392a864dc36Sdarran 		dt_proc_rdwatch(dpr, RD_PREINIT, "RD_PREINIT");
393c0855460Schristos #endif
394a864dc36Sdarran 		dt_proc_rdwatch(dpr, RD_POSTINIT, "RD_POSTINIT");
395c0855460Schristos #ifdef illumos
396a864dc36Sdarran 		dt_proc_rdwatch(dpr, RD_DLACTIVITY, "RD_DLACTIVITY");
397c0855460Schristos #endif
398a864dc36Sdarran 	} else {
399a864dc36Sdarran 		dt_dprintf("pid %d: failed to enable rtld events: %s\n",
400a864dc36Sdarran 		    (int)dpr->dpr_pid, dpr->dpr_rtld ? rd_errstr(err) :
401a864dc36Sdarran 		    "rtld_db agent initialization failed");
402a864dc36Sdarran 	}
403a864dc36Sdarran 
404a864dc36Sdarran 	Pupdate_maps(dpr->dpr_proc);
405a864dc36Sdarran 
406a864dc36Sdarran 	if (Pxlookup_by_name(dpr->dpr_proc, LM_ID_BASE,
407a864dc36Sdarran 	    "a.out", "main", &sym, NULL) == 0) {
408a864dc36Sdarran 		(void) dt_proc_bpcreate(dpr, (uintptr_t)sym.st_value,
409a864dc36Sdarran 		    (dt_bkpt_f *)dt_proc_bpmain, "a.out`main");
410a864dc36Sdarran 	} else {
411a864dc36Sdarran 		dt_dprintf("pid %d: failed to find a.out`main: %s\n",
412a864dc36Sdarran 		    (int)dpr->dpr_pid, strerror(errno));
413a864dc36Sdarran 	}
414a864dc36Sdarran }
415a864dc36Sdarran 
416a864dc36Sdarran /*
417a864dc36Sdarran  * Wait for a stopped process to be set running again by some other debugger.
418a864dc36Sdarran  * This is typically not required by /proc-based debuggers, since the usual
419a864dc36Sdarran  * model is that one debugger controls one victim.  But DTrace, as usual, has
420a864dc36Sdarran  * its own needs: the stop() action assumes that prun(1) or some other tool
421a864dc36Sdarran  * will be applied to resume the victim process.  This could be solved by
422a864dc36Sdarran  * adding a PCWRUN directive to /proc, but that seems like overkill unless
423a864dc36Sdarran  * other debuggers end up needing this functionality, so we implement a cheap
424a864dc36Sdarran  * equivalent to PCWRUN using the set of existing kernel mechanisms.
425a864dc36Sdarran  *
426a864dc36Sdarran  * Our intent is really not just to wait for the victim to run, but rather to
427a864dc36Sdarran  * wait for it to run and then stop again for a reason other than the current
428a864dc36Sdarran  * PR_REQUESTED stop.  Since PCWSTOP/Pstopstatus() can be applied repeatedly
429a864dc36Sdarran  * to a stopped process and will return the same result without affecting the
430a864dc36Sdarran  * victim, we can just perform these operations repeatedly until Pstate()
431a864dc36Sdarran  * changes, the representative LWP ID changes, or the stop timestamp advances.
432a864dc36Sdarran  * dt_proc_control() will then rediscover the new state and continue as usual.
433a864dc36Sdarran  * When the process is still stopped in the same exact state, we sleep for a
434a864dc36Sdarran  * brief interval before waiting again so as not to spin consuming CPU cycles.
435a864dc36Sdarran  */
436a864dc36Sdarran static void
dt_proc_waitrun(dt_proc_t * dpr)437a864dc36Sdarran dt_proc_waitrun(dt_proc_t *dpr)
438a864dc36Sdarran {
439c0855460Schristos printf("%s:%s(%d): DOODAD\n",__FUNCTION__,__FILE__,__LINE__);
440c0855460Schristos #ifdef DOODAD
441a864dc36Sdarran 	struct ps_prochandle *P = dpr->dpr_proc;
442a864dc36Sdarran 	const lwpstatus_t *psp = &Pstatus(P)->pr_lwp;
443a864dc36Sdarran 
444a864dc36Sdarran 	int krflag = psp->pr_flags & (PR_KLC | PR_RLC);
445a864dc36Sdarran 	timestruc_t tstamp = psp->pr_tstamp;
446a864dc36Sdarran 	lwpid_t lwpid = psp->pr_lwpid;
447a864dc36Sdarran 
448a864dc36Sdarran 	const long wstop = PCWSTOP;
449a864dc36Sdarran 	int pfd = Pctlfd(P);
450a864dc36Sdarran 
451ac8906f7Sdarran 	assert(DT_MUTEX_HELD(&dpr->dpr_lock));
452a864dc36Sdarran 	assert(psp->pr_flags & PR_STOPPED);
453a864dc36Sdarran 	assert(Pstate(P) == PS_STOP);
454a864dc36Sdarran 
455a864dc36Sdarran 	/*
456a864dc36Sdarran 	 * While we are waiting for the victim to run, clear PR_KLC and PR_RLC
457a864dc36Sdarran 	 * so that if the libdtrace client is killed, the victim stays stopped.
458a864dc36Sdarran 	 * dt_proc_destroy() will also observe this and perform PRELEASE_HANG.
459a864dc36Sdarran 	 */
460a864dc36Sdarran 	(void) Punsetflags(P, krflag);
461a864dc36Sdarran 	Psync(P);
462a864dc36Sdarran 
463a864dc36Sdarran 	(void) pthread_mutex_unlock(&dpr->dpr_lock);
464a864dc36Sdarran 
465a864dc36Sdarran 	while (!dpr->dpr_quit) {
466a864dc36Sdarran 		if (write(pfd, &wstop, sizeof (wstop)) == -1 && errno == EINTR)
467a864dc36Sdarran 			continue; /* check dpr_quit and continue waiting */
468a864dc36Sdarran 
469a864dc36Sdarran 		(void) pthread_mutex_lock(&dpr->dpr_lock);
470a864dc36Sdarran 		(void) Pstopstatus(P, PCNULL, 0);
471a864dc36Sdarran 		psp = &Pstatus(P)->pr_lwp;
472a864dc36Sdarran 
473a864dc36Sdarran 		/*
474a864dc36Sdarran 		 * If we've reached a new state, found a new representative, or
475a864dc36Sdarran 		 * the stop timestamp has changed, restore PR_KLC/PR_RLC to its
476a864dc36Sdarran 		 * original setting and then return with dpr_lock held.
477a864dc36Sdarran 		 */
478a864dc36Sdarran 		if (Pstate(P) != PS_STOP || psp->pr_lwpid != lwpid ||
479a864dc36Sdarran 		    bcmp(&psp->pr_tstamp, &tstamp, sizeof (tstamp)) != 0) {
480a864dc36Sdarran 			(void) Psetflags(P, krflag);
481a864dc36Sdarran 			Psync(P);
482a864dc36Sdarran 			return;
483a864dc36Sdarran 		}
484a864dc36Sdarran 
485a864dc36Sdarran 		(void) pthread_mutex_unlock(&dpr->dpr_lock);
486a864dc36Sdarran 		(void) poll(NULL, 0, MILLISEC / 2);
487a864dc36Sdarran 	}
488a864dc36Sdarran 
489a864dc36Sdarran 	(void) pthread_mutex_lock(&dpr->dpr_lock);
490bb8023b5Sdarran #endif
491c0855460Schristos }
492a864dc36Sdarran 
493a864dc36Sdarran typedef struct dt_proc_control_data {
494a864dc36Sdarran 	dtrace_hdl_t *dpcd_hdl;			/* DTrace handle */
495a864dc36Sdarran 	dt_proc_t *dpcd_proc;			/* proccess to control */
496a864dc36Sdarran } dt_proc_control_data_t;
497a864dc36Sdarran 
498a864dc36Sdarran /*
499a864dc36Sdarran  * Main loop for all victim process control threads.  We initialize all the
500a864dc36Sdarran  * appropriate /proc control mechanisms, and then enter a loop waiting for
501a864dc36Sdarran  * the process to stop on an event or die.  We process any events by calling
502a864dc36Sdarran  * appropriate subroutines, and exit when the victim dies or we lose control.
503a864dc36Sdarran  *
504a864dc36Sdarran  * The control thread synchronizes the use of dpr_proc with other libdtrace
505a864dc36Sdarran  * threads using dpr_lock.  We hold the lock for all of our operations except
506a864dc36Sdarran  * waiting while the process is running: this is accomplished by writing a
507a864dc36Sdarran  * PCWSTOP directive directly to the underlying /proc/<pid>/ctl file.  If the
508a864dc36Sdarran  * libdtrace client wishes to exit or abort our wait, SIGCANCEL can be used.
509a864dc36Sdarran  */
510a864dc36Sdarran static void *
dt_proc_control(void * arg)511a864dc36Sdarran dt_proc_control(void *arg)
512a864dc36Sdarran {
513a864dc36Sdarran 	dt_proc_control_data_t *datap = arg;
514a864dc36Sdarran 	dtrace_hdl_t *dtp = datap->dpcd_hdl;
515a864dc36Sdarran 	dt_proc_t *dpr = datap->dpcd_proc;
516a864dc36Sdarran 	dt_proc_hash_t *dph = dpr->dpr_hdl->dt_procs;
517a864dc36Sdarran 	struct ps_prochandle *P = dpr->dpr_proc;
518a864dc36Sdarran 	int pid = dpr->dpr_pid;
519a864dc36Sdarran 
520c0855460Schristos #ifdef illumos
521bb8023b5Sdarran 	int pfd = Pctlfd(P);
522bb8023b5Sdarran 
523a864dc36Sdarran 	const long wstop = PCWSTOP;
524bb8023b5Sdarran #endif
525a864dc36Sdarran 	int notify = B_FALSE;
526a864dc36Sdarran 
527a864dc36Sdarran 	/*
528a864dc36Sdarran 	 * We disable the POSIX thread cancellation mechanism so that the
529a864dc36Sdarran 	 * client program using libdtrace can't accidentally cancel our thread.
530a864dc36Sdarran 	 * dt_proc_destroy() uses SIGCANCEL explicitly to simply poke us out
531a864dc36Sdarran 	 * of PCWSTOP with EINTR, at which point we will see dpr_quit and exit.
532a864dc36Sdarran 	 */
533a864dc36Sdarran 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
534a864dc36Sdarran 
535a864dc36Sdarran 	/*
536a864dc36Sdarran 	 * Set up the corresponding process for tracing by libdtrace.  We want
537a864dc36Sdarran 	 * to be able to catch breakpoints and efficiently single-step over
538a864dc36Sdarran 	 * them, and we need to enable librtld_db to watch libdl activity.
539a864dc36Sdarran 	 */
540a864dc36Sdarran 	(void) pthread_mutex_lock(&dpr->dpr_lock);
541a864dc36Sdarran 
542c0855460Schristos #ifdef illumos
543a864dc36Sdarran 	(void) Punsetflags(P, PR_ASYNC);	/* require synchronous mode */
544a864dc36Sdarran 	(void) Psetflags(P, PR_BPTADJ);		/* always adjust eip on x86 */
545a864dc36Sdarran 	(void) Punsetflags(P, PR_FORK);		/* do not inherit on fork */
546a864dc36Sdarran 
547a864dc36Sdarran 	(void) Pfault(P, FLTBPT, B_TRUE);	/* always trace breakpoints */
548a864dc36Sdarran 	(void) Pfault(P, FLTTRACE, B_TRUE);	/* always trace single-step */
549a864dc36Sdarran 
550a864dc36Sdarran 	/*
551a864dc36Sdarran 	 * We must trace exit from exec() system calls so that if the exec is
552a864dc36Sdarran 	 * successful, we can reset our breakpoints and re-initialize libproc.
553a864dc36Sdarran 	 */
554a864dc36Sdarran 	(void) Psysexit(P, SYS_execve, B_TRUE);
555a864dc36Sdarran 
556a864dc36Sdarran 	/*
557a864dc36Sdarran 	 * We must trace entry and exit for fork() system calls in order to
558a864dc36Sdarran 	 * disable our breakpoints temporarily during the fork.  We do not set
559a864dc36Sdarran 	 * the PR_FORK flag, so if fork succeeds the child begins executing and
560a864dc36Sdarran 	 * does not inherit any other tracing behaviors or a control thread.
561a864dc36Sdarran 	 */
562a864dc36Sdarran 	(void) Psysentry(P, SYS_vfork, B_TRUE);
563a864dc36Sdarran 	(void) Psysexit(P, SYS_vfork, B_TRUE);
564a864dc36Sdarran 	(void) Psysentry(P, SYS_forksys, B_TRUE);
565a864dc36Sdarran 	(void) Psysexit(P, SYS_forksys, B_TRUE);
566a864dc36Sdarran 
567a864dc36Sdarran 	Psync(P);				/* enable all /proc changes */
568c0855460Schristos #endif
569a864dc36Sdarran 	dt_proc_attach(dpr, B_FALSE);		/* enable rtld breakpoints */
570a864dc36Sdarran 
571a864dc36Sdarran 	/*
572a864dc36Sdarran 	 * If PR_KLC is set, we created the process; otherwise we grabbed it.
573a864dc36Sdarran 	 * Check for an appropriate stop request and wait for dt_proc_continue.
574a864dc36Sdarran 	 */
575c0855460Schristos #ifdef illumos
576a864dc36Sdarran 	if (Pstatus(P)->pr_flags & PR_KLC)
577c0855460Schristos #else
578c0855460Schristos 	if (proc_getflags(P) & PR_KLC)
579c0855460Schristos #endif
580a864dc36Sdarran 		dt_proc_stop(dpr, DT_PROC_STOP_CREATE);
581a864dc36Sdarran 	else
582a864dc36Sdarran 		dt_proc_stop(dpr, DT_PROC_STOP_GRAB);
583a864dc36Sdarran 
584a864dc36Sdarran 	if (Psetrun(P, 0, 0) == -1) {
585a864dc36Sdarran 		dt_dprintf("pid %d: failed to set running: %s\n",
586a864dc36Sdarran 		    (int)dpr->dpr_pid, strerror(errno));
587a864dc36Sdarran 	}
588a864dc36Sdarran 
589a864dc36Sdarran 	(void) pthread_mutex_unlock(&dpr->dpr_lock);
590a864dc36Sdarran 
591a864dc36Sdarran 	/*
592a864dc36Sdarran 	 * Wait for the process corresponding to this control thread to stop,
593a864dc36Sdarran 	 * process the event, and then set it running again.  We want to sleep
594a864dc36Sdarran 	 * with dpr_lock *unheld* so that other parts of libdtrace can use the
595a864dc36Sdarran 	 * ps_prochandle in the meantime (e.g. ustack()).  To do this, we write
596a864dc36Sdarran 	 * a PCWSTOP directive directly to the underlying /proc/<pid>/ctl file.
597a864dc36Sdarran 	 * Once the process stops, we wake up, grab dpr_lock, and then call
598a864dc36Sdarran 	 * Pwait() (which will return immediately) and do our processing.
599a864dc36Sdarran 	 */
600a864dc36Sdarran 	while (!dpr->dpr_quit) {
601a864dc36Sdarran 		const lwpstatus_t *psp;
602a864dc36Sdarran 
603c0855460Schristos #ifdef illumos
604a864dc36Sdarran 		if (write(pfd, &wstop, sizeof (wstop)) == -1 && errno == EINTR)
605a864dc36Sdarran 			continue; /* check dpr_quit and continue waiting */
606bb8023b5Sdarran #else
607bb8023b5Sdarran 		/* Wait for the process to report status. */
608c0855460Schristos 		proc_wstatus(P);
609c0855460Schristos 		if (errno == EINTR)
610c0855460Schristos 			continue; /* check dpr_quit and continue waiting */
611bb8023b5Sdarran #endif
612a864dc36Sdarran 
613a864dc36Sdarran 		(void) pthread_mutex_lock(&dpr->dpr_lock);
614bb8023b5Sdarran 
615c0855460Schristos #ifdef illumos
616a864dc36Sdarran pwait_locked:
617a864dc36Sdarran 		if (Pstopstatus(P, PCNULL, 0) == -1 && errno == EINTR) {
618a864dc36Sdarran 			(void) pthread_mutex_unlock(&dpr->dpr_lock);
619a864dc36Sdarran 			continue; /* check dpr_quit and continue waiting */
620a864dc36Sdarran 		}
621bb8023b5Sdarran #endif
622a864dc36Sdarran 
623a864dc36Sdarran 		switch (Pstate(P)) {
624a864dc36Sdarran 		case PS_STOP:
625c0855460Schristos #ifdef illumos
626a864dc36Sdarran 			psp = &Pstatus(P)->pr_lwp;
627c0855460Schristos #else
628c0855460Schristos 			psp = proc_getlwpstatus(P);
629c0855460Schristos #endif
630a864dc36Sdarran 
631a864dc36Sdarran 			dt_dprintf("pid %d: proc stopped showing %d/%d\n",
632a864dc36Sdarran 			    pid, psp->pr_why, psp->pr_what);
633a864dc36Sdarran 
634a864dc36Sdarran 			/*
635a864dc36Sdarran 			 * If the process stops showing PR_REQUESTED, then the
636a864dc36Sdarran 			 * DTrace stop() action was applied to it or another
637a864dc36Sdarran 			 * debugging utility (e.g. pstop(1)) asked it to stop.
638a864dc36Sdarran 			 * In either case, the user's intention is for the
639a864dc36Sdarran 			 * process to remain stopped until another external
640a864dc36Sdarran 			 * mechanism (e.g. prun(1)) is applied.  So instead of
641a864dc36Sdarran 			 * setting the process running ourself, we wait for
642a864dc36Sdarran 			 * someone else to do so.  Once that happens, we return
643a864dc36Sdarran 			 * to our normal loop waiting for an event of interest.
644a864dc36Sdarran 			 */
645a864dc36Sdarran 			if (psp->pr_why == PR_REQUESTED) {
646a864dc36Sdarran 				dt_proc_waitrun(dpr);
647a864dc36Sdarran 				(void) pthread_mutex_unlock(&dpr->dpr_lock);
648a864dc36Sdarran 				continue;
649a864dc36Sdarran 			}
650a864dc36Sdarran 
651a864dc36Sdarran 			/*
652a864dc36Sdarran 			 * If the process stops showing one of the events that
653a864dc36Sdarran 			 * we are tracing, perform the appropriate response.
654a864dc36Sdarran 			 * Note that we ignore PR_SUSPENDED, PR_CHECKPOINT, and
655a864dc36Sdarran 			 * PR_JOBCONTROL by design: if one of these conditions
656a864dc36Sdarran 			 * occurs, we will fall through to Psetrun() but the
657a864dc36Sdarran 			 * process will remain stopped in the kernel by the
658a864dc36Sdarran 			 * corresponding mechanism (e.g. job control stop).
659a864dc36Sdarran 			 */
660a864dc36Sdarran 			if (psp->pr_why == PR_FAULTED && psp->pr_what == FLTBPT)
661a864dc36Sdarran 				dt_proc_bpmatch(dtp, dpr);
662a864dc36Sdarran 			else if (psp->pr_why == PR_SYSENTRY &&
663a864dc36Sdarran 			    IS_SYS_FORK(psp->pr_what))
664a864dc36Sdarran 				dt_proc_bpdisable(dpr);
665a864dc36Sdarran 			else if (psp->pr_why == PR_SYSEXIT &&
666a864dc36Sdarran 			    IS_SYS_FORK(psp->pr_what))
667a864dc36Sdarran 				dt_proc_bpenable(dpr);
668a864dc36Sdarran 			else if (psp->pr_why == PR_SYSEXIT &&
669a864dc36Sdarran 			    IS_SYS_EXEC(psp->pr_what))
670a864dc36Sdarran 				dt_proc_attach(dpr, B_TRUE);
671a864dc36Sdarran 			break;
672a864dc36Sdarran 
673a864dc36Sdarran 		case PS_LOST:
674c0855460Schristos #ifdef illumos
675a864dc36Sdarran 			if (Preopen(P) == 0)
676a864dc36Sdarran 				goto pwait_locked;
677bb8023b5Sdarran #endif
678a864dc36Sdarran 
679a864dc36Sdarran 			dt_dprintf("pid %d: proc lost: %s\n",
680a864dc36Sdarran 			    pid, strerror(errno));
681a864dc36Sdarran 
682a864dc36Sdarran 			dpr->dpr_quit = B_TRUE;
683a864dc36Sdarran 			notify = B_TRUE;
684a864dc36Sdarran 			break;
685a864dc36Sdarran 
686a864dc36Sdarran 		case PS_UNDEAD:
687a864dc36Sdarran 			dt_dprintf("pid %d: proc died\n", pid);
688a864dc36Sdarran 			dpr->dpr_quit = B_TRUE;
689a864dc36Sdarran 			notify = B_TRUE;
690a864dc36Sdarran 			break;
691a864dc36Sdarran 		}
692a864dc36Sdarran 
693a864dc36Sdarran 		if (Pstate(P) != PS_UNDEAD && Psetrun(P, 0, 0) == -1) {
694a864dc36Sdarran 			dt_dprintf("pid %d: failed to set running: %s\n",
695a864dc36Sdarran 			    (int)dpr->dpr_pid, strerror(errno));
696a864dc36Sdarran 		}
697a864dc36Sdarran 
698a864dc36Sdarran 		(void) pthread_mutex_unlock(&dpr->dpr_lock);
699a864dc36Sdarran 	}
700a864dc36Sdarran 
701a864dc36Sdarran 	/*
702a864dc36Sdarran 	 * If the control thread detected PS_UNDEAD or PS_LOST, then enqueue
703a864dc36Sdarran 	 * the dt_proc_t structure on the dt_proc_hash_t notification list.
704a864dc36Sdarran 	 */
705a864dc36Sdarran 	if (notify)
706a864dc36Sdarran 		dt_proc_notify(dtp, dph, dpr, NULL);
707a864dc36Sdarran 
708a864dc36Sdarran 	/*
709a864dc36Sdarran 	 * Destroy and remove any remaining breakpoints, set dpr_done and clear
710a864dc36Sdarran 	 * dpr_tid to indicate the control thread has exited, and notify any
711a864dc36Sdarran 	 * waiting thread in dt_proc_destroy() that we have succesfully exited.
712a864dc36Sdarran 	 */
713a864dc36Sdarran 	(void) pthread_mutex_lock(&dpr->dpr_lock);
714a864dc36Sdarran 
715a864dc36Sdarran 	dt_proc_bpdestroy(dpr, B_TRUE);
716a864dc36Sdarran 	dpr->dpr_done = B_TRUE;
717a864dc36Sdarran 	dpr->dpr_tid = 0;
718a864dc36Sdarran 
719a864dc36Sdarran 	(void) pthread_cond_broadcast(&dpr->dpr_cv);
720a864dc36Sdarran 	(void) pthread_mutex_unlock(&dpr->dpr_lock);
721a864dc36Sdarran 
722a864dc36Sdarran 	return (NULL);
723a864dc36Sdarran }
724a864dc36Sdarran 
725a864dc36Sdarran /*PRINTFLIKE3*/
726a864dc36Sdarran static struct ps_prochandle *
dt_proc_error(dtrace_hdl_t * dtp,dt_proc_t * dpr,const char * format,...)727a864dc36Sdarran dt_proc_error(dtrace_hdl_t *dtp, dt_proc_t *dpr, const char *format, ...)
728a864dc36Sdarran {
729a864dc36Sdarran 	va_list ap;
730a864dc36Sdarran 
731a864dc36Sdarran 	va_start(ap, format);
732a864dc36Sdarran 	dt_set_errmsg(dtp, NULL, NULL, NULL, 0, format, ap);
733a864dc36Sdarran 	va_end(ap);
734a864dc36Sdarran 
735a864dc36Sdarran 	if (dpr->dpr_proc != NULL)
736a864dc36Sdarran 		Prelease(dpr->dpr_proc, 0);
737a864dc36Sdarran 
738a864dc36Sdarran 	dt_free(dtp, dpr);
739a864dc36Sdarran 	(void) dt_set_errno(dtp, EDT_COMPILER);
740a864dc36Sdarran 	return (NULL);
741a864dc36Sdarran }
742a864dc36Sdarran 
743a864dc36Sdarran dt_proc_t *
dt_proc_lookup(dtrace_hdl_t * dtp,struct ps_prochandle * P,int remove)744a864dc36Sdarran dt_proc_lookup(dtrace_hdl_t *dtp, struct ps_prochandle *P, int remove)
745a864dc36Sdarran {
746a864dc36Sdarran 	dt_proc_hash_t *dph = dtp->dt_procs;
747c0855460Schristos #ifdef illumos
748a864dc36Sdarran 	pid_t pid = Pstatus(P)->pr_pid;
749bb8023b5Sdarran #else
750bb8023b5Sdarran 	pid_t pid = proc_getpid(P);
751bb8023b5Sdarran #endif
752a864dc36Sdarran 	dt_proc_t *dpr, **dpp = &dph->dph_hash[pid & (dph->dph_hashlen - 1)];
753a864dc36Sdarran 
754a864dc36Sdarran 	for (dpr = *dpp; dpr != NULL; dpr = dpr->dpr_hash) {
755a864dc36Sdarran 		if (dpr->dpr_pid == pid)
756a864dc36Sdarran 			break;
757a864dc36Sdarran 		else
758a864dc36Sdarran 			dpp = &dpr->dpr_hash;
759a864dc36Sdarran 	}
760a864dc36Sdarran 
761a864dc36Sdarran 	assert(dpr != NULL);
762a864dc36Sdarran 	assert(dpr->dpr_proc == P);
763a864dc36Sdarran 
764a864dc36Sdarran 	if (remove)
765a864dc36Sdarran 		*dpp = dpr->dpr_hash; /* remove from pid hash chain */
766a864dc36Sdarran 
767a864dc36Sdarran 	return (dpr);
768a864dc36Sdarran }
769a864dc36Sdarran 
770a864dc36Sdarran static void
dt_proc_destroy(dtrace_hdl_t * dtp,struct ps_prochandle * P)771a864dc36Sdarran dt_proc_destroy(dtrace_hdl_t *dtp, struct ps_prochandle *P)
772a864dc36Sdarran {
773a864dc36Sdarran 	dt_proc_t *dpr = dt_proc_lookup(dtp, P, B_FALSE);
774a864dc36Sdarran 	dt_proc_hash_t *dph = dtp->dt_procs;
775a864dc36Sdarran 	dt_proc_notify_t *npr, **npp;
776a864dc36Sdarran 	int rflag;
777a864dc36Sdarran 
778a864dc36Sdarran 	assert(dpr != NULL);
779a864dc36Sdarran 
780a864dc36Sdarran 	/*
781a864dc36Sdarran 	 * If neither PR_KLC nor PR_RLC is set, then the process is stopped by
782a864dc36Sdarran 	 * an external debugger and we were waiting in dt_proc_waitrun().
783a864dc36Sdarran 	 * Leave the process in this condition using PRELEASE_HANG.
784a864dc36Sdarran 	 */
785c0855460Schristos #ifdef illumos
786a864dc36Sdarran 	if (!(Pstatus(dpr->dpr_proc)->pr_flags & (PR_KLC | PR_RLC))) {
787bb8023b5Sdarran #else
788bb8023b5Sdarran 	if (!(proc_getflags(dpr->dpr_proc) & (PR_KLC | PR_RLC))) {
789bb8023b5Sdarran #endif
790a864dc36Sdarran 		dt_dprintf("abandoning pid %d\n", (int)dpr->dpr_pid);
791a864dc36Sdarran 		rflag = PRELEASE_HANG;
792c0855460Schristos #ifdef illumos
793c0855460Schristos 	} else if (Pstatus(dpr->dpr_proc)->pr_flags & PR_KLC) {
794bb8023b5Sdarran #else
795c0855460Schristos 	} else if (proc_getflags(dpr->dpr_proc) & PR_KLC) {
796bb8023b5Sdarran #endif
797c0855460Schristos 		dt_dprintf("killing pid %d\n", (int)dpr->dpr_pid);
798c0855460Schristos 		rflag = PRELEASE_KILL; /* apply kill-on-last-close */
799a864dc36Sdarran 	} else {
800a864dc36Sdarran 		dt_dprintf("releasing pid %d\n", (int)dpr->dpr_pid);
801a252d550Shaad 		rflag = 0; /* apply run-on-last-close */
802a864dc36Sdarran 	}
803a864dc36Sdarran 
804a864dc36Sdarran 	if (dpr->dpr_tid) {
805a864dc36Sdarran 		/*
806a864dc36Sdarran 		 * Set the dpr_quit flag to tell the daemon thread to exit.  We
807a864dc36Sdarran 		 * send it a SIGCANCEL to poke it out of PCWSTOP or any other
808a864dc36Sdarran 		 * long-term /proc system call.  Our daemon threads have POSIX
809a864dc36Sdarran 		 * cancellation disabled, so EINTR will be the only effect.  We
810a864dc36Sdarran 		 * then wait for dpr_done to indicate the thread has exited.
811a864dc36Sdarran 		 *
812a864dc36Sdarran 		 * We can't use pthread_kill() to send SIGCANCEL because the
813a864dc36Sdarran 		 * interface forbids it and we can't use pthread_cancel()
814a864dc36Sdarran 		 * because with cancellation disabled it won't actually
815a864dc36Sdarran 		 * send SIGCANCEL to the target thread, so we use _lwp_kill()
816a864dc36Sdarran 		 * to do the job.  This is all built on evil knowledge of
817a864dc36Sdarran 		 * the details of the cancellation mechanism in libc.
818a864dc36Sdarran 		 */
819a864dc36Sdarran 		(void) pthread_mutex_lock(&dpr->dpr_lock);
820a864dc36Sdarran 		dpr->dpr_quit = B_TRUE;
821c0855460Schristos #ifdef illumos
822a864dc36Sdarran 		(void) _lwp_kill(dpr->dpr_tid, SIGCANCEL);
823c0855460Schristos #elif defined(__FreeBSD__)
824c0855460Schristos 		pthread_kill(dpr->dpr_tid, SIGTHR);
825bb8023b5Sdarran #else
826c0855460Schristos 		pthread_cancel(dpr->dpr_tid);
827bb8023b5Sdarran #endif
828a864dc36Sdarran 
829a864dc36Sdarran 		/*
830a864dc36Sdarran 		 * If the process is currently idling in dt_proc_stop(), re-
831a864dc36Sdarran 		 * enable breakpoints and poke it into running again.
832a864dc36Sdarran 		 */
833a864dc36Sdarran 		if (dpr->dpr_stop & DT_PROC_STOP_IDLE) {
834a864dc36Sdarran 			dt_proc_bpenable(dpr);
835a864dc36Sdarran 			dpr->dpr_stop &= ~DT_PROC_STOP_IDLE;
836a864dc36Sdarran 			(void) pthread_cond_broadcast(&dpr->dpr_cv);
837a864dc36Sdarran 		}
838a864dc36Sdarran 
839a864dc36Sdarran 		while (!dpr->dpr_done)
840a864dc36Sdarran 			(void) pthread_cond_wait(&dpr->dpr_cv, &dpr->dpr_lock);
841a864dc36Sdarran 
842a864dc36Sdarran 		(void) pthread_mutex_unlock(&dpr->dpr_lock);
843a864dc36Sdarran 	}
844a864dc36Sdarran 
845a864dc36Sdarran 	/*
846a864dc36Sdarran 	 * Before we free the process structure, remove this dt_proc_t from the
847a864dc36Sdarran 	 * lookup hash, and then walk the dt_proc_hash_t's notification list
848a864dc36Sdarran 	 * and remove this dt_proc_t if it is enqueued.
849a864dc36Sdarran 	 */
850a864dc36Sdarran 	(void) pthread_mutex_lock(&dph->dph_lock);
851a864dc36Sdarran 	(void) dt_proc_lookup(dtp, P, B_TRUE);
852a864dc36Sdarran 	npp = &dph->dph_notify;
853a864dc36Sdarran 
854a864dc36Sdarran 	while ((npr = *npp) != NULL) {
855a864dc36Sdarran 		if (npr->dprn_dpr == dpr) {
856a864dc36Sdarran 			*npp = npr->dprn_next;
857a864dc36Sdarran 			dt_free(dtp, npr);
858a864dc36Sdarran 		} else {
859a864dc36Sdarran 			npp = &npr->dprn_next;
860a864dc36Sdarran 		}
861a864dc36Sdarran 	}
862a864dc36Sdarran 
863a864dc36Sdarran 	(void) pthread_mutex_unlock(&dph->dph_lock);
864a864dc36Sdarran 
865a864dc36Sdarran 	/*
866a864dc36Sdarran 	 * Remove the dt_proc_list from the LRU list, release the underlying
867a864dc36Sdarran 	 * libproc handle, and free our dt_proc_t data structure.
868a864dc36Sdarran 	 */
869a864dc36Sdarran 	if (dpr->dpr_cacheable) {
870a864dc36Sdarran 		assert(dph->dph_lrucnt != 0);
871a864dc36Sdarran 		dph->dph_lrucnt--;
872a864dc36Sdarran 	}
873a864dc36Sdarran 
874a864dc36Sdarran 	dt_list_delete(&dph->dph_lrulist, dpr);
875a864dc36Sdarran 	Prelease(dpr->dpr_proc, rflag);
876a864dc36Sdarran 	dt_free(dtp, dpr);
877a864dc36Sdarran }
878a864dc36Sdarran 
879a864dc36Sdarran static int
880a864dc36Sdarran dt_proc_create_thread(dtrace_hdl_t *dtp, dt_proc_t *dpr, uint_t stop)
881a864dc36Sdarran {
882a864dc36Sdarran 	dt_proc_control_data_t data;
883a864dc36Sdarran 	sigset_t nset, oset;
884a864dc36Sdarran 	pthread_attr_t a;
885a864dc36Sdarran 	int err;
886a864dc36Sdarran 
887a864dc36Sdarran 	(void) pthread_mutex_lock(&dpr->dpr_lock);
888a864dc36Sdarran 	dpr->dpr_stop |= stop; /* set bit for initial rendezvous */
889a864dc36Sdarran 
890a864dc36Sdarran 	(void) pthread_attr_init(&a);
891a864dc36Sdarran 	(void) pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
892a864dc36Sdarran 
893a864dc36Sdarran 	(void) sigfillset(&nset);
894a864dc36Sdarran 	(void) sigdelset(&nset, SIGABRT);	/* unblocked for assert() */
895c0855460Schristos #ifdef illumos
896a864dc36Sdarran 	(void) sigdelset(&nset, SIGCANCEL);	/* see dt_proc_destroy() */
897bb8023b5Sdarran #else
898bb8023b5Sdarran 	(void) sigdelset(&nset, SIGUSR1);	/* see dt_proc_destroy() */
899bb8023b5Sdarran #endif
900a864dc36Sdarran 
901a864dc36Sdarran 	data.dpcd_hdl = dtp;
902a864dc36Sdarran 	data.dpcd_proc = dpr;
903a864dc36Sdarran 
904a864dc36Sdarran 	(void) pthread_sigmask(SIG_SETMASK, &nset, &oset);
905a864dc36Sdarran 	err = pthread_create(&dpr->dpr_tid, &a, dt_proc_control, &data);
906a864dc36Sdarran 	(void) pthread_sigmask(SIG_SETMASK, &oset, NULL);
907a864dc36Sdarran 
908a864dc36Sdarran 	/*
909a864dc36Sdarran 	 * If the control thread was created, then wait on dpr_cv for either
910a864dc36Sdarran 	 * dpr_done to be set (the victim died or the control thread failed)
911a864dc36Sdarran 	 * or DT_PROC_STOP_IDLE to be set, indicating that the victim is now
912a864dc36Sdarran 	 * stopped by /proc and the control thread is at the rendezvous event.
913a864dc36Sdarran 	 * On success, we return with the process and control thread stopped:
914a864dc36Sdarran 	 * the caller can then apply dt_proc_continue() to resume both.
915a864dc36Sdarran 	 */
916a864dc36Sdarran 	if (err == 0) {
917a864dc36Sdarran 		while (!dpr->dpr_done && !(dpr->dpr_stop & DT_PROC_STOP_IDLE))
918a864dc36Sdarran 			(void) pthread_cond_wait(&dpr->dpr_cv, &dpr->dpr_lock);
919a864dc36Sdarran 
920a864dc36Sdarran 		/*
921a864dc36Sdarran 		 * If dpr_done is set, the control thread aborted before it
922a864dc36Sdarran 		 * reached the rendezvous event.  This is either due to PS_LOST
923a864dc36Sdarran 		 * or PS_UNDEAD (i.e. the process died).  We try to provide a
924a864dc36Sdarran 		 * small amount of useful information to help figure it out.
925a864dc36Sdarran 		 */
926a864dc36Sdarran 		if (dpr->dpr_done) {
927c0855460Schristos #ifdef illumos
928a864dc36Sdarran 			const psinfo_t *prp = Ppsinfo(dpr->dpr_proc);
929a864dc36Sdarran 			int stat = prp ? prp->pr_wstat : 0;
930a864dc36Sdarran 			int pid = dpr->dpr_pid;
931bb8023b5Sdarran #else
932c0855460Schristos 			int stat = proc_getwstat(dpr->dpr_proc);
933c0855460Schristos 			int pid = proc_getpid(dpr->dpr_proc);
934bb8023b5Sdarran #endif
935c0855460Schristos 			if (proc_state(dpr->dpr_proc) == PS_LOST) {
936a864dc36Sdarran 				(void) dt_proc_error(dpr->dpr_hdl, dpr,
937a864dc36Sdarran 				    "failed to control pid %d: process exec'd "
938a864dc36Sdarran 				    "set-id or unobservable program\n", pid);
939a864dc36Sdarran 			} else if (WIFSIGNALED(stat)) {
940a864dc36Sdarran 				(void) dt_proc_error(dpr->dpr_hdl, dpr,
941a864dc36Sdarran 				    "failed to control pid %d: process died "
942a864dc36Sdarran 				    "from signal %d\n", pid, WTERMSIG(stat));
943a864dc36Sdarran 			} else {
944a864dc36Sdarran 				(void) dt_proc_error(dpr->dpr_hdl, dpr,
945a864dc36Sdarran 				    "failed to control pid %d: process exited "
946a864dc36Sdarran 				    "with status %d\n", pid, WEXITSTATUS(stat));
947a864dc36Sdarran 			}
948a864dc36Sdarran 
949a864dc36Sdarran 			err = ESRCH; /* cause grab() or create() to fail */
950a864dc36Sdarran 		}
951a864dc36Sdarran 	} else {
952a864dc36Sdarran 		(void) dt_proc_error(dpr->dpr_hdl, dpr,
953a864dc36Sdarran 		    "failed to create control thread for process-id %d: %s\n",
954a864dc36Sdarran 		    (int)dpr->dpr_pid, strerror(err));
955a864dc36Sdarran 	}
956a864dc36Sdarran 
957c0855460Schristos 	if (err == 0)
958a864dc36Sdarran 		(void) pthread_mutex_unlock(&dpr->dpr_lock);
959a864dc36Sdarran 	(void) pthread_attr_destroy(&a);
960a864dc36Sdarran 
961a864dc36Sdarran 	return (err);
962a864dc36Sdarran }
963a864dc36Sdarran 
964a864dc36Sdarran struct ps_prochandle *
965bb8023b5Sdarran dt_proc_create(dtrace_hdl_t *dtp, const char *file, char *const *argv,
966bb8023b5Sdarran     proc_child_func *pcf, void *child_arg)
967a864dc36Sdarran {
968a864dc36Sdarran 	dt_proc_hash_t *dph = dtp->dt_procs;
969a864dc36Sdarran 	dt_proc_t *dpr;
970a864dc36Sdarran 	int err;
971a864dc36Sdarran 
972a864dc36Sdarran 	if ((dpr = dt_zalloc(dtp, sizeof (dt_proc_t))) == NULL)
973a864dc36Sdarran 		return (NULL); /* errno is set for us */
974a864dc36Sdarran 
975a864dc36Sdarran 	(void) pthread_mutex_init(&dpr->dpr_lock, NULL);
976a864dc36Sdarran 	(void) pthread_cond_init(&dpr->dpr_cv, NULL);
977a864dc36Sdarran 
978c0855460Schristos #ifdef illumos
979a864dc36Sdarran 	if ((dpr->dpr_proc = Pcreate(file, argv, &err, NULL, 0)) == NULL) {
980c0855460Schristos #else
981c0855460Schristos 	if ((err = proc_create(file, argv, pcf, child_arg,
982c0855460Schristos 	    &dpr->dpr_proc)) != 0) {
983c0855460Schristos #endif
984a864dc36Sdarran 		return (dt_proc_error(dtp, dpr,
985a864dc36Sdarran 		    "failed to execute %s: %s\n", file, Pcreate_error(err)));
986a864dc36Sdarran 	}
987a864dc36Sdarran 
988a864dc36Sdarran 	dpr->dpr_hdl = dtp;
989c0855460Schristos #ifdef illumos
990a864dc36Sdarran 	dpr->dpr_pid = Pstatus(dpr->dpr_proc)->pr_pid;
991bb8023b5Sdarran #else
992bb8023b5Sdarran 	dpr->dpr_pid = proc_getpid(dpr->dpr_proc);
993bb8023b5Sdarran #endif
994a864dc36Sdarran 
995c0855460Schristos 	(void) Punsetflags(dpr->dpr_proc, PR_RLC);
996c0855460Schristos 	(void) Psetflags(dpr->dpr_proc, PR_KLC);
997c0855460Schristos 
998a864dc36Sdarran 	if (dt_proc_create_thread(dtp, dpr, dtp->dt_prcmode) != 0)
999a864dc36Sdarran 		return (NULL); /* dt_proc_error() has been called for us */
1000a864dc36Sdarran 
1001a864dc36Sdarran 	dpr->dpr_hash = dph->dph_hash[dpr->dpr_pid & (dph->dph_hashlen - 1)];
1002a864dc36Sdarran 	dph->dph_hash[dpr->dpr_pid & (dph->dph_hashlen - 1)] = dpr;
1003a864dc36Sdarran 	dt_list_prepend(&dph->dph_lrulist, dpr);
1004a864dc36Sdarran 
1005a864dc36Sdarran 	dt_dprintf("created pid %d\n", (int)dpr->dpr_pid);
1006a864dc36Sdarran 	dpr->dpr_refs++;
1007a864dc36Sdarran 
1008a864dc36Sdarran 	return (dpr->dpr_proc);
1009a864dc36Sdarran }
1010a864dc36Sdarran 
1011a864dc36Sdarran struct ps_prochandle *
1012a864dc36Sdarran dt_proc_grab(dtrace_hdl_t *dtp, pid_t pid, int flags, int nomonitor)
1013a864dc36Sdarran {
1014a864dc36Sdarran 	dt_proc_hash_t *dph = dtp->dt_procs;
1015a864dc36Sdarran 	uint_t h = pid & (dph->dph_hashlen - 1);
1016a864dc36Sdarran 	dt_proc_t *dpr, *opr;
1017a864dc36Sdarran 	int err;
1018a864dc36Sdarran 
1019a864dc36Sdarran 	/*
1020a864dc36Sdarran 	 * Search the hash table for the pid.  If it is already grabbed or
1021a864dc36Sdarran 	 * created, move the handle to the front of the lrulist, increment
1022a864dc36Sdarran 	 * the reference count, and return the existing ps_prochandle.
1023a864dc36Sdarran 	 */
1024a864dc36Sdarran 	for (dpr = dph->dph_hash[h]; dpr != NULL; dpr = dpr->dpr_hash) {
1025a864dc36Sdarran 		if (dpr->dpr_pid == pid && !dpr->dpr_stale) {
1026a864dc36Sdarran 			/*
1027a864dc36Sdarran 			 * If the cached handle was opened read-only and
1028a864dc36Sdarran 			 * this request is for a writeable handle, mark
1029a864dc36Sdarran 			 * the cached handle as stale and open a new handle.
1030a864dc36Sdarran 			 * Since it's stale, unmark it as cacheable.
1031a864dc36Sdarran 			 */
1032a864dc36Sdarran 			if (dpr->dpr_rdonly && !(flags & PGRAB_RDONLY)) {
1033a864dc36Sdarran 				dt_dprintf("upgrading pid %d\n", (int)pid);
1034a864dc36Sdarran 				dpr->dpr_stale = B_TRUE;
1035a864dc36Sdarran 				dpr->dpr_cacheable = B_FALSE;
1036a864dc36Sdarran 				dph->dph_lrucnt--;
1037a864dc36Sdarran 				break;
1038a864dc36Sdarran 			}
1039a864dc36Sdarran 
1040a864dc36Sdarran 			dt_dprintf("grabbed pid %d (cached)\n", (int)pid);
1041a864dc36Sdarran 			dt_list_delete(&dph->dph_lrulist, dpr);
1042a864dc36Sdarran 			dt_list_prepend(&dph->dph_lrulist, dpr);
1043a864dc36Sdarran 			dpr->dpr_refs++;
1044a864dc36Sdarran 			return (dpr->dpr_proc);
1045a864dc36Sdarran 		}
1046a864dc36Sdarran 	}
1047a864dc36Sdarran 
1048a864dc36Sdarran 	if ((dpr = dt_zalloc(dtp, sizeof (dt_proc_t))) == NULL)
1049a864dc36Sdarran 		return (NULL); /* errno is set for us */
1050a864dc36Sdarran 
1051a864dc36Sdarran 	(void) pthread_mutex_init(&dpr->dpr_lock, NULL);
1052a864dc36Sdarran 	(void) pthread_cond_init(&dpr->dpr_cv, NULL);
1053a864dc36Sdarran 
1054c0855460Schristos #ifdef illumos
1055a864dc36Sdarran 	if ((dpr->dpr_proc = Pgrab(pid, flags, &err)) == NULL) {
1056c0855460Schristos #else
1057c0855460Schristos 	if ((err = proc_attach(pid, flags, &dpr->dpr_proc)) != 0) {
1058c0855460Schristos #endif
1059a864dc36Sdarran 		return (dt_proc_error(dtp, dpr,
1060a864dc36Sdarran 		    "failed to grab pid %d: %s\n", (int)pid, Pgrab_error(err)));
1061a864dc36Sdarran 	}
1062a864dc36Sdarran 
1063a864dc36Sdarran 	dpr->dpr_hdl = dtp;
1064a864dc36Sdarran 	dpr->dpr_pid = pid;
1065a864dc36Sdarran 
1066a864dc36Sdarran 	(void) Punsetflags(dpr->dpr_proc, PR_KLC);
1067a864dc36Sdarran 	(void) Psetflags(dpr->dpr_proc, PR_RLC);
1068a864dc36Sdarran 
1069a864dc36Sdarran 	/*
1070a864dc36Sdarran 	 * If we are attempting to grab the process without a monitor
1071a864dc36Sdarran 	 * thread, then mark the process cacheable only if it's being
1072a864dc36Sdarran 	 * grabbed read-only.  If we're currently caching more process
1073a864dc36Sdarran 	 * handles than dph_lrulim permits, attempt to find the
1074a864dc36Sdarran 	 * least-recently-used handle that is currently unreferenced and
1075a864dc36Sdarran 	 * release it from the cache.  Otherwise we are grabbing the process
1076a864dc36Sdarran 	 * for control: create a control thread for this process and store
1077a864dc36Sdarran 	 * its ID in dpr->dpr_tid.
1078a864dc36Sdarran 	 */
1079a864dc36Sdarran 	if (nomonitor || (flags & PGRAB_RDONLY)) {
1080a864dc36Sdarran 		if (dph->dph_lrucnt >= dph->dph_lrulim) {
1081a864dc36Sdarran 			for (opr = dt_list_prev(&dph->dph_lrulist);
1082a864dc36Sdarran 			    opr != NULL; opr = dt_list_prev(opr)) {
1083a864dc36Sdarran 				if (opr->dpr_cacheable && opr->dpr_refs == 0) {
1084a864dc36Sdarran 					dt_proc_destroy(dtp, opr->dpr_proc);
1085a864dc36Sdarran 					break;
1086a864dc36Sdarran 				}
1087a864dc36Sdarran 			}
1088a864dc36Sdarran 		}
1089a864dc36Sdarran 
1090a864dc36Sdarran 		if (flags & PGRAB_RDONLY) {
1091a864dc36Sdarran 			dpr->dpr_cacheable = B_TRUE;
1092a864dc36Sdarran 			dpr->dpr_rdonly = B_TRUE;
1093a864dc36Sdarran 			dph->dph_lrucnt++;
1094a864dc36Sdarran 		}
1095a864dc36Sdarran 
1096a864dc36Sdarran 	} else if (dt_proc_create_thread(dtp, dpr, DT_PROC_STOP_GRAB) != 0)
1097a864dc36Sdarran 		return (NULL); /* dt_proc_error() has been called for us */
1098a864dc36Sdarran 
1099a864dc36Sdarran 	dpr->dpr_hash = dph->dph_hash[h];
1100a864dc36Sdarran 	dph->dph_hash[h] = dpr;
1101a864dc36Sdarran 	dt_list_prepend(&dph->dph_lrulist, dpr);
1102a864dc36Sdarran 
1103a864dc36Sdarran 	dt_dprintf("grabbed pid %d\n", (int)pid);
1104a864dc36Sdarran 	dpr->dpr_refs++;
1105a864dc36Sdarran 
1106a864dc36Sdarran 	return (dpr->dpr_proc);
1107a864dc36Sdarran }
1108a864dc36Sdarran 
1109a864dc36Sdarran void
1110a864dc36Sdarran dt_proc_release(dtrace_hdl_t *dtp, struct ps_prochandle *P)
1111a864dc36Sdarran {
1112a864dc36Sdarran 	dt_proc_t *dpr = dt_proc_lookup(dtp, P, B_FALSE);
1113a864dc36Sdarran 	dt_proc_hash_t *dph = dtp->dt_procs;
1114a864dc36Sdarran 
1115a864dc36Sdarran 	assert(dpr != NULL);
1116a864dc36Sdarran 	assert(dpr->dpr_refs != 0);
1117a864dc36Sdarran 
1118a864dc36Sdarran 	if (--dpr->dpr_refs == 0 &&
1119a864dc36Sdarran 	    (!dpr->dpr_cacheable || dph->dph_lrucnt > dph->dph_lrulim))
1120a864dc36Sdarran 		dt_proc_destroy(dtp, P);
1121a864dc36Sdarran }
1122a864dc36Sdarran 
1123a864dc36Sdarran void
1124a864dc36Sdarran dt_proc_continue(dtrace_hdl_t *dtp, struct ps_prochandle *P)
1125a864dc36Sdarran {
1126a864dc36Sdarran 	dt_proc_t *dpr = dt_proc_lookup(dtp, P, B_FALSE);
1127a864dc36Sdarran 
1128a864dc36Sdarran 	(void) pthread_mutex_lock(&dpr->dpr_lock);
1129a864dc36Sdarran 
1130a864dc36Sdarran 	if (dpr->dpr_stop & DT_PROC_STOP_IDLE) {
1131a864dc36Sdarran 		dpr->dpr_stop &= ~DT_PROC_STOP_IDLE;
1132a864dc36Sdarran 		(void) pthread_cond_broadcast(&dpr->dpr_cv);
1133a864dc36Sdarran 	}
1134a864dc36Sdarran 
1135a864dc36Sdarran 	(void) pthread_mutex_unlock(&dpr->dpr_lock);
1136a864dc36Sdarran }
1137a864dc36Sdarran 
1138a864dc36Sdarran void
1139a864dc36Sdarran dt_proc_lock(dtrace_hdl_t *dtp, struct ps_prochandle *P)
1140a864dc36Sdarran {
1141a864dc36Sdarran 	dt_proc_t *dpr = dt_proc_lookup(dtp, P, B_FALSE);
1142a864dc36Sdarran 	int err = pthread_mutex_lock(&dpr->dpr_lock);
1143a864dc36Sdarran 	assert(err == 0); /* check for recursion */
1144a864dc36Sdarran }
1145a864dc36Sdarran 
1146a864dc36Sdarran void
1147a864dc36Sdarran dt_proc_unlock(dtrace_hdl_t *dtp, struct ps_prochandle *P)
1148a864dc36Sdarran {
1149a864dc36Sdarran 	dt_proc_t *dpr = dt_proc_lookup(dtp, P, B_FALSE);
1150a864dc36Sdarran 	int err = pthread_mutex_unlock(&dpr->dpr_lock);
1151a864dc36Sdarran 	assert(err == 0); /* check for unheld lock */
1152a864dc36Sdarran }
1153a864dc36Sdarran 
1154a864dc36Sdarran void
1155a864dc36Sdarran dt_proc_hash_create(dtrace_hdl_t *dtp)
1156a864dc36Sdarran {
1157a864dc36Sdarran 	if ((dtp->dt_procs = dt_zalloc(dtp, sizeof (dt_proc_hash_t) +
1158a864dc36Sdarran 	    sizeof (dt_proc_t *) * _dtrace_pidbuckets - 1)) != NULL) {
1159a864dc36Sdarran 
1160a864dc36Sdarran 		(void) pthread_mutex_init(&dtp->dt_procs->dph_lock, NULL);
1161a864dc36Sdarran 		(void) pthread_cond_init(&dtp->dt_procs->dph_cv, NULL);
1162a864dc36Sdarran 
1163a864dc36Sdarran 		dtp->dt_procs->dph_hashlen = _dtrace_pidbuckets;
1164a864dc36Sdarran 		dtp->dt_procs->dph_lrulim = _dtrace_pidlrulim;
1165a864dc36Sdarran 	}
1166a864dc36Sdarran }
1167a864dc36Sdarran 
1168a864dc36Sdarran void
1169a864dc36Sdarran dt_proc_hash_destroy(dtrace_hdl_t *dtp)
1170a864dc36Sdarran {
1171a864dc36Sdarran 	dt_proc_hash_t *dph = dtp->dt_procs;
1172a864dc36Sdarran 	dt_proc_t *dpr;
1173a864dc36Sdarran 
1174a864dc36Sdarran 	while ((dpr = dt_list_next(&dph->dph_lrulist)) != NULL)
1175a864dc36Sdarran 		dt_proc_destroy(dtp, dpr->dpr_proc);
1176a864dc36Sdarran 
1177a864dc36Sdarran 	dtp->dt_procs = NULL;
1178a864dc36Sdarran 	dt_free(dtp, dph);
1179a864dc36Sdarran }
1180a864dc36Sdarran 
1181a864dc36Sdarran struct ps_prochandle *
1182bb8023b5Sdarran dtrace_proc_create(dtrace_hdl_t *dtp, const char *file, char *const *argv,
1183bb8023b5Sdarran     proc_child_func *pcf, void *child_arg)
1184a864dc36Sdarran {
1185a864dc36Sdarran 	dt_ident_t *idp = dt_idhash_lookup(dtp->dt_macros, "target");
1186bb8023b5Sdarran 	struct ps_prochandle *P = dt_proc_create(dtp, file, argv, pcf, child_arg);
1187a864dc36Sdarran 
1188c0855460Schristos 	if (P != NULL && idp != NULL && idp->di_id == 0) {
1189c0855460Schristos #ifdef illumos
1190a864dc36Sdarran 		idp->di_id = Pstatus(P)->pr_pid; /* $target = created pid */
1191bb8023b5Sdarran #else
1192bb8023b5Sdarran 		idp->di_id = proc_getpid(P); /* $target = created pid */
1193bb8023b5Sdarran #endif
1194c0855460Schristos 	}
1195a864dc36Sdarran 
1196a864dc36Sdarran 	return (P);
1197a864dc36Sdarran }
1198a864dc36Sdarran 
1199a864dc36Sdarran struct ps_prochandle *
1200a864dc36Sdarran dtrace_proc_grab(dtrace_hdl_t *dtp, pid_t pid, int flags)
1201a864dc36Sdarran {
1202a864dc36Sdarran 	dt_ident_t *idp = dt_idhash_lookup(dtp->dt_macros, "target");
1203a864dc36Sdarran 	struct ps_prochandle *P = dt_proc_grab(dtp, pid, flags, 0);
1204a864dc36Sdarran 
1205a864dc36Sdarran 	if (P != NULL && idp != NULL && idp->di_id == 0)
1206a864dc36Sdarran 		idp->di_id = pid; /* $target = grabbed pid */
1207a864dc36Sdarran 
1208a864dc36Sdarran 	return (P);
1209a864dc36Sdarran }
1210a864dc36Sdarran 
1211a864dc36Sdarran void
1212a864dc36Sdarran dtrace_proc_release(dtrace_hdl_t *dtp, struct ps_prochandle *P)
1213a864dc36Sdarran {
1214a864dc36Sdarran 	dt_proc_release(dtp, P);
1215a864dc36Sdarran }
1216a864dc36Sdarran 
1217a864dc36Sdarran void
1218a864dc36Sdarran dtrace_proc_continue(dtrace_hdl_t *dtp, struct ps_prochandle *P)
1219a864dc36Sdarran {
1220a864dc36Sdarran 	dt_proc_continue(dtp, P);
1221a864dc36Sdarran }
1222