libthr_db.c revision 155414
1132332Smarcel/*
2132332Smarcel * Copyright (c) 2004 Marcel Moolenaar
3144519Sdavidxu * Copyright (c) 2005 David Xu
4132332Smarcel * All rights reserved.
5132332Smarcel *
6132332Smarcel * Redistribution and use in source and binary forms, with or without
7132332Smarcel * modification, are permitted provided that the following conditions
8132332Smarcel * are met:
9132332Smarcel *
10132332Smarcel * 1. Redistributions of source code must retain the above copyright
11132332Smarcel *    notice, this list of conditions and the following disclaimer.
12132332Smarcel * 2. Redistributions in binary form must reproduce the above copyright
13132332Smarcel *    notice, this list of conditions and the following disclaimer in the
14132332Smarcel *    documentation and/or other materials provided with the distribution.
15132332Smarcel *
16132332Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17132332Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18132332Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19132332Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20132332Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21132332Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22132332Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23132332Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24132332Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25132332Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26132332Smarcel */
27132332Smarcel
28132332Smarcel#include <sys/cdefs.h>
29132332Smarcel__FBSDID("$FreeBSD: head/lib/libthread_db/libthr_db.c 155414 2006-02-07 02:55:34Z davidxu $");
30132332Smarcel
31132332Smarcel#include <proc_service.h>
32144519Sdavidxu#include <stddef.h>
33132332Smarcel#include <stdlib.h>
34144519Sdavidxu#include <string.h>
35144519Sdavidxu#include <sys/types.h>
36144519Sdavidxu#include <sys/ptrace.h>
37132332Smarcel#include <thread_db.h>
38144519Sdavidxu#include <unistd.h>
39132332Smarcel
40132332Smarcel#include "thread_db_int.h"
41132332Smarcel
42144922Sdavidxu#define	TERMINATED	1
43144922Sdavidxu
44132332Smarcelstruct td_thragent {
45132332Smarcel	TD_THRAGENT_FIELDS;
46144519Sdavidxu	psaddr_t	libthr_debug_addr;
47144519Sdavidxu	psaddr_t	thread_list_addr;
48144519Sdavidxu	psaddr_t	thread_active_threads_addr;
49144519Sdavidxu	psaddr_t	thread_keytable_addr;
50144922Sdavidxu	psaddr_t	thread_last_event_addr;
51144922Sdavidxu	psaddr_t	thread_event_mask_addr;
52144922Sdavidxu	psaddr_t	thread_bp_create_addr;
53144922Sdavidxu	psaddr_t	thread_bp_death_addr;
54144519Sdavidxu	int		thread_off_dtv;
55144519Sdavidxu	int		thread_off_tlsindex;
56144519Sdavidxu	int		thread_off_attr_flags;
57144519Sdavidxu	int		thread_size_key;
58144519Sdavidxu	int		thread_off_tcb;
59144519Sdavidxu	int		thread_off_linkmap;
60144519Sdavidxu	int		thread_off_next;
61144519Sdavidxu	int		thread_off_state;
62144519Sdavidxu	int		thread_off_tid;
63144519Sdavidxu	int		thread_max_keys;
64144519Sdavidxu	int		thread_off_key_allocated;
65144519Sdavidxu	int		thread_off_key_destructor;
66144922Sdavidxu	int		thread_off_report_events;
67144922Sdavidxu	int		thread_off_event_mask;
68144922Sdavidxu	int		thread_off_event_buf;
69144519Sdavidxu	int		thread_state_zoombie;
70144519Sdavidxu	int		thread_state_running;
71132332Smarcel};
72132332Smarcel
73144519Sdavidxu#define P2T(c) ps2td(c)
74144519Sdavidxu
75144519Sdavidxustatic int pt_validate(const td_thrhandle_t *th);
76144519Sdavidxu
77144519Sdavidxustatic int
78144519Sdavidxups2td(int c)
79132332Smarcel{
80144519Sdavidxu	switch (c) {
81144519Sdavidxu	case PS_OK:
82144519Sdavidxu		return TD_OK;
83144519Sdavidxu	case PS_ERR:
84144519Sdavidxu		return TD_ERR;
85144519Sdavidxu	case PS_BADPID:
86144519Sdavidxu		return TD_BADPH;
87144519Sdavidxu	case PS_BADLID:
88144519Sdavidxu		return TD_NOLWP;
89144519Sdavidxu	case PS_BADADDR:
90144519Sdavidxu		return TD_ERR;
91144519Sdavidxu	case PS_NOSYM:
92144519Sdavidxu		return TD_NOLIBTHREAD;
93144519Sdavidxu	case PS_NOFREGS:
94144519Sdavidxu		return TD_NOFPREGS;
95144519Sdavidxu	default:
96144519Sdavidxu		return TD_ERR;
97144519Sdavidxu	}
98132332Smarcel}
99132332Smarcel
100132332Smarcelstatic td_err_e
101144519Sdavidxupt_init(void)
102132332Smarcel{
103144519Sdavidxu	return (0);
104132332Smarcel}
105132332Smarcel
106132332Smarcelstatic td_err_e
107144519Sdavidxupt_ta_new(struct ps_prochandle *ph, td_thragent_t **pta)
108132332Smarcel{
109144519Sdavidxu#define LOOKUP_SYM(proc, sym, addr) 			\
110144519Sdavidxu	ret = ps_pglobal_lookup(proc, NULL, sym, addr);	\
111144519Sdavidxu	if (ret != 0) {					\
112144519Sdavidxu		TDBG("can not find symbol: %s\n", sym);	\
113144519Sdavidxu		ret = TD_NOLIBTHREAD;			\
114144519Sdavidxu		goto error;				\
115144519Sdavidxu	}
116144519Sdavidxu
117144519Sdavidxu#define	LOOKUP_VAL(proc, sym, val)			\
118144519Sdavidxu	ret = ps_pglobal_lookup(proc, NULL, sym, &vaddr);\
119144519Sdavidxu	if (ret != 0) {					\
120144519Sdavidxu		TDBG("can not find symbol: %s\n", sym);	\
121144519Sdavidxu		ret = TD_NOLIBTHREAD;			\
122144519Sdavidxu		goto error;				\
123144519Sdavidxu	}						\
124144519Sdavidxu	ret = ps_pread(proc, vaddr, val, sizeof(int));	\
125144519Sdavidxu	if (ret != 0) {					\
126144519Sdavidxu		TDBG("can not read value of %s\n", sym);\
127144519Sdavidxu		ret = TD_NOLIBTHREAD;			\
128144519Sdavidxu		goto error;				\
129144519Sdavidxu	}
130144519Sdavidxu
131144519Sdavidxu	td_thragent_t *ta;
132144519Sdavidxu	psaddr_t vaddr;
133144519Sdavidxu	int dbg;
134144519Sdavidxu	int ret;
135144519Sdavidxu
136144519Sdavidxu	TDBG_FUNC();
137144519Sdavidxu
138144519Sdavidxu	ta = malloc(sizeof(td_thragent_t));
139144519Sdavidxu	if (ta == NULL)
140144519Sdavidxu		return (TD_MALLOC);
141144519Sdavidxu
142144519Sdavidxu	ta->ph = ph;
143144519Sdavidxu
144144519Sdavidxu	LOOKUP_SYM(ph, "_libthr_debug",		&ta->libthr_debug_addr);
145144519Sdavidxu	LOOKUP_SYM(ph, "_thread_list",		&ta->thread_list_addr);
146144519Sdavidxu	LOOKUP_SYM(ph, "_thread_active_threads",&ta->thread_active_threads_addr);
147144519Sdavidxu	LOOKUP_SYM(ph, "_thread_keytable",	&ta->thread_keytable_addr);
148144922Sdavidxu	LOOKUP_SYM(ph, "_thread_last_event",	&ta->thread_last_event_addr);
149144922Sdavidxu	LOOKUP_SYM(ph, "_thread_event_mask",	&ta->thread_event_mask_addr);
150144922Sdavidxu	LOOKUP_SYM(ph, "_thread_bp_create",	&ta->thread_bp_create_addr);
151144922Sdavidxu	LOOKUP_SYM(ph, "_thread_bp_death",	&ta->thread_bp_death_addr);
152144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_dtv",	&ta->thread_off_dtv);
153144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_tlsindex",	&ta->thread_off_tlsindex);
154144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_attr_flags",	&ta->thread_off_attr_flags);
155144519Sdavidxu	LOOKUP_VAL(ph, "_thread_size_key",	&ta->thread_size_key);
156144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_tcb",	&ta->thread_off_tcb);
157144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_tid",	&ta->thread_off_tid);
158144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_linkmap",	&ta->thread_off_linkmap);
159144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_next",	&ta->thread_off_next);
160144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_state",	&ta->thread_off_state);
161144519Sdavidxu	LOOKUP_VAL(ph, "_thread_max_keys",	&ta->thread_max_keys);
162144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_key_allocated", &ta->thread_off_key_allocated);
163144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_key_destructor", &ta->thread_off_key_destructor);
164144519Sdavidxu	LOOKUP_VAL(ph, "_thread_state_running", &ta->thread_state_running);
165144519Sdavidxu	LOOKUP_VAL(ph, "_thread_state_zoombie", &ta->thread_state_zoombie);
166144922Sdavidxu	LOOKUP_VAL(ph, "_thread_off_report_events", &ta->thread_off_report_events);
167144922Sdavidxu	LOOKUP_VAL(ph, "_thread_off_event_mask", &ta->thread_off_event_mask);
168144922Sdavidxu	LOOKUP_VAL(ph, "_thread_off_event_buf", &ta->thread_off_event_buf);
169144519Sdavidxu	dbg = getpid();
170144519Sdavidxu	/*
171144519Sdavidxu	 * If this fails it probably means we're debugging a core file and
172144519Sdavidxu	 * can't write to it.
173144519Sdavidxu	 */
174144519Sdavidxu	ps_pwrite(ph, ta->libthr_debug_addr, &dbg, sizeof(int));
175144519Sdavidxu	*pta = ta;
176144519Sdavidxu	return (0);
177144519Sdavidxu
178144519Sdavidxuerror:
179132332Smarcel	free(ta);
180144519Sdavidxu	return (ret);
181132332Smarcel}
182132332Smarcel
183132332Smarcelstatic td_err_e
184144519Sdavidxupt_ta_delete(td_thragent_t *ta)
185132332Smarcel{
186144519Sdavidxu	int dbg;
187144519Sdavidxu
188144519Sdavidxu	TDBG_FUNC();
189144519Sdavidxu
190144519Sdavidxu	dbg = 0;
191144519Sdavidxu	/*
192144519Sdavidxu	 * Error returns from this write are not really a problem;
193144519Sdavidxu	 * the process doesn't exist any more.
194144519Sdavidxu	 */
195144519Sdavidxu	ps_pwrite(ta->ph, ta->libthr_debug_addr, &dbg, sizeof(int));
196144519Sdavidxu	free(ta);
197144519Sdavidxu	return (TD_OK);
198132332Smarcel}
199132332Smarcel
200132332Smarcelstatic td_err_e
201144519Sdavidxupt_ta_map_id2thr(const td_thragent_t *ta, thread_t id, td_thrhandle_t *th)
202132332Smarcel{
203144519Sdavidxu	TAILQ_HEAD(, pthread) thread_list;
204144519Sdavidxu	psaddr_t pt;
205144989Sdavidxu	long lwp;
206144922Sdavidxu	int ret;
207144519Sdavidxu
208144519Sdavidxu	TDBG_FUNC();
209144519Sdavidxu
210144989Sdavidxu	if (id == 0)
211144519Sdavidxu		return (TD_NOTHR);
212144519Sdavidxu	ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
213144989Sdavidxu		sizeof(thread_list));
214144519Sdavidxu	if (ret != 0)
215144519Sdavidxu		return (P2T(ret));
216144989Sdavidxu	/* Iterate through thread list to find pthread */
217144519Sdavidxu	pt = (psaddr_t)thread_list.tqh_first;
218144989Sdavidxu	while (pt != NULL) {
219144519Sdavidxu		ret = ps_pread(ta->ph, pt + ta->thread_off_tid,
220144989Sdavidxu			       &lwp, sizeof(lwp));
221144519Sdavidxu		if (ret != 0)
222144519Sdavidxu			return (P2T(ret));
223144989Sdavidxu		if (lwp == id)
224144519Sdavidxu			break;
225144519Sdavidxu		/* get next thread */
226144519Sdavidxu		ret = ps_pread(ta->ph,
227144989Sdavidxu				pt + ta->thread_off_next,
228144989Sdavidxu				&pt, sizeof(pt));
229144519Sdavidxu		if (ret != 0)
230144519Sdavidxu			return (P2T(ret));
231133631Sdavidxu	}
232144989Sdavidxu	if (pt == NULL)
233144519Sdavidxu		return (TD_NOTHR);
234144989Sdavidxu	th->th_ta = ta;
235144989Sdavidxu	th->th_tid = id;
236144663Sdavidxu	th->th_thread = pt;
237144519Sdavidxu	return (TD_OK);
238132332Smarcel}
239132332Smarcel
240132332Smarcelstatic td_err_e
241144989Sdavidxupt_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwp, td_thrhandle_t *th)
242144989Sdavidxu{
243144989Sdavidxu	return (pt_ta_map_id2thr(ta, lwp, th));
244144989Sdavidxu}
245144989Sdavidxu
246144989Sdavidxustatic td_err_e
247144519Sdavidxupt_ta_thr_iter(const td_thragent_t *ta,
248144519Sdavidxu               td_thr_iter_f *callback, void *cbdata_p,
249144519Sdavidxu               td_thr_state_e state, int ti_pri,
250144519Sdavidxu               sigset_t *ti_sigmask_p,
251144519Sdavidxu               unsigned int ti_user_flags)
252132332Smarcel{
253144519Sdavidxu	TAILQ_HEAD(, pthread) thread_list;
254144519Sdavidxu	td_thrhandle_t th;
255144519Sdavidxu	psaddr_t pt;
256144989Sdavidxu	long lwp;
257144989Sdavidxu	int ret;
258132332Smarcel
259144519Sdavidxu	TDBG_FUNC();
260132332Smarcel
261144519Sdavidxu	ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
262144519Sdavidxu		       sizeof(thread_list));
263144519Sdavidxu	if (ret != 0)
264144519Sdavidxu		return (P2T(ret));
265144519Sdavidxu	pt = (psaddr_t)thread_list.tqh_first;
266144519Sdavidxu	while (pt != 0) {
267144989Sdavidxu		ret = ps_pread(ta->ph, pt + ta->thread_off_tid, &lwp,
268144989Sdavidxu			      sizeof(lwp));
269144519Sdavidxu		if (ret != 0)
270144519Sdavidxu			return (P2T(ret));
271144989Sdavidxu		if (lwp != 0 && lwp != TERMINATED) {
272144989Sdavidxu			th.th_ta = ta;
273144989Sdavidxu			th.th_tid = (thread_t)lwp;
274144922Sdavidxu			th.th_thread = pt;
275144922Sdavidxu			if ((*callback)(&th, cbdata_p))
276144922Sdavidxu				return (TD_DBERR);
277133631Sdavidxu		}
278144519Sdavidxu		/* get next thread */
279144519Sdavidxu		ret = ps_pread(ta->ph, pt + ta->thread_off_next, &pt,
280144519Sdavidxu			       sizeof(pt));
281144519Sdavidxu		if (ret != 0)
282144519Sdavidxu			return (P2T(ret));
283132332Smarcel	}
284144519Sdavidxu	return (TD_OK);
285132332Smarcel}
286132332Smarcel
287132332Smarcelstatic td_err_e
288144519Sdavidxupt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg)
289132332Smarcel{
290144519Sdavidxu	char *keytable;
291144519Sdavidxu	void *destructor;
292144519Sdavidxu	int i, ret, allocated;
293132332Smarcel
294144519Sdavidxu	TDBG_FUNC();
295132332Smarcel
296144519Sdavidxu	keytable = malloc(ta->thread_max_keys * ta->thread_size_key);
297144519Sdavidxu	if (keytable == NULL)
298132332Smarcel		return (TD_MALLOC);
299144519Sdavidxu	ret = ps_pread(ta->ph, (psaddr_t)ta->thread_keytable_addr, keytable,
300144519Sdavidxu	               ta->thread_max_keys * ta->thread_size_key);
301144519Sdavidxu	if (ret != 0) {
302144519Sdavidxu		free(keytable);
303144519Sdavidxu		return (P2T(ret));
304144989Sdavidxu	}
305144519Sdavidxu	for (i = 0; i < ta->thread_max_keys; i++) {
306144519Sdavidxu		allocated = *(int *)(keytable + i * ta->thread_size_key +
307144519Sdavidxu			ta->thread_off_key_allocated);
308144519Sdavidxu		destructor = *(void **)(keytable + i * ta->thread_size_key +
309144519Sdavidxu			ta->thread_off_key_destructor);
310144519Sdavidxu		if (allocated) {
311144519Sdavidxu			ret = (ki)(i, destructor, arg);
312144519Sdavidxu			if (ret != 0) {
313144519Sdavidxu				free(keytable);
314144519Sdavidxu				return (TD_DBERR);
315144519Sdavidxu			}
316144519Sdavidxu		}
317144519Sdavidxu	}
318144519Sdavidxu	free(keytable);
319144519Sdavidxu	return (TD_OK);
320144519Sdavidxu}
321132332Smarcel
322144519Sdavidxustatic td_err_e
323144519Sdavidxupt_ta_event_addr(const td_thragent_t *ta, td_event_e event, td_notify_t *ptr)
324144519Sdavidxu{
325144989Sdavidxu
326144519Sdavidxu	TDBG_FUNC();
327144922Sdavidxu
328144922Sdavidxu	switch (event) {
329144922Sdavidxu	case TD_CREATE:
330144922Sdavidxu		ptr->type = NOTIFY_BPT;
331144922Sdavidxu		ptr->u.bptaddr = ta->thread_bp_create_addr;
332144922Sdavidxu		return (0);
333144922Sdavidxu	case TD_DEATH:
334144922Sdavidxu		ptr->type = NOTIFY_BPT;
335144922Sdavidxu		ptr->u.bptaddr = ta->thread_bp_death_addr;
336144922Sdavidxu		return (0);
337144922Sdavidxu	default:
338144922Sdavidxu		return (TD_ERR);
339144922Sdavidxu	}
340144519Sdavidxu}
341132332Smarcel
342144519Sdavidxustatic td_err_e
343144519Sdavidxupt_ta_set_event(const td_thragent_t *ta, td_thr_events_t *events)
344144519Sdavidxu{
345144989Sdavidxu	td_thr_events_t mask;
346144922Sdavidxu	int ret;
347144922Sdavidxu
348144519Sdavidxu	TDBG_FUNC();
349144922Sdavidxu	ret = ps_pread(ta->ph, ta->thread_event_mask_addr, &mask,
350144922Sdavidxu		sizeof(mask));
351144922Sdavidxu	if (ret != 0)
352144922Sdavidxu		return (P2T(ret));
353144922Sdavidxu	mask |= *events;
354144922Sdavidxu	ret = ps_pwrite(ta->ph, ta->thread_event_mask_addr, &mask,
355144922Sdavidxu		sizeof(mask));
356144922Sdavidxu	return (P2T(ret));
357132332Smarcel}
358132332Smarcel
359132332Smarcelstatic td_err_e
360144519Sdavidxupt_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *events)
361132332Smarcel{
362144989Sdavidxu	td_thr_events_t mask;
363144922Sdavidxu	int ret;
364144922Sdavidxu
365144519Sdavidxu	TDBG_FUNC();
366144922Sdavidxu	ret = ps_pread(ta->ph, ta->thread_event_mask_addr, &mask,
367144922Sdavidxu		sizeof(mask));
368144922Sdavidxu	if (ret != 0)
369144922Sdavidxu		return (P2T(ret));
370144922Sdavidxu	mask &= ~*events;
371144922Sdavidxu	ret = ps_pwrite(ta->ph, ta->thread_event_mask_addr, &mask,
372144922Sdavidxu		sizeof(mask));
373144922Sdavidxu	return (P2T(ret));
374132332Smarcel}
375132332Smarcel
376132332Smarcelstatic td_err_e
377144519Sdavidxupt_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg)
378132332Smarcel{
379144922Sdavidxu	static td_thrhandle_t handle;
380144922Sdavidxu
381144922Sdavidxu	psaddr_t pt, pt_temp;
382144989Sdavidxu	td_thr_events_e	tmp;
383144922Sdavidxu	long lwp;
384144922Sdavidxu	int ret;
385144922Sdavidxu
386144519Sdavidxu	TDBG_FUNC();
387144922Sdavidxu
388144922Sdavidxu	ret = ps_pread(ta->ph, ta->thread_last_event_addr, &pt, sizeof(pt));
389144922Sdavidxu	if (ret != 0)
390144922Sdavidxu		return (P2T(ret));
391144922Sdavidxu	if (pt == NULL)
392144922Sdavidxu		return (TD_NOMSG);
393144989Sdavidxu	/*
394144989Sdavidxu	 * Take the event pointer, at the time, libthr only reports event
395144989Sdavidxu	 * once a time, so it is not a link list.
396144989Sdavidxu	 */
397144922Sdavidxu	pt_temp = NULL;
398144922Sdavidxu	ps_pwrite(ta->ph, ta->thread_last_event_addr, &pt_temp, sizeof(pt_temp));
399144922Sdavidxu
400144989Sdavidxu	/* Read event info */
401144922Sdavidxu	ret = ps_pread(ta->ph, pt + ta->thread_off_event_buf, msg, sizeof(*msg));
402144922Sdavidxu	if (ret != 0)
403144922Sdavidxu		return (P2T(ret));
404144922Sdavidxu	if (msg->event == 0)
405144922Sdavidxu		return (TD_NOMSG);
406144989Sdavidxu	/* Clear event */
407144922Sdavidxu	tmp = 0;
408144922Sdavidxu	ps_pwrite(ta->ph, pt + ta->thread_off_event_buf, &tmp, sizeof(tmp));
409144989Sdavidxu	/* Convert event */
410144922Sdavidxu	pt = (psaddr_t)msg->th_p;
411144922Sdavidxu	ret = ps_pread(ta->ph, pt + ta->thread_off_tid, &lwp, sizeof(lwp));
412144922Sdavidxu	if (ret != 0)
413144922Sdavidxu		return (P2T(ret));
414144922Sdavidxu	handle.th_ta = ta;
415144989Sdavidxu	handle.th_tid = lwp;
416144922Sdavidxu	handle.th_thread = pt;
417144922Sdavidxu	msg->th_p = &handle;
418144922Sdavidxu	return (0);
419144519Sdavidxu}
420132332Smarcel
421144519Sdavidxustatic td_err_e
422144519Sdavidxupt_dbsuspend(const td_thrhandle_t *th, int suspend)
423144519Sdavidxu{
424144519Sdavidxu	td_thragent_t *ta = (td_thragent_t *)th->th_ta;
425144519Sdavidxu	int ret;
426132332Smarcel
427144519Sdavidxu	TDBG_FUNC();
428144519Sdavidxu
429144519Sdavidxu	ret = pt_validate(th);
430144519Sdavidxu	if (ret)
431144519Sdavidxu		return (ret);
432144519Sdavidxu
433144519Sdavidxu	if (suspend)
434144989Sdavidxu		ret = ps_lstop(ta->ph, th->th_tid);
435144519Sdavidxu	else
436144989Sdavidxu		ret = ps_lcontinue(ta->ph, th->th_tid);
437144519Sdavidxu	return (P2T(ret));
438132332Smarcel}
439132332Smarcel
440132332Smarcelstatic td_err_e
441144519Sdavidxupt_thr_dbresume(const td_thrhandle_t *th)
442132332Smarcel{
443144519Sdavidxu	TDBG_FUNC();
444144519Sdavidxu
445144519Sdavidxu	return pt_dbsuspend(th, 0);
446132332Smarcel}
447132332Smarcel
448132332Smarcelstatic td_err_e
449144519Sdavidxupt_thr_dbsuspend(const td_thrhandle_t *th)
450133631Sdavidxu{
451144519Sdavidxu	TDBG_FUNC();
452133631Sdavidxu
453144519Sdavidxu	return pt_dbsuspend(th, 1);
454133631Sdavidxu}
455133631Sdavidxu
456133631Sdavidxustatic td_err_e
457144519Sdavidxupt_thr_validate(const td_thrhandle_t *th)
458133631Sdavidxu{
459144519Sdavidxu	td_thrhandle_t temp;
460144519Sdavidxu	int ret;
461133631Sdavidxu
462144519Sdavidxu	TDBG_FUNC();
463144519Sdavidxu
464144989Sdavidxu	ret = pt_ta_map_id2thr(th->th_ta, th->th_tid, &temp);
465144519Sdavidxu	return (ret);
466133631Sdavidxu}
467133631Sdavidxu
468133631Sdavidxustatic td_err_e
469144519Sdavidxupt_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
470132332Smarcel{
471144519Sdavidxu	const td_thragent_t *ta = th->th_ta;
472155414Sdavidxu	struct ptrace_lwpinfo linfo;
473144519Sdavidxu	int state;
474144519Sdavidxu	int ret;
475144519Sdavidxu
476144519Sdavidxu	TDBG_FUNC();
477144519Sdavidxu
478155387Sdavidxu	bzero(info, sizeof(*info));
479144519Sdavidxu	ret = pt_validate(th);
480144519Sdavidxu	if (ret)
481144519Sdavidxu		return (ret);
482144989Sdavidxu	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_state,
483144519Sdavidxu	               &state, sizeof(state));
484144519Sdavidxu	if (ret != 0)
485144519Sdavidxu		return (P2T(ret));
486144989Sdavidxu	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_report_events,
487144974Sdavidxu		&info->ti_traceme, sizeof(int));
488144974Sdavidxu	if (ret != 0)
489144974Sdavidxu		return (P2T(ret));
490144989Sdavidxu	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
491144974Sdavidxu		&info->ti_events, sizeof(td_thr_events_t));
492144974Sdavidxu	if (ret != 0)
493144974Sdavidxu		return (P2T(ret));
494144989Sdavidxu	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_tcb,
495144989Sdavidxu		&info->ti_tls, sizeof(void *));
496144989Sdavidxu	info->ti_lid = th->th_tid;
497144519Sdavidxu	info->ti_tid = th->th_tid;
498144989Sdavidxu	info->ti_thread = th->th_thread;
499144519Sdavidxu	info->ti_ta_p = th->th_ta;
500155414Sdavidxu	ret = ps_linfo(ta->ph, th->th_tid, &linfo);
501155414Sdavidxu	if (ret == PS_OK) {
502155414Sdavidxu		info->ti_sigmask = linfo.pl_sigmask;
503155414Sdavidxu		info->ti_pending = linfo.pl_siglist;
504155414Sdavidxu	}
505144519Sdavidxu	if (state == ta->thread_state_running)
506144519Sdavidxu		info->ti_state = TD_THR_RUN;
507144519Sdavidxu	else if (state == ta->thread_state_zoombie)
508144519Sdavidxu		info->ti_state = TD_THR_ZOMBIE;
509144519Sdavidxu	else
510144519Sdavidxu		info->ti_state = TD_THR_SLEEP;
511144519Sdavidxu	info->ti_type = TD_THR_USER;
512144519Sdavidxu	return (0);
513132332Smarcel}
514132332Smarcel
515146818Sdfr#ifdef __i386__
516132332Smarcelstatic td_err_e
517146818Sdfrpt_thr_getxmmregs(const td_thrhandle_t *th, char *fxsave)
518146818Sdfr{
519146818Sdfr	const td_thragent_t *ta = th->th_ta;
520146818Sdfr	int ret;
521146818Sdfr
522146818Sdfr	TDBG_FUNC();
523146818Sdfr
524146818Sdfr	ret = pt_validate(th);
525146818Sdfr	if (ret)
526146818Sdfr		return (ret);
527146818Sdfr
528146818Sdfr	ret = ps_lgetxmmregs(ta->ph, th->th_tid, fxsave);
529146818Sdfr	return (P2T(ret));
530146818Sdfr}
531146818Sdfr#endif
532146818Sdfr
533146818Sdfrstatic td_err_e
534144519Sdavidxupt_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregs)
535132332Smarcel{
536144519Sdavidxu	const td_thragent_t *ta = th->th_ta;
537144519Sdavidxu	int ret;
538144519Sdavidxu
539144519Sdavidxu	TDBG_FUNC();
540144519Sdavidxu
541144519Sdavidxu	ret = pt_validate(th);
542144519Sdavidxu	if (ret)
543144519Sdavidxu		return (ret);
544144519Sdavidxu
545144989Sdavidxu	ret = ps_lgetfpregs(ta->ph, th->th_tid, fpregs);
546144519Sdavidxu	return (P2T(ret));
547132332Smarcel}
548132332Smarcel
549132332Smarcelstatic td_err_e
550144519Sdavidxupt_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
551132332Smarcel{
552144519Sdavidxu	const td_thragent_t *ta = th->th_ta;
553144519Sdavidxu	int ret;
554132332Smarcel
555144519Sdavidxu	TDBG_FUNC();
556144519Sdavidxu
557144519Sdavidxu	ret = pt_validate(th);
558144519Sdavidxu	if (ret)
559144519Sdavidxu		return (ret);
560144519Sdavidxu
561144989Sdavidxu	ret = ps_lgetregs(ta->ph, th->th_tid, gregs);
562144519Sdavidxu	return (P2T(ret));
563132332Smarcel}
564132332Smarcel
565146818Sdfr#ifdef __i386__
566132332Smarcelstatic td_err_e
567146818Sdfrpt_thr_setxmmregs(const td_thrhandle_t *th, const char *fxsave)
568146818Sdfr{
569146818Sdfr	const td_thragent_t *ta = th->th_ta;
570146818Sdfr	int ret;
571146818Sdfr
572146818Sdfr	TDBG_FUNC();
573146818Sdfr
574146818Sdfr	ret = pt_validate(th);
575146818Sdfr	if (ret)
576146818Sdfr		return (ret);
577146818Sdfr
578146818Sdfr	ret = ps_lsetxmmregs(ta->ph, th->th_tid, fxsave);
579146818Sdfr	return (P2T(ret));
580146818Sdfr}
581146818Sdfr#endif
582146818Sdfr
583146818Sdfrstatic td_err_e
584144519Sdavidxupt_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
585132332Smarcel{
586144519Sdavidxu	const td_thragent_t *ta = th->th_ta;
587144519Sdavidxu	int ret;
588132332Smarcel
589144519Sdavidxu	TDBG_FUNC();
590144519Sdavidxu
591144519Sdavidxu	ret = pt_validate(th);
592144519Sdavidxu	if (ret)
593144519Sdavidxu		return (ret);
594144519Sdavidxu
595144989Sdavidxu	ret = ps_lsetfpregs(ta->ph, th->th_tid, fpregs);
596144519Sdavidxu	return (P2T(ret));
597132332Smarcel}
598132332Smarcel
599132332Smarcelstatic td_err_e
600144519Sdavidxupt_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs)
601132332Smarcel{
602144519Sdavidxu	const td_thragent_t *ta = th->th_ta;
603144519Sdavidxu	int ret;
604132332Smarcel
605144519Sdavidxu	TDBG_FUNC();
606144519Sdavidxu
607144519Sdavidxu	ret = pt_validate(th);
608144519Sdavidxu	if (ret)
609144519Sdavidxu		return (ret);
610144519Sdavidxu
611144989Sdavidxu	ret = ps_lsetregs(ta->ph, th->th_tid, gregs);
612144519Sdavidxu	return (P2T(ret));
613132332Smarcel}
614132332Smarcel
615132332Smarcelstatic td_err_e
616144519Sdavidxupt_thr_event_enable(const td_thrhandle_t *th, int en)
617132332Smarcel{
618144922Sdavidxu	const td_thragent_t *ta = th->th_ta;
619144922Sdavidxu	int ret;
620144922Sdavidxu
621144519Sdavidxu	TDBG_FUNC();
622144922Sdavidxu	ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_report_events,
623144922Sdavidxu		&en, sizeof(int));
624144922Sdavidxu	return (P2T(ret));
625132332Smarcel}
626132332Smarcel
627132332Smarcelstatic td_err_e
628144519Sdavidxupt_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *setp)
629132332Smarcel{
630144922Sdavidxu	const td_thragent_t *ta = th->th_ta;
631144922Sdavidxu	td_thr_events_t mask;
632144922Sdavidxu	int ret;
633144922Sdavidxu
634144519Sdavidxu	TDBG_FUNC();
635144922Sdavidxu	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
636144922Sdavidxu			&mask, sizeof(mask));
637144922Sdavidxu	mask |= *setp;
638144922Sdavidxu	ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_event_mask,
639144922Sdavidxu			&mask, sizeof(mask));
640144922Sdavidxu	return (P2T(ret));
641144519Sdavidxu}
642133631Sdavidxu
643144519Sdavidxustatic td_err_e
644144519Sdavidxupt_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *setp)
645144519Sdavidxu{
646144922Sdavidxu	const td_thragent_t *ta = th->th_ta;
647144922Sdavidxu	td_thr_events_t mask;
648144922Sdavidxu	int ret;
649144922Sdavidxu
650144519Sdavidxu	TDBG_FUNC();
651144922Sdavidxu	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
652144922Sdavidxu			&mask, sizeof(mask));
653144922Sdavidxu	mask &= ~*setp;
654144922Sdavidxu	ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_event_mask,
655144922Sdavidxu			&mask, sizeof(mask));
656144922Sdavidxu	return (P2T(ret));
657132332Smarcel}
658132332Smarcel
659132332Smarcelstatic td_err_e
660144519Sdavidxupt_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg)
661132332Smarcel{
662144922Sdavidxu	static td_thrhandle_t handle;
663144922Sdavidxu	td_thragent_t *ta = (td_thragent_t *)th->th_ta;
664144922Sdavidxu	psaddr_t pt, pt_temp;
665144922Sdavidxu	long lwp;
666144922Sdavidxu	int ret;
667144922Sdavidxu	td_thr_events_e	tmp;
668144922Sdavidxu
669144519Sdavidxu	TDBG_FUNC();
670144922Sdavidxu	pt = th->th_thread;
671144922Sdavidxu	ret = ps_pread(ta->ph, ta->thread_last_event_addr, &pt_temp, sizeof(pt_temp));
672144922Sdavidxu	if (ret != 0)
673144922Sdavidxu		return (P2T(ret));
674144989Sdavidxu	/* Get event */
675144922Sdavidxu	ret = ps_pread(ta->ph, pt + ta->thread_off_event_buf, msg, sizeof(*msg));
676144922Sdavidxu	if (ret != 0)
677144922Sdavidxu		return (P2T(ret));
678144922Sdavidxu	if (msg->event == 0)
679144922Sdavidxu		return (TD_NOMSG);
680144989Sdavidxu	/*
681144989Sdavidxu	 * Take the event pointer, at the time, libthr only reports event
682144989Sdavidxu	 * once a time, so it is not a link list.
683144989Sdavidxu	 */
684144922Sdavidxu	if (pt == pt_temp) {
685144922Sdavidxu		pt_temp = NULL;
686144922Sdavidxu		ps_pwrite(ta->ph, ta->thread_last_event_addr, &pt_temp, sizeof(pt_temp));
687144922Sdavidxu	}
688144989Sdavidxu	/* Clear event */
689144922Sdavidxu	tmp = 0;
690144922Sdavidxu	ps_pwrite(ta->ph, pt + ta->thread_off_event_buf, &tmp, sizeof(tmp));
691144989Sdavidxu	/* Convert event */
692144922Sdavidxu	pt = (psaddr_t)msg->th_p;
693144922Sdavidxu	ret = ps_pread(ta->ph, pt + ta->thread_off_tid, &lwp, sizeof(lwp));
694144922Sdavidxu	if (ret != 0)
695144922Sdavidxu		return (P2T(ret));
696144922Sdavidxu	handle.th_ta = ta;
697144989Sdavidxu	handle.th_tid = lwp;
698144922Sdavidxu	handle.th_thread = pt;
699144922Sdavidxu	msg->th_p = &handle;
700144922Sdavidxu	return (0);
701132332Smarcel}
702132332Smarcel
703132332Smarcelstatic td_err_e
704144519Sdavidxupt_thr_sstep(const td_thrhandle_t *th, int step)
705132332Smarcel{
706144519Sdavidxu	TDBG_FUNC();
707144519Sdavidxu
708144989Sdavidxu	return pt_validate(th);
709132332Smarcel}
710132332Smarcel
711144519Sdavidxustatic int
712144519Sdavidxupt_validate(const td_thrhandle_t *th)
713144519Sdavidxu{
714144519Sdavidxu
715144989Sdavidxu	if (th->th_tid == 0 || th->th_thread == NULL)
716144989Sdavidxu		return (TD_ERR);
717144519Sdavidxu	return (TD_OK);
718144519Sdavidxu}
719144519Sdavidxu
720133631Sdavidxustatic td_err_e
721144519Sdavidxupt_thr_tls_get_addr(const td_thrhandle_t *th, void *_linkmap, size_t offset,
722144519Sdavidxu		    void **address)
723133631Sdavidxu{
724144519Sdavidxu	char *obj_entry;
725144519Sdavidxu	const td_thragent_t *ta = th->th_ta;
726146447Scharnier	psaddr_t tcb_addr, *dtv_addr;
727144519Sdavidxu	int tls_index, ret;
728144519Sdavidxu
729144519Sdavidxu	/* linkmap is a member of Obj_Entry */
730144519Sdavidxu	obj_entry = (char *)_linkmap - ta->thread_off_linkmap;
731144519Sdavidxu
732144519Sdavidxu	/* get tlsindex of the object file */
733144519Sdavidxu	ret = ps_pread(ta->ph,
734144519Sdavidxu		obj_entry + ta->thread_off_tlsindex,
735144519Sdavidxu		&tls_index, sizeof(tls_index));
736144519Sdavidxu	if (ret != 0)
737144519Sdavidxu		return (P2T(ret));
738144519Sdavidxu
739144519Sdavidxu	/* get thread tcb */
740144989Sdavidxu	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_tcb,
741144519Sdavidxu		&tcb_addr, sizeof(tcb_addr));
742144519Sdavidxu	if (ret != 0)
743144519Sdavidxu		return (P2T(ret));
744144519Sdavidxu
745144519Sdavidxu	/* get dtv array address */
746144519Sdavidxu	ret = ps_pread(ta->ph, tcb_addr + ta->thread_off_dtv,
747144519Sdavidxu		&dtv_addr, sizeof(dtv_addr));
748144519Sdavidxu	if (ret != 0)
749144519Sdavidxu		return (P2T(ret));
750144519Sdavidxu	/* now get the object's tls block base address */
751144519Sdavidxu	ret = ps_pread(ta->ph, &dtv_addr[tls_index+1], address,
752144519Sdavidxu		sizeof(*address));
753144519Sdavidxu	if (ret != 0)
754144519Sdavidxu		return (P2T(ret));
755144519Sdavidxu
756144519Sdavidxu	*address += offset;
757133631Sdavidxu	return (TD_OK);
758133631Sdavidxu}
759133631Sdavidxu
760132332Smarcelstruct ta_ops libthr_db_ops = {
761144519Sdavidxu	.to_init		= pt_init,
762144519Sdavidxu	.to_ta_clear_event	= pt_ta_clear_event,
763144519Sdavidxu	.to_ta_delete		= pt_ta_delete,
764144519Sdavidxu	.to_ta_event_addr	= pt_ta_event_addr,
765144519Sdavidxu	.to_ta_event_getmsg	= pt_ta_event_getmsg,
766144519Sdavidxu	.to_ta_map_id2thr	= pt_ta_map_id2thr,
767144519Sdavidxu	.to_ta_map_lwp2thr	= pt_ta_map_lwp2thr,
768144519Sdavidxu	.to_ta_new		= pt_ta_new,
769144519Sdavidxu	.to_ta_set_event	= pt_ta_set_event,
770144519Sdavidxu	.to_ta_thr_iter		= pt_ta_thr_iter,
771144519Sdavidxu	.to_ta_tsd_iter		= pt_ta_tsd_iter,
772144519Sdavidxu	.to_thr_clear_event	= pt_thr_clear_event,
773144519Sdavidxu	.to_thr_dbresume	= pt_thr_dbresume,
774144519Sdavidxu	.to_thr_dbsuspend	= pt_thr_dbsuspend,
775144519Sdavidxu	.to_thr_event_enable	= pt_thr_event_enable,
776144519Sdavidxu	.to_thr_event_getmsg	= pt_thr_event_getmsg,
777144519Sdavidxu	.to_thr_get_info	= pt_thr_get_info,
778144519Sdavidxu	.to_thr_getfpregs	= pt_thr_getfpregs,
779144519Sdavidxu	.to_thr_getgregs	= pt_thr_getgregs,
780144519Sdavidxu	.to_thr_set_event	= pt_thr_set_event,
781144519Sdavidxu	.to_thr_setfpregs	= pt_thr_setfpregs,
782144519Sdavidxu	.to_thr_setgregs	= pt_thr_setgregs,
783144519Sdavidxu	.to_thr_validate	= pt_thr_validate,
784144519Sdavidxu	.to_thr_tls_get_addr	= pt_thr_tls_get_addr,
785132332Smarcel
786133631Sdavidxu	/* FreeBSD specific extensions. */
787144519Sdavidxu	.to_thr_sstep		= pt_thr_sstep,
788146818Sdfr#ifdef __i386__
789146818Sdfr	.to_thr_getxmmregs	= pt_thr_getxmmregs,
790146818Sdfr	.to_thr_setxmmregs	= pt_thr_setxmmregs,
791146818Sdfr#endif
792132332Smarcel};
793