libpthread_db.c revision 133047
1132332Smarcel/*
2132332Smarcel * Copyright (c) 2004 David Xu <davidxu@freebsd.org>
3132332Smarcel * All rights reserved.
4132332Smarcel *
5132332Smarcel * Redistribution and use in source and binary forms, with or without
6132332Smarcel * modification, are permitted provided that the following conditions
7132332Smarcel * are met:
8132332Smarcel * 1. Redistributions of source code must retain the above copyright
9132332Smarcel *    notice, this list of conditions and the following disclaimer.
10132332Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11132332Smarcel *    notice, this list of conditions and the following disclaimer in the
12132332Smarcel *    documentation and/or other materials provided with the distribution.
13132332Smarcel *
14132332Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15132332Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16132332Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17132332Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18132332Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19132332Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20132332Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21132332Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22132332Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23132332Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24132332Smarcel * SUCH DAMAGE.
25132332Smarcel */
26132332Smarcel
27132332Smarcel#include <sys/cdefs.h>
28132332Smarcel__FBSDID("$FreeBSD: head/lib/libthread_db/libpthread_db.c 133047 2004-08-03 02:23:06Z davidxu $");
29132332Smarcel
30132332Smarcel#include <stddef.h>
31132332Smarcel#include <stdlib.h>
32132332Smarcel#include <string.h>
33132332Smarcel#include <unistd.h>
34132332Smarcel#include <pthread.h>
35132332Smarcel#include <sys/types.h>
36132332Smarcel#include <sys/kse.h>
37132332Smarcel#include <sys/ptrace.h>
38132332Smarcel#include <proc_service.h>
39132332Smarcel#include <thread_db.h>
40132332Smarcel
41132332Smarcel#include "libpthread.h"
42132332Smarcel#include "libpthread_db.h"
43132332Smarcel
44132332Smarcel#define P2T(c) ps2td(c)
45132332Smarcel
46132332Smarcelstatic void pt_unmap_lwp(const td_thragent_t *ta, lwpid_t lwp);
47132332Smarcelstatic int pt_validate(const td_thrhandle_t *th);
48132332Smarcel
49132332Smarcelstatic int
50132332Smarcelps2td(int c)
51132332Smarcel{
52132332Smarcel	switch (c) {
53132332Smarcel	case PS_OK:
54132332Smarcel		return TD_OK;
55132332Smarcel	case PS_ERR:
56132332Smarcel		return TD_ERR;
57132332Smarcel	case PS_BADPID:
58132332Smarcel		return TD_BADPH;
59132332Smarcel	case PS_BADLID:
60132332Smarcel		return TD_NOLWP;
61132332Smarcel	case PS_BADADDR:
62132332Smarcel		return TD_ERR;
63132332Smarcel	case PS_NOSYM:
64132332Smarcel		return TD_NOLIBTHREAD;
65132332Smarcel	case PS_NOFREGS:
66132332Smarcel		return TD_NOFPREGS;
67132332Smarcel	default:
68132332Smarcel		return TD_ERR;
69132332Smarcel	}
70132332Smarcel}
71132332Smarcel
72132332Smarcelstatic long
73132332Smarcelpt_map_thread(const td_thragent_t *const_ta, psaddr_t pt, int type)
74132332Smarcel{
75132332Smarcel	td_thragent_t *ta = __DECONST(td_thragent_t *, const_ta);
76132332Smarcel	struct pt_map *new;
77132332Smarcel	int i, first = -1;
78132332Smarcel
79132332Smarcel	/* leave zero out */
80132332Smarcel	for (i = 1; i < ta->map_len; ++i) {
81132332Smarcel		if (ta->map[i].type == PT_NONE) {
82132332Smarcel			if (first == -1)
83132332Smarcel				first = i;
84132332Smarcel		} else if (ta->map[i].type == type && ta->map[i].thr == pt) {
85132332Smarcel				return (i);
86132332Smarcel		}
87132332Smarcel	}
88132332Smarcel
89132332Smarcel	if (first == -1) {
90132332Smarcel		if (ta->map_len == 0) {
91132332Smarcel			ta->map = calloc(20, sizeof(struct pt_map));
92132332Smarcel			if (ta->map == NULL)
93132332Smarcel				return (-1);
94132332Smarcel			ta->map_len = 20;
95132332Smarcel			first = 1;
96132332Smarcel		} else {
97132332Smarcel			new = realloc(ta->map,
98132332Smarcel			              sizeof(struct pt_map) * ta->map_len * 2);
99132332Smarcel			if (new == NULL)
100132332Smarcel				return (-1);
101132332Smarcel			memset(new + ta->map_len, '\0', sizeof(struct pt_map) *
102132332Smarcel			       ta->map_len);
103132332Smarcel			first = ta->map_len;
104132332Smarcel			ta->map = new;
105132332Smarcel			ta->map_len *= 2;
106132332Smarcel		}
107132332Smarcel	}
108132332Smarcel
109132332Smarcel	ta->map[first].type = type;
110132332Smarcel	ta->map[first].thr = pt;
111132332Smarcel	return (first);
112132332Smarcel}
113132332Smarcel
114132332Smarcelstatic td_err_e
115132332Smarcelpt_init(void)
116132332Smarcel{
117132332Smarcel	pt_md_init();
118132332Smarcel	return (0);
119132332Smarcel}
120132332Smarcel
121132332Smarcelstatic td_err_e
122132332Smarcelpt_ta_new(struct ps_prochandle *ph, td_thragent_t **pta)
123132332Smarcel{
124132332Smarcel#define LOOKUP_SYM(proc, sym, addr) 			\
125132332Smarcel	ret = ps_pglobal_lookup(proc, NULL, sym, addr);	\
126132332Smarcel	if (ret != 0) {					\
127132332Smarcel		TDBG("can not find symbol: %s\n", sym);	\
128132332Smarcel		ret = TD_NOLIBTHREAD;			\
129132332Smarcel		goto error;				\
130132332Smarcel	}
131132332Smarcel
132132332Smarcel	td_thragent_t *ta;
133132332Smarcel	int dbg;
134132332Smarcel	int ret;
135132332Smarcel
136132332Smarcel	TDBG_FUNC();
137132332Smarcel
138132332Smarcel	ta = malloc(sizeof(td_thragent_t));
139132332Smarcel	if (ta == NULL)
140132332Smarcel		return (TD_MALLOC);
141132332Smarcel
142132332Smarcel	ta->ph = ph;
143132332Smarcel	ta->thread_activated = 0;
144132332Smarcel	ta->map = NULL;
145132332Smarcel	ta->map_len = 0;
146132332Smarcel
147132332Smarcel	LOOKUP_SYM(ph, "_libkse_debug",		&ta->libkse_debug_addr);
148132332Smarcel	LOOKUP_SYM(ph, "_thread_list",		&ta->thread_list_addr);
149132332Smarcel	LOOKUP_SYM(ph, "_thread_activated",	&ta->thread_activated_addr);
150132332Smarcel	LOOKUP_SYM(ph, "_thread_active_threads",&ta->thread_active_threads_addr);
151132332Smarcel	LOOKUP_SYM(ph, "_thread_keytable",	&ta->thread_keytable_addr);
152132332Smarcel
153132332Smarcel	dbg = getpid();
154132332Smarcel	/*
155132332Smarcel	 * If this fails it probably means we're debugging a core file and
156132332Smarcel	 * can't write to it.
157132332Smarcel	 */
158132332Smarcel	ps_pwrite(ph, ta->libkse_debug_addr, &dbg, sizeof(int));
159132332Smarcel	*pta = ta;
160132332Smarcel	return (0);
161132332Smarcel
162132332Smarcelerror:
163132332Smarcel	free(ta);
164132332Smarcel	return (ret);
165132332Smarcel}
166132332Smarcel
167132332Smarcelstatic td_err_e
168132332Smarcelpt_ta_delete(td_thragent_t *ta)
169132332Smarcel{
170132332Smarcel	int dbg;
171132332Smarcel
172132332Smarcel	TDBG_FUNC();
173132332Smarcel
174132332Smarcel	dbg = 0;
175132332Smarcel	/*
176132332Smarcel	 * Error returns from this write are not really a problem;
177132332Smarcel	 * the process doesn't exist any more.
178132332Smarcel	 */
179132332Smarcel	ps_pwrite(ta->ph, ta->libkse_debug_addr, &dbg, sizeof(int));
180132332Smarcel	if (ta->map)
181132332Smarcel		free(ta->map);
182132332Smarcel	free(ta);
183132332Smarcel	return (TD_OK);
184132332Smarcel}
185132332Smarcel
186132332Smarcelstatic td_err_e
187132332Smarcelpt_ta_map_id2thr(const td_thragent_t *ta, thread_t id, td_thrhandle_t *th)
188132332Smarcel{
189132332Smarcel	prgregset_t gregs;
190132332Smarcel	TAILQ_HEAD(, pthread) thread_list;
191132332Smarcel	psaddr_t pt, tcb_addr;
192132332Smarcel	lwpid_t lwp;
193132332Smarcel	int ret;
194132332Smarcel
195132332Smarcel	TDBG_FUNC();
196132332Smarcel
197132332Smarcel	if (id < 0 || id >= ta->map_len || ta->map[id].type == PT_NONE)
198132332Smarcel		return (TD_NOTHR);
199132332Smarcel	ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
200132332Smarcel			sizeof(thread_list));
201132332Smarcel	if (ret != 0)
202132332Smarcel		return (P2T(ret));
203132332Smarcel	pt = (psaddr_t)thread_list.tqh_first;
204132332Smarcel	if (ta->map[id].type == PT_LWP) {
205132332Smarcel		/*
206132332Smarcel		 * if we are referencing a lwp, make sure it was not already
207132332Smarcel		 * mapped to user thread.
208132332Smarcel		 */
209132332Smarcel		while (pt != 0) {
210132332Smarcel			ret = ps_pread(ta->ph,
211132332Smarcel			        pt + offsetof(struct pthread, tcb),
212132332Smarcel			        &tcb_addr, sizeof(tcb_addr));
213132332Smarcel			if (ret != 0)
214132332Smarcel				return (P2T(ret));
215132332Smarcel			ret = ps_pread(ta->ph,
216132332Smarcel			        tcb_addr + offsetof(struct tcb,
217132332Smarcel				  tcb_tmbx.tm_lwp),
218132332Smarcel				&lwp, sizeof(lwp));
219132332Smarcel			if (ret != 0)
220132332Smarcel				return (P2T(ret));
221132332Smarcel			/*
222132332Smarcel			 * If the lwp was already mapped to userland thread,
223132332Smarcel			 * we shouldn't reference it directly in future.
224132332Smarcel			 */
225132332Smarcel			if (lwp == ta->map[id].lwp) {
226132332Smarcel				ta->map[id].type = PT_NONE;
227132332Smarcel				return (TD_NOTHR);
228132332Smarcel			}
229132332Smarcel			/* get next thread */
230132332Smarcel			ret = ps_pread(ta->ph,
231132332Smarcel			        pt + offsetof(struct pthread, tle.tqe_next),
232132332Smarcel			        &pt, sizeof(pt));
233132332Smarcel			if (ret != 0)
234132332Smarcel				return (P2T(ret));
235132332Smarcel		}
236132332Smarcel		/* check lwp */
237132332Smarcel		ret = ptrace(PT_GETREGS, ta->map[id].lwp, (caddr_t)&gregs, 0);
238132332Smarcel		if (ret != 0) {
239132332Smarcel			/* no longer exists */
240132332Smarcel			ta->map[id].type = PT_NONE;
241132332Smarcel			return (TD_NOTHR);
242132332Smarcel		}
243132332Smarcel	} else {
244132332Smarcel		while (pt != 0 && ta->map[id].thr != pt) {
245132332Smarcel			ret = ps_pread(ta->ph,
246132332Smarcel				pt + offsetof(struct pthread, tcb),
247132332Smarcel				&tcb_addr, sizeof(tcb_addr));
248132332Smarcel			if (ret != 0)
249132332Smarcel				return (P2T(ret));
250132332Smarcel			/* get next thread */
251132332Smarcel			ret = ps_pread(ta->ph,
252132332Smarcel				pt + offsetof(struct pthread, tle.tqe_next),
253132332Smarcel				&pt, sizeof(pt));
254132332Smarcel			if (ret != 0)
255132332Smarcel				return (P2T(ret));
256132332Smarcel		}
257132332Smarcel
258132332Smarcel		if (pt == 0) {
259132332Smarcel			/* no longer exists */
260132332Smarcel			ta->map[id].type = PT_NONE;
261132332Smarcel			return (TD_NOTHR);
262132332Smarcel		}
263132332Smarcel	}
264132332Smarcel	th->th_ta = ta;
265132332Smarcel	th->th_tid = id;
266132332Smarcel	return (TD_OK);
267132332Smarcel}
268132332Smarcel
269132332Smarcelstatic td_err_e
270132332Smarcelpt_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwp, td_thrhandle_t *th)
271132332Smarcel{
272132332Smarcel	TAILQ_HEAD(, pthread) thread_list;
273132332Smarcel	psaddr_t pt, ptr;
274132332Smarcel	lwpid_t tmp_lwp;
275132332Smarcel	int ret;
276132332Smarcel
277132332Smarcel	TDBG_FUNC();
278132332Smarcel
279132332Smarcel	ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
280132332Smarcel	                sizeof(thread_list));
281132332Smarcel	if (ret != 0)
282132332Smarcel		return (P2T(ret));
283132332Smarcel	pt = (psaddr_t)thread_list.tqh_first;
284132332Smarcel	while (pt != 0) {
285132332Smarcel		ret = ps_pread(ta->ph, pt + offsetof(struct pthread, tcb),
286132332Smarcel				&ptr, sizeof(ptr));
287132332Smarcel		if (ret != 0)
288132332Smarcel			return (P2T(ret));
289132332Smarcel		ptr += offsetof(struct tcb, tcb_tmbx.tm_lwp);
290132332Smarcel		ret = ps_pread(ta->ph, ptr, &tmp_lwp, sizeof(lwpid_t));
291132332Smarcel		if (ret != 0)
292132332Smarcel			return (P2T(ret));
293132332Smarcel		if (tmp_lwp == lwp) {
294132332Smarcel			th->th_ta = ta;
295132332Smarcel			th->th_tid = pt_map_thread(ta, pt, PT_USER);
296132332Smarcel			if (th->th_tid == -1)
297132332Smarcel				return (TD_MALLOC);
298132332Smarcel			pt_unmap_lwp(ta, lwp);
299132332Smarcel			return (TD_OK);
300132332Smarcel		}
301132332Smarcel
302132332Smarcel		/* get next thread */
303132332Smarcel		ret = ps_pread(ta->ph,
304132332Smarcel		           pt + offsetof(struct pthread, tle.tqe_next),
305132332Smarcel		           &pt, sizeof(pt));
306132332Smarcel		if (ret != 0)
307132332Smarcel			return (P2T(ret));
308132332Smarcel	}
309132332Smarcel
310132332Smarcel	return (TD_NOTHR);
311132332Smarcel}
312132332Smarcel
313132332Smarcelstatic td_err_e
314132332Smarcelpt_ta_thr_iter(const td_thragent_t *ta,
315132332Smarcel               td_thr_iter_f *callback, void *cbdata_p,
316132332Smarcel               td_thr_state_e state, int ti_pri,
317132332Smarcel               sigset_t *ti_sigmask_p,
318132332Smarcel               unsigned int ti_user_flags)
319132332Smarcel{
320132332Smarcel	TAILQ_HEAD(, pthread) thread_list;
321132332Smarcel	td_thrhandle_t th;
322132332Smarcel	psaddr_t pt;
323132332Smarcel	ps_err_e pserr;
324132332Smarcel	int activated;
325132332Smarcel
326132332Smarcel	TDBG_FUNC();
327132332Smarcel
328132332Smarcel	pserr = ps_pread(ta->ph, ta->thread_activated_addr, &activated,
329132332Smarcel	    sizeof(int));
330132332Smarcel	if (pserr != PS_OK)
331132332Smarcel		return (P2T(pserr));
332132332Smarcel	if (!activated)
333132332Smarcel		return (TD_OK);
334132332Smarcel
335132332Smarcel	pserr = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
336132332Smarcel	    sizeof(thread_list));
337132332Smarcel	if (pserr != 0)
338132332Smarcel		return (P2T(pserr));
339132332Smarcel	pt = (psaddr_t)thread_list.tqh_first;
340132332Smarcel	while (pt != 0) {
341132332Smarcel		th.th_ta = ta;
342132332Smarcel		th.th_tid = pt_map_thread(ta, pt, PT_USER);
343132332Smarcel		/* should we unmap lwp here ? */
344132332Smarcel		if (th.th_tid == -1)
345132332Smarcel			return (TD_MALLOC);
346132332Smarcel		if ((*callback)(&th, cbdata_p))
347132332Smarcel			return (TD_DBERR);
348132332Smarcel		/* get next thread */
349132332Smarcel		pserr = ps_pread(ta->ph,
350132332Smarcel		    pt + offsetof(struct pthread, tle.tqe_next), &pt,
351132332Smarcel		    sizeof(pt));
352132332Smarcel		if (pserr != PS_OK)
353132332Smarcel			return (P2T(pserr));
354132332Smarcel	}
355132332Smarcel	return (TD_OK);
356132332Smarcel}
357132332Smarcel
358132332Smarcelstatic td_err_e
359132332Smarcelpt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg)
360132332Smarcel{
361132332Smarcel	struct pthread_key keytable[PTHREAD_KEYS_MAX];
362132332Smarcel	int i, ret;
363132332Smarcel
364132332Smarcel	TDBG_FUNC();
365132332Smarcel
366132332Smarcel	ret = ps_pread(ta->ph, (psaddr_t)ta->thread_keytable_addr, keytable,
367132332Smarcel	                sizeof(keytable));
368132332Smarcel	if (ret != 0)
369132332Smarcel		return (P2T(ret));
370132332Smarcel
371132332Smarcel	for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
372132332Smarcel		if (keytable[i].allocated) {
373132332Smarcel			ret = (ki)(i, keytable[i].destructor, arg);
374132332Smarcel			if (ret != 0)
375132332Smarcel				return (TD_DBERR);
376132332Smarcel		}
377132332Smarcel	}
378132332Smarcel	return (TD_OK);
379132332Smarcel}
380132332Smarcel
381132332Smarcelstatic td_err_e
382132332Smarcelpt_ta_event_addr(const td_thragent_t *ta, td_event_e event, td_notify_t *ptr)
383132332Smarcel{
384132332Smarcel	TDBG_FUNC();
385132332Smarcel	return (TD_NOEVENT);
386132332Smarcel}
387132332Smarcel
388132332Smarcelstatic td_err_e
389132332Smarcelpt_ta_set_event(const td_thragent_t *ta, td_thr_events_t *events)
390132332Smarcel{
391132332Smarcel	TDBG_FUNC();
392132332Smarcel	return (TD_ERR);
393132332Smarcel}
394132332Smarcel
395132332Smarcelstatic td_err_e
396132332Smarcelpt_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *events)
397132332Smarcel{
398132332Smarcel	TDBG_FUNC();
399132332Smarcel	return (TD_ERR);
400132332Smarcel}
401132332Smarcel
402132332Smarcelstatic td_err_e
403132332Smarcelpt_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg)
404132332Smarcel{
405132332Smarcel	TDBG_FUNC();
406132332Smarcel	return (TD_NOMSG);
407132332Smarcel}
408132332Smarcel
409132332Smarcelstatic td_err_e
410132951Sdavidxupt_dbsuspend(const td_thrhandle_t *th, int suspend)
411132951Sdavidxu{
412132951Sdavidxu	td_thragent_t *ta = (td_thragent_t *)th->th_ta;
413132951Sdavidxu	psaddr_t tcb_addr, tmbx_addr, ptr;
414132951Sdavidxu	lwpid_t lwp;
415132951Sdavidxu	uint32_t dflags;
416132951Sdavidxu	int attrflags;
417132951Sdavidxu	int ret;
418132951Sdavidxu
419132951Sdavidxu	TDBG_FUNC();
420132951Sdavidxu
421132951Sdavidxu	ret = pt_validate(th);
422132951Sdavidxu	if (ret)
423132951Sdavidxu		return (ret);
424132951Sdavidxu
425132951Sdavidxu	if (ta->map[th->th_tid].type == PT_LWP) {
426132951Sdavidxu		if (suspend)
427132951Sdavidxu			ret = ps_lstop(ta->ph, ta->map[th->th_tid].lwp);
428132951Sdavidxu		else
429132951Sdavidxu			ret = ps_lcontinue(ta->ph, ta->map[th->th_tid].lwp);
430132951Sdavidxu		return (P2T(ret));
431132951Sdavidxu	}
432132951Sdavidxu
433132951Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
434132951Sdavidxu		offsetof(struct pthread, attr.flags),
435132951Sdavidxu		&attrflags, sizeof(attrflags));
436132951Sdavidxu	if (ret != 0)
437132951Sdavidxu		return (P2T(ret));
438132951Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
439132951Sdavidxu	                offsetof(struct pthread, tcb),
440132951Sdavidxu	                &tcb_addr, sizeof(tcb_addr));
441132951Sdavidxu	if (ret != 0)
442132951Sdavidxu		return (P2T(ret));
443132951Sdavidxu	tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
444132951Sdavidxu	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
445132951Sdavidxu	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
446132951Sdavidxu	if (ret != 0)
447132951Sdavidxu		return (P2T(ret));
448132951Sdavidxu	/*
449132951Sdavidxu	 * Don't stop lwp assigned to a M:N thread, it belongs
450132951Sdavidxu	 * to UTS, UTS shouldn't be stopped.
451132951Sdavidxu	 */
452132951Sdavidxu	if (lwp != 0 && (attrflags & PTHREAD_SCOPE_SYSTEM)) {
453132951Sdavidxu		/* dont' suspend signal thread */
454132951Sdavidxu		if (attrflags & THR_SIGNAL_THREAD)
455132951Sdavidxu			return 0;
456132951Sdavidxu		ptr = ta->map[th->th_tid].thr +
457132951Sdavidxu	                offsetof(struct pthread, kse);
458132951Sdavidxu		/* Too many indirect level :-( */
459132951Sdavidxu		/* read struct kse * */
460132951Sdavidxu		ret = ps_pread(ta->ph, ptr, &ptr, sizeof(ptr));
461132951Sdavidxu		if (ret != 0)
462132951Sdavidxu			return (P2T(ret));
463132951Sdavidxu		ptr = ptr + offsetof(struct kse, k_kcb);
464132951Sdavidxu		/* read k_kcb * */
465132951Sdavidxu		ret = ps_pread(ta->ph, ptr, &ptr, sizeof(ptr));
466132951Sdavidxu		if (ret != 0)
467132951Sdavidxu			return (P2T(ret));
468132951Sdavidxu		/* read kcb.kcb_kmbx.km_curthread */
469132951Sdavidxu		ptr = ptr + offsetof(struct kcb, kcb_kmbx.km_curthread);
470132951Sdavidxu		ret = ps_pread(ta->ph, ptr, &ptr, sizeof(ptr));
471132951Sdavidxu		if (ret != 0)
472132951Sdavidxu			return (P2T(ret));
473132951Sdavidxu		if (ptr != 0) { /* not in critical */
474132951Sdavidxu			if (suspend)
475132951Sdavidxu				ret = ps_lstop(ta->ph, lwp);
476132951Sdavidxu			else
477132951Sdavidxu				ret = ps_lcontinue(ta->ph, lwp);
478132951Sdavidxu			if (ret != 0)
479132951Sdavidxu				return (P2T(ret));
480132951Sdavidxu		}
481132951Sdavidxu		/* FALLTHROUGH */
482132951Sdavidxu	}
483132951Sdavidxu	/* read tm_dflags */
484132951Sdavidxu	ret = ps_pread(ta->ph,
485132951Sdavidxu		tmbx_addr + offsetof(struct kse_thr_mailbox, tm_dflags),
486132951Sdavidxu		&dflags, sizeof(dflags));
487132951Sdavidxu	if (ret != 0)
488132951Sdavidxu		return (P2T(ret));
489132951Sdavidxu	if (suspend)
490133047Sdavidxu		dflags |= TMDF_SUSPEND;
491132951Sdavidxu	else
492133047Sdavidxu		dflags &= ~TMDF_SUSPEND;
493132951Sdavidxu	ret = ps_pwrite(ta->ph,
494132951Sdavidxu	       tmbx_addr + offsetof(struct kse_thr_mailbox, tm_dflags),
495132951Sdavidxu	       &dflags, sizeof(dflags));
496132951Sdavidxu	return (P2T(ret));
497132951Sdavidxu}
498132951Sdavidxu
499132951Sdavidxustatic td_err_e
500132332Smarcelpt_thr_dbresume(const td_thrhandle_t *th)
501132332Smarcel{
502132332Smarcel	TDBG_FUNC();
503132951Sdavidxu
504132951Sdavidxu	return pt_dbsuspend(th, 0);
505132332Smarcel}
506132332Smarcel
507132332Smarcelstatic td_err_e
508132332Smarcelpt_thr_dbsuspend(const td_thrhandle_t *th)
509132332Smarcel{
510132332Smarcel	TDBG_FUNC();
511132951Sdavidxu
512132951Sdavidxu	return pt_dbsuspend(th, 1);
513132332Smarcel}
514132332Smarcel
515132332Smarcelstatic td_err_e
516132332Smarcelpt_thr_validate(const td_thrhandle_t *th)
517132332Smarcel{
518132332Smarcel	td_thrhandle_t temp;
519132332Smarcel	int ret;
520132332Smarcel
521132332Smarcel	TDBG_FUNC();
522132332Smarcel
523132332Smarcel	ret = pt_ta_map_id2thr(th->th_ta, th->th_tid,
524132332Smarcel	                       &temp);
525132951Sdavidxu	return (ret);
526132332Smarcel}
527132332Smarcel
528132332Smarcelstatic td_err_e
529132332Smarcelpt_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
530132332Smarcel{
531132332Smarcel	const td_thragent_t *ta = th->th_ta;
532132332Smarcel	struct pthread pt;
533132332Smarcel	int ret;
534132951Sdavidxu	uint32_t dflags;
535132332Smarcel
536132332Smarcel	TDBG_FUNC();
537132332Smarcel
538132332Smarcel	ret = pt_validate(th);
539132332Smarcel	if (ret)
540132332Smarcel		return (ret);
541132332Smarcel
542132332Smarcel	memset(info, 0, sizeof(*info));
543132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
544132332Smarcel		info->ti_type = TD_THR_SYSTEM;
545132332Smarcel		info->ti_lid = ta->map[th->th_tid].lwp;
546132332Smarcel		info->ti_tid = th->th_tid;
547132332Smarcel		info->ti_state = TD_THR_RUN;
548132332Smarcel		info->ti_type = TD_THR_SYSTEM;
549132332Smarcel		return (TD_OK);
550132332Smarcel	}
551132332Smarcel
552132332Smarcel	ret = ps_pread(ta->ph, (psaddr_t)(ta->map[th->th_tid].thr),
553132332Smarcel	                &pt, sizeof(pt));
554132332Smarcel	if (ret != 0)
555132332Smarcel		return (P2T(ret));
556132332Smarcel	if (pt.magic != THR_MAGIC)
557132332Smarcel		return (TD_BADTH);
558132332Smarcel	ret = ps_pread(ta->ph,
559132332Smarcel	        ((psaddr_t)pt.tcb) + offsetof(struct tcb, tcb_tmbx.tm_lwp),
560132332Smarcel	        &info->ti_lid, sizeof(lwpid_t));
561132332Smarcel	if (ret != 0)
562132332Smarcel		return (P2T(ret));
563132951Sdavidxu	ret = ps_pread(ta->ph,
564132951Sdavidxu		((psaddr_t)pt.tcb) + offsetof(struct tcb, tcb_tmbx.tm_dflags),
565132951Sdavidxu		&dflags, sizeof(dflags));
566132951Sdavidxu	if (ret != 0)
567132951Sdavidxu		return (P2T(ret));
568132332Smarcel	info->ti_ta_p = th->th_ta;
569132332Smarcel	info->ti_tid = th->th_tid;
570132332Smarcel	info->ti_tls = (char *)pt.specific;
571132332Smarcel	info->ti_startfunc = (psaddr_t)pt.start_routine;
572132332Smarcel	info->ti_stkbase = (psaddr_t) pt.attr.stackaddr_attr;
573132332Smarcel	info->ti_stksize = pt.attr.stacksize_attr;
574132332Smarcel	switch (pt.state) {
575132332Smarcel	case PS_RUNNING:
576132332Smarcel		info->ti_state = TD_THR_RUN;
577132332Smarcel		break;
578132332Smarcel	case PS_LOCKWAIT:
579132332Smarcel	case PS_MUTEX_WAIT:
580132332Smarcel	case PS_COND_WAIT:
581132332Smarcel	case PS_SIGSUSPEND:
582132332Smarcel	case PS_SIGWAIT:
583132332Smarcel	case PS_JOIN:
584132332Smarcel	case PS_SUSPENDED:
585132332Smarcel	case PS_DEADLOCK:
586132332Smarcel	case PS_SLEEP_WAIT:
587132332Smarcel		info->ti_state = TD_THR_SLEEP;
588132332Smarcel		break;
589132332Smarcel	case PS_DEAD:
590132332Smarcel		info->ti_state = TD_THR_ZOMBIE;
591132332Smarcel		break;
592132332Smarcel	default:
593132332Smarcel		info->ti_state = TD_THR_UNKNOWN;
594132332Smarcel		break;
595132332Smarcel	}
596132332Smarcel
597133047Sdavidxu	info->ti_db_suspended = ((dflags & TMDF_SUSPEND) != 0);
598132332Smarcel	info->ti_type = TD_THR_USER;
599132332Smarcel	info->ti_pri = pt.active_priority;
600132332Smarcel	info->ti_sigmask = pt.sigmask;
601132332Smarcel	info->ti_traceme = 0;
602132332Smarcel	info->ti_pending = pt.sigpend;
603132332Smarcel	info->ti_events = 0;
604132332Smarcel	return (0);
605132332Smarcel}
606132332Smarcel
607132332Smarcelstatic td_err_e
608132332Smarcelpt_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregs)
609132332Smarcel{
610132332Smarcel	const td_thragent_t *ta = th->th_ta;
611132332Smarcel	struct kse_thr_mailbox tmbx;
612132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
613132332Smarcel	lwpid_t lwp;
614132332Smarcel	int ret;
615132332Smarcel
616132332Smarcel	TDBG_FUNC();
617132332Smarcel
618132332Smarcel	ret = pt_validate(th);
619132332Smarcel	if (ret)
620132332Smarcel		return (ret);
621132332Smarcel
622132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
623132332Smarcel		ret = ps_lgetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs);
624132332Smarcel		return (P2T(ret));
625132332Smarcel	}
626132332Smarcel
627132332Smarcel	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
628132951Sdavidxu	               offsetof(struct pthread, tcb),
629132951Sdavidxu	               &tcb_addr, sizeof(tcb_addr));
630132332Smarcel	if (ret != 0)
631132332Smarcel		return (P2T(ret));
632132332Smarcel	tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
633132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
634132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
635132332Smarcel	if (ret != 0)
636132332Smarcel		return (P2T(ret));
637132332Smarcel	if (lwp != 0) {
638132332Smarcel		ret = ps_lgetfpregs(ta->ph, lwp, fpregs);
639132332Smarcel		return (P2T(ret));
640132332Smarcel	}
641132332Smarcel
642132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
643132332Smarcel	if (ret != 0)
644132332Smarcel		return (P2T(ret));
645132332Smarcel	pt_ucontext_to_fpreg(&tmbx.tm_context, fpregs);
646132332Smarcel	return (0);
647132332Smarcel}
648132332Smarcel
649132332Smarcelstatic td_err_e
650132332Smarcelpt_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
651132332Smarcel{
652132332Smarcel	const td_thragent_t *ta = th->th_ta;
653132332Smarcel	struct kse_thr_mailbox tmbx;
654132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
655132332Smarcel	lwpid_t lwp;
656132332Smarcel	int ret;
657132332Smarcel
658132332Smarcel	TDBG_FUNC();
659132332Smarcel
660132332Smarcel	ret = pt_validate(th);
661132332Smarcel	if (ret)
662132332Smarcel		return (ret);
663132332Smarcel
664132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
665132332Smarcel		ret = ps_lgetregs(ta->ph,
666132332Smarcel		                  ta->map[th->th_tid].lwp, gregs);
667132332Smarcel		return (P2T(ret));
668132332Smarcel	}
669132332Smarcel
670132332Smarcel	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
671132332Smarcel	                offsetof(struct pthread, tcb),
672132332Smarcel			&tcb_addr, sizeof(tcb_addr));
673132332Smarcel	if (ret != 0)
674132332Smarcel		return (P2T(ret));
675132332Smarcel	tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
676132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
677132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
678132332Smarcel	if (ret != 0)
679132332Smarcel		return (P2T(ret));
680132332Smarcel	if (lwp != 0) {
681132332Smarcel		ret = ps_lgetregs(ta->ph, lwp, gregs);
682132332Smarcel		return (P2T(ret));
683132332Smarcel	}
684132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
685132332Smarcel	if (ret != 0)
686132332Smarcel		return (P2T(ret));
687132332Smarcel	pt_ucontext_to_reg(&tmbx.tm_context, gregs);
688132332Smarcel	return (0);
689132332Smarcel}
690132332Smarcel
691132332Smarcelstatic td_err_e
692132332Smarcelpt_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
693132332Smarcel{
694132332Smarcel	const td_thragent_t *ta = th->th_ta;
695132332Smarcel	struct kse_thr_mailbox tmbx;
696132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
697132332Smarcel	lwpid_t lwp;
698132332Smarcel	int ret;
699132332Smarcel
700132332Smarcel	TDBG_FUNC();
701132332Smarcel
702132332Smarcel	ret = pt_validate(th);
703132332Smarcel	if (ret)
704132332Smarcel		return (ret);
705132332Smarcel
706132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
707132332Smarcel		ret = ps_lsetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs);
708132332Smarcel		return (P2T(ret));
709132332Smarcel	}
710132332Smarcel
711132332Smarcel	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
712132332Smarcel	                offsetof(struct pthread, tcb),
713132332Smarcel                        &tcb_addr, sizeof(tcb_addr));
714132332Smarcel	if (ret != 0)
715132332Smarcel		return (P2T(ret));
716132332Smarcel	tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
717132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
718132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
719132332Smarcel	if (ret != 0)
720132332Smarcel		return (P2T(ret));
721132332Smarcel	if (lwp != 0) {
722132332Smarcel		ret = ps_lsetfpregs(ta->ph, lwp, fpregs);
723132332Smarcel		return (P2T(ret));
724132332Smarcel	}
725132332Smarcel	/*
726132332Smarcel	 * Read a copy of context, this makes sure that registers
727132332Smarcel	 * not covered by structure reg won't be clobbered
728132332Smarcel	 */
729132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
730132332Smarcel	if (ret != 0)
731132332Smarcel		return (P2T(ret));
732132332Smarcel
733132332Smarcel	pt_fpreg_to_ucontext(fpregs, &tmbx.tm_context);
734132332Smarcel	ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
735132332Smarcel	return (P2T(ret));
736132332Smarcel}
737132332Smarcel
738132332Smarcelstatic td_err_e
739132332Smarcelpt_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs)
740132332Smarcel{
741132332Smarcel	const td_thragent_t *ta = th->th_ta;
742132332Smarcel	struct kse_thr_mailbox tmbx;
743132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
744132332Smarcel	lwpid_t lwp;
745132332Smarcel	int ret;
746132332Smarcel
747132332Smarcel	TDBG_FUNC();
748132332Smarcel
749132332Smarcel	ret = pt_validate(th);
750132332Smarcel	if (ret)
751132332Smarcel		return (ret);
752132332Smarcel
753132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
754132332Smarcel		ret = ps_lsetregs(ta->ph, ta->map[th->th_tid].lwp, gregs);
755132332Smarcel		return (P2T(ret));
756132332Smarcel	}
757132332Smarcel
758132332Smarcel	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
759132332Smarcel	                offsetof(struct pthread, tcb),
760132332Smarcel	                &tcb_addr, sizeof(tcb_addr));
761132332Smarcel	if (ret != 0)
762132332Smarcel		return (P2T(ret));
763132332Smarcel	tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
764132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
765132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
766132332Smarcel	if (ret != 0)
767132332Smarcel		return (P2T(ret));
768132332Smarcel	if (lwp != 0) {
769132332Smarcel		ret = ps_lsetregs(ta->ph, lwp, gregs);
770132332Smarcel		return (P2T(ret));
771132332Smarcel	}
772132332Smarcel
773132332Smarcel	/*
774132332Smarcel	 * Read a copy of context, make sure that registers
775132332Smarcel	 * not covered by structure reg won't be clobbered
776132332Smarcel	 */
777132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
778132332Smarcel	if (ret != 0)
779132332Smarcel		return (P2T(ret));
780132332Smarcel	pt_reg_to_ucontext(gregs, &tmbx.tm_context);
781132332Smarcel	ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
782132332Smarcel	return (P2T(ret));
783132332Smarcel}
784132332Smarcel
785132332Smarcelstatic td_err_e
786132332Smarcelpt_thr_event_enable(const td_thrhandle_t *th, int en)
787132332Smarcel{
788132332Smarcel	TDBG_FUNC();
789132332Smarcel	return (TD_ERR);
790132332Smarcel}
791132332Smarcel
792132332Smarcelstatic td_err_e
793132332Smarcelpt_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *setp)
794132332Smarcel{
795132332Smarcel	TDBG_FUNC();
796132332Smarcel	return (TD_ERR);
797132332Smarcel}
798132332Smarcel
799132332Smarcelstatic td_err_e
800132332Smarcelpt_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *setp)
801132332Smarcel{
802132332Smarcel	TDBG_FUNC();
803132332Smarcel	return (TD_ERR);
804132332Smarcel}
805132332Smarcel
806132332Smarcelstatic td_err_e
807132332Smarcelpt_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg)
808132332Smarcel{
809132332Smarcel	TDBG_FUNC();
810132332Smarcel	return (TD_NOMSG);
811132332Smarcel}
812132332Smarcel
813132332Smarcelstatic td_err_e
814132332Smarcelpt_thr_sstep(const td_thrhandle_t *th, int step)
815132332Smarcel{
816132332Smarcel	const td_thragent_t *ta = th->th_ta;
817132332Smarcel	struct kse_thr_mailbox tmbx;
818132332Smarcel	struct reg regs;
819132332Smarcel	psaddr_t tcb_addr, tmbx_addr;
820132951Sdavidxu	uint32_t dflags;
821132332Smarcel	lwpid_t lwp;
822132332Smarcel	int ret;
823132332Smarcel
824132332Smarcel	TDBG_FUNC();
825132332Smarcel
826132332Smarcel	ret = pt_validate(th);
827132332Smarcel	if (ret)
828132332Smarcel		return (ret);
829132332Smarcel
830132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP)
831132332Smarcel		return (TD_BADTH);
832132332Smarcel
833132332Smarcel	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
834132332Smarcel	                offsetof(struct pthread, tcb),
835132332Smarcel	                &tcb_addr, sizeof(tcb_addr));
836132332Smarcel	if (ret != 0)
837132332Smarcel		return (P2T(ret));
838132332Smarcel
839132332Smarcel	/* Clear or set single step flag in thread mailbox */
840132951Sdavidxu	ret = ps_pread(ta->ph, tcb_addr + offsetof(struct tcb,
841132951Sdavidxu			 tcb_tmbx.tm_dflags), &dflags, sizeof(uint32_t));
842132951Sdavidxu	if (ret != 0)
843132951Sdavidxu		return (P2T(ret));
844132951Sdavidxu	if (step != 0)
845132951Sdavidxu		dflags |= TMDF_SSTEP;
846132951Sdavidxu	else
847132951Sdavidxu		dflags &= ~TMDF_SSTEP;
848132332Smarcel	ret = ps_pwrite(ta->ph, tcb_addr + offsetof(struct tcb,
849132951Sdavidxu	                 tcb_tmbx.tm_dflags), &dflags, sizeof(uint32_t));
850132332Smarcel	if (ret != 0)
851132332Smarcel		return (P2T(ret));
852132332Smarcel	/* Get lwp */
853132332Smarcel	ret = ps_pread(ta->ph, tcb_addr + offsetof(struct tcb,
854132332Smarcel	                tcb_tmbx.tm_lwp), &lwp, sizeof(lwpid_t));
855132332Smarcel	if (ret != 0)
856132332Smarcel		return (P2T(ret));
857132332Smarcel	if (lwp != 0)
858132951Sdavidxu		return (0);
859132332Smarcel
860132332Smarcel	tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
861132332Smarcel	/*
862132332Smarcel	 * context is in userland, some architectures store
863132332Smarcel	 * single step status in registers, we should change
864132332Smarcel	 * these registers.
865132332Smarcel	 */
866132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
867132332Smarcel	if (ret == 0) {
868132332Smarcel		pt_ucontext_to_reg(&tmbx.tm_context, &regs);
869132332Smarcel		/* only write out if it is really changed. */
870132332Smarcel		if (pt_reg_sstep(&regs, step) != 0) {
871132332Smarcel			pt_reg_to_ucontext(&regs, &tmbx.tm_context);
872132332Smarcel			ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx,
873132332Smarcel			                 sizeof(tmbx));
874132332Smarcel		}
875132332Smarcel	}
876132332Smarcel	return (P2T(ret));
877132332Smarcel}
878132332Smarcel
879132332Smarcelstatic void
880132332Smarcelpt_unmap_lwp(const td_thragent_t *ta, lwpid_t lwp)
881132332Smarcel{
882132332Smarcel	int i;
883132332Smarcel
884132332Smarcel	for (i = 0; i < ta->map_len; ++i) {
885132332Smarcel		if (ta->map[i].type == PT_LWP && ta->map[i].lwp == lwp) {
886132332Smarcel			ta->map[i].type = PT_NONE;
887132332Smarcel			return;
888132332Smarcel		}
889132332Smarcel	}
890132332Smarcel}
891132332Smarcel
892132332Smarcelstatic int
893132332Smarcelpt_validate(const td_thrhandle_t *th)
894132332Smarcel{
895132332Smarcel
896132332Smarcel	if (th->th_tid < 0 || th->th_tid >= th->th_ta->map_len ||
897132332Smarcel	    th->th_ta->map[th->th_tid].type == PT_NONE)
898132332Smarcel		return (TD_NOTHR);
899132332Smarcel	return (TD_OK);
900132332Smarcel}
901132332Smarcel
902132332Smarcelstruct ta_ops libpthread_db_ops = {
903132332Smarcel	.to_init		= pt_init,
904132332Smarcel	.to_ta_clear_event	= pt_ta_clear_event,
905132332Smarcel	.to_ta_delete		= pt_ta_delete,
906132332Smarcel	.to_ta_event_addr	= pt_ta_event_addr,
907132332Smarcel	.to_ta_event_getmsg	= pt_ta_event_getmsg,
908132332Smarcel	.to_ta_map_id2thr	= pt_ta_map_id2thr,
909132332Smarcel	.to_ta_map_lwp2thr	= pt_ta_map_lwp2thr,
910132332Smarcel	.to_ta_new		= pt_ta_new,
911132332Smarcel	.to_ta_set_event	= pt_ta_set_event,
912132332Smarcel	.to_ta_thr_iter		= pt_ta_thr_iter,
913132332Smarcel	.to_ta_tsd_iter		= pt_ta_tsd_iter,
914132332Smarcel	.to_thr_clear_event	= pt_thr_clear_event,
915132332Smarcel	.to_thr_dbresume	= pt_thr_dbresume,
916132332Smarcel	.to_thr_dbsuspend	= pt_thr_dbsuspend,
917132332Smarcel	.to_thr_event_enable	= pt_thr_event_enable,
918132332Smarcel	.to_thr_event_getmsg	= pt_thr_event_getmsg,
919132332Smarcel	.to_thr_get_info	= pt_thr_get_info,
920132332Smarcel	.to_thr_getfpregs	= pt_thr_getfpregs,
921132332Smarcel	.to_thr_getgregs	= pt_thr_getgregs,
922132332Smarcel	.to_thr_set_event	= pt_thr_set_event,
923132332Smarcel	.to_thr_setfpregs	= pt_thr_setfpregs,
924132332Smarcel	.to_thr_setgregs	= pt_thr_setgregs,
925132332Smarcel	.to_thr_validate	= pt_thr_validate,
926132332Smarcel
927132332Smarcel	/* FreeBSD specific extensions. */
928132332Smarcel	.to_thr_sstep		= pt_thr_sstep,
929132332Smarcel};
930