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$");
30132332Smarcel
31132332Smarcel#include <proc_service.h>
32144519Sdavidxu#include <stddef.h>
33132332Smarcel#include <stdlib.h>
34144519Sdavidxu#include <string.h>
35144519Sdavidxu#include <sys/types.h>
36177490Sdavidxu#include <sys/linker_set.h>
37144519Sdavidxu#include <sys/ptrace.h>
38132332Smarcel#include <thread_db.h>
39144519Sdavidxu#include <unistd.h>
40132332Smarcel
41132332Smarcel#include "thread_db_int.h"
42132332Smarcel
43144922Sdavidxu#define	TERMINATED	1
44144922Sdavidxu
45132332Smarcelstruct td_thragent {
46132332Smarcel	TD_THRAGENT_FIELDS;
47144519Sdavidxu	psaddr_t	libthr_debug_addr;
48144519Sdavidxu	psaddr_t	thread_list_addr;
49144519Sdavidxu	psaddr_t	thread_active_threads_addr;
50144519Sdavidxu	psaddr_t	thread_keytable_addr;
51144922Sdavidxu	psaddr_t	thread_last_event_addr;
52144922Sdavidxu	psaddr_t	thread_event_mask_addr;
53144922Sdavidxu	psaddr_t	thread_bp_create_addr;
54144922Sdavidxu	psaddr_t	thread_bp_death_addr;
55144519Sdavidxu	int		thread_off_dtv;
56144519Sdavidxu	int		thread_off_tlsindex;
57144519Sdavidxu	int		thread_off_attr_flags;
58144519Sdavidxu	int		thread_size_key;
59144519Sdavidxu	int		thread_off_tcb;
60144519Sdavidxu	int		thread_off_linkmap;
61144519Sdavidxu	int		thread_off_next;
62144519Sdavidxu	int		thread_off_state;
63144519Sdavidxu	int		thread_off_tid;
64144519Sdavidxu	int		thread_max_keys;
65144519Sdavidxu	int		thread_off_key_allocated;
66144519Sdavidxu	int		thread_off_key_destructor;
67144922Sdavidxu	int		thread_off_report_events;
68144922Sdavidxu	int		thread_off_event_mask;
69144922Sdavidxu	int		thread_off_event_buf;
70144519Sdavidxu	int		thread_state_zoombie;
71144519Sdavidxu	int		thread_state_running;
72132332Smarcel};
73132332Smarcel
74144519Sdavidxu#define P2T(c) ps2td(c)
75144519Sdavidxu
76144519Sdavidxustatic int pt_validate(const td_thrhandle_t *th);
77144519Sdavidxu
78144519Sdavidxustatic int
79144519Sdavidxups2td(int c)
80132332Smarcel{
81144519Sdavidxu	switch (c) {
82144519Sdavidxu	case PS_OK:
83144519Sdavidxu		return TD_OK;
84144519Sdavidxu	case PS_ERR:
85144519Sdavidxu		return TD_ERR;
86144519Sdavidxu	case PS_BADPID:
87144519Sdavidxu		return TD_BADPH;
88144519Sdavidxu	case PS_BADLID:
89144519Sdavidxu		return TD_NOLWP;
90144519Sdavidxu	case PS_BADADDR:
91144519Sdavidxu		return TD_ERR;
92144519Sdavidxu	case PS_NOSYM:
93144519Sdavidxu		return TD_NOLIBTHREAD;
94144519Sdavidxu	case PS_NOFREGS:
95144519Sdavidxu		return TD_NOFPREGS;
96144519Sdavidxu	default:
97144519Sdavidxu		return TD_ERR;
98144519Sdavidxu	}
99132332Smarcel}
100132332Smarcel
101132332Smarcelstatic td_err_e
102144519Sdavidxupt_init(void)
103132332Smarcel{
104144519Sdavidxu	return (0);
105132332Smarcel}
106132332Smarcel
107132332Smarcelstatic td_err_e
108144519Sdavidxupt_ta_new(struct ps_prochandle *ph, td_thragent_t **pta)
109132332Smarcel{
110144519Sdavidxu#define LOOKUP_SYM(proc, sym, addr) 			\
111144519Sdavidxu	ret = ps_pglobal_lookup(proc, NULL, sym, addr);	\
112144519Sdavidxu	if (ret != 0) {					\
113144519Sdavidxu		TDBG("can not find symbol: %s\n", sym);	\
114144519Sdavidxu		ret = TD_NOLIBTHREAD;			\
115144519Sdavidxu		goto error;				\
116144519Sdavidxu	}
117144519Sdavidxu
118144519Sdavidxu#define	LOOKUP_VAL(proc, sym, val)			\
119144519Sdavidxu	ret = ps_pglobal_lookup(proc, NULL, sym, &vaddr);\
120144519Sdavidxu	if (ret != 0) {					\
121144519Sdavidxu		TDBG("can not find symbol: %s\n", sym);	\
122144519Sdavidxu		ret = TD_NOLIBTHREAD;			\
123144519Sdavidxu		goto error;				\
124144519Sdavidxu	}						\
125144519Sdavidxu	ret = ps_pread(proc, vaddr, val, sizeof(int));	\
126144519Sdavidxu	if (ret != 0) {					\
127144519Sdavidxu		TDBG("can not read value of %s\n", sym);\
128144519Sdavidxu		ret = TD_NOLIBTHREAD;			\
129144519Sdavidxu		goto error;				\
130144519Sdavidxu	}
131144519Sdavidxu
132144519Sdavidxu	td_thragent_t *ta;
133144519Sdavidxu	psaddr_t vaddr;
134144519Sdavidxu	int dbg;
135144519Sdavidxu	int ret;
136144519Sdavidxu
137144519Sdavidxu	TDBG_FUNC();
138144519Sdavidxu
139144519Sdavidxu	ta = malloc(sizeof(td_thragent_t));
140144519Sdavidxu	if (ta == NULL)
141144519Sdavidxu		return (TD_MALLOC);
142144519Sdavidxu
143144519Sdavidxu	ta->ph = ph;
144144519Sdavidxu
145144519Sdavidxu	LOOKUP_SYM(ph, "_libthr_debug",		&ta->libthr_debug_addr);
146144519Sdavidxu	LOOKUP_SYM(ph, "_thread_list",		&ta->thread_list_addr);
147144519Sdavidxu	LOOKUP_SYM(ph, "_thread_active_threads",&ta->thread_active_threads_addr);
148144519Sdavidxu	LOOKUP_SYM(ph, "_thread_keytable",	&ta->thread_keytable_addr);
149144922Sdavidxu	LOOKUP_SYM(ph, "_thread_last_event",	&ta->thread_last_event_addr);
150144922Sdavidxu	LOOKUP_SYM(ph, "_thread_event_mask",	&ta->thread_event_mask_addr);
151144922Sdavidxu	LOOKUP_SYM(ph, "_thread_bp_create",	&ta->thread_bp_create_addr);
152144922Sdavidxu	LOOKUP_SYM(ph, "_thread_bp_death",	&ta->thread_bp_death_addr);
153144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_dtv",	&ta->thread_off_dtv);
154144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_tlsindex",	&ta->thread_off_tlsindex);
155144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_attr_flags",	&ta->thread_off_attr_flags);
156144519Sdavidxu	LOOKUP_VAL(ph, "_thread_size_key",	&ta->thread_size_key);
157144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_tcb",	&ta->thread_off_tcb);
158144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_tid",	&ta->thread_off_tid);
159144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_linkmap",	&ta->thread_off_linkmap);
160144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_next",	&ta->thread_off_next);
161144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_state",	&ta->thread_off_state);
162144519Sdavidxu	LOOKUP_VAL(ph, "_thread_max_keys",	&ta->thread_max_keys);
163144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_key_allocated", &ta->thread_off_key_allocated);
164144519Sdavidxu	LOOKUP_VAL(ph, "_thread_off_key_destructor", &ta->thread_off_key_destructor);
165144519Sdavidxu	LOOKUP_VAL(ph, "_thread_state_running", &ta->thread_state_running);
166144519Sdavidxu	LOOKUP_VAL(ph, "_thread_state_zoombie", &ta->thread_state_zoombie);
167144922Sdavidxu	LOOKUP_VAL(ph, "_thread_off_report_events", &ta->thread_off_report_events);
168144922Sdavidxu	LOOKUP_VAL(ph, "_thread_off_event_mask", &ta->thread_off_event_mask);
169144922Sdavidxu	LOOKUP_VAL(ph, "_thread_off_event_buf", &ta->thread_off_event_buf);
170144519Sdavidxu	dbg = getpid();
171144519Sdavidxu	/*
172144519Sdavidxu	 * If this fails it probably means we're debugging a core file and
173144519Sdavidxu	 * can't write to it.
174144519Sdavidxu	 */
175144519Sdavidxu	ps_pwrite(ph, ta->libthr_debug_addr, &dbg, sizeof(int));
176144519Sdavidxu	*pta = ta;
177144519Sdavidxu	return (0);
178144519Sdavidxu
179144519Sdavidxuerror:
180132332Smarcel	free(ta);
181144519Sdavidxu	return (ret);
182132332Smarcel}
183132332Smarcel
184132332Smarcelstatic td_err_e
185144519Sdavidxupt_ta_delete(td_thragent_t *ta)
186132332Smarcel{
187144519Sdavidxu	int dbg;
188144519Sdavidxu
189144519Sdavidxu	TDBG_FUNC();
190144519Sdavidxu
191144519Sdavidxu	dbg = 0;
192144519Sdavidxu	/*
193144519Sdavidxu	 * Error returns from this write are not really a problem;
194144519Sdavidxu	 * the process doesn't exist any more.
195144519Sdavidxu	 */
196144519Sdavidxu	ps_pwrite(ta->ph, ta->libthr_debug_addr, &dbg, sizeof(int));
197144519Sdavidxu	free(ta);
198144519Sdavidxu	return (TD_OK);
199132332Smarcel}
200132332Smarcel
201132332Smarcelstatic td_err_e
202144519Sdavidxupt_ta_map_id2thr(const td_thragent_t *ta, thread_t id, td_thrhandle_t *th)
203132332Smarcel{
204144519Sdavidxu	psaddr_t pt;
205224693Smarius	int64_t lwp;
206144922Sdavidxu	int ret;
207144519Sdavidxu
208144519Sdavidxu	TDBG_FUNC();
209144519Sdavidxu
210144989Sdavidxu	if (id == 0)
211144519Sdavidxu		return (TD_NOTHR);
212183021Smarcel	ret = thr_pread_ptr(ta, ta->thread_list_addr, &pt);
213144519Sdavidxu	if (ret != 0)
214183021Smarcel		return (TD_ERR);
215144989Sdavidxu	/* Iterate through thread list to find pthread */
216180982Smarcel	while (pt != 0) {
217224683Smarius		ret = thr_pread_long(ta, pt + ta->thread_off_tid, &lwp);
218144519Sdavidxu		if (ret != 0)
219183021Smarcel			return (TD_ERR);
220144989Sdavidxu		if (lwp == id)
221144519Sdavidxu			break;
222144519Sdavidxu		/* get next thread */
223183021Smarcel		ret = thr_pread_ptr(ta, pt + ta->thread_off_next, &pt);
224144519Sdavidxu		if (ret != 0)
225183021Smarcel			return (TD_ERR);
226133631Sdavidxu	}
227180982Smarcel	if (pt == 0)
228144519Sdavidxu		return (TD_NOTHR);
229144989Sdavidxu	th->th_ta = ta;
230144989Sdavidxu	th->th_tid = id;
231144663Sdavidxu	th->th_thread = pt;
232144519Sdavidxu	return (TD_OK);
233132332Smarcel}
234132332Smarcel
235132332Smarcelstatic td_err_e
236144989Sdavidxupt_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwp, td_thrhandle_t *th)
237144989Sdavidxu{
238144989Sdavidxu	return (pt_ta_map_id2thr(ta, lwp, th));
239144989Sdavidxu}
240144989Sdavidxu
241144989Sdavidxustatic td_err_e
242181341Smarcelpt_ta_thr_iter(const td_thragent_t *ta, td_thr_iter_f *callback,
243181341Smarcel    void *cbdata_p, td_thr_state_e state __unused, int ti_pri __unused,
244181341Smarcel    sigset_t *ti_sigmask_p __unused, unsigned int ti_user_flags __unused)
245132332Smarcel{
246144519Sdavidxu	td_thrhandle_t th;
247144519Sdavidxu	psaddr_t pt;
248224693Smarius	int64_t lwp;
249144989Sdavidxu	int ret;
250132332Smarcel
251144519Sdavidxu	TDBG_FUNC();
252132332Smarcel
253183021Smarcel	ret = thr_pread_ptr(ta, ta->thread_list_addr, &pt);
254144519Sdavidxu	if (ret != 0)
255183021Smarcel		return (TD_ERR);
256144519Sdavidxu	while (pt != 0) {
257224683Smarius		ret = thr_pread_long(ta, pt + ta->thread_off_tid, &lwp);
258144519Sdavidxu		if (ret != 0)
259183021Smarcel			return (TD_ERR);
260144989Sdavidxu		if (lwp != 0 && lwp != TERMINATED) {
261144989Sdavidxu			th.th_ta = ta;
262144989Sdavidxu			th.th_tid = (thread_t)lwp;
263144922Sdavidxu			th.th_thread = pt;
264144922Sdavidxu			if ((*callback)(&th, cbdata_p))
265144922Sdavidxu				return (TD_DBERR);
266133631Sdavidxu		}
267144519Sdavidxu		/* get next thread */
268183021Smarcel		ret = thr_pread_ptr(ta, pt + ta->thread_off_next, &pt);
269144519Sdavidxu		if (ret != 0)
270183021Smarcel			return (TD_ERR);
271132332Smarcel	}
272144519Sdavidxu	return (TD_OK);
273132332Smarcel}
274132332Smarcel
275132332Smarcelstatic td_err_e
276144519Sdavidxupt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg)
277132332Smarcel{
278181341Smarcel	void *keytable;
279144519Sdavidxu	void *destructor;
280144519Sdavidxu	int i, ret, allocated;
281132332Smarcel
282144519Sdavidxu	TDBG_FUNC();
283132332Smarcel
284144519Sdavidxu	keytable = malloc(ta->thread_max_keys * ta->thread_size_key);
285144519Sdavidxu	if (keytable == NULL)
286132332Smarcel		return (TD_MALLOC);
287144519Sdavidxu	ret = ps_pread(ta->ph, (psaddr_t)ta->thread_keytable_addr, keytable,
288144519Sdavidxu	               ta->thread_max_keys * ta->thread_size_key);
289144519Sdavidxu	if (ret != 0) {
290144519Sdavidxu		free(keytable);
291144519Sdavidxu		return (P2T(ret));
292144989Sdavidxu	}
293144519Sdavidxu	for (i = 0; i < ta->thread_max_keys; i++) {
294181341Smarcel		allocated = *(int *)(void *)((uintptr_t)keytable +
295181341Smarcel		    i * ta->thread_size_key + ta->thread_off_key_allocated);
296181341Smarcel		destructor = *(void **)(void *)((uintptr_t)keytable +
297181341Smarcel		    i * ta->thread_size_key + ta->thread_off_key_destructor);
298144519Sdavidxu		if (allocated) {
299144519Sdavidxu			ret = (ki)(i, destructor, arg);
300144519Sdavidxu			if (ret != 0) {
301144519Sdavidxu				free(keytable);
302144519Sdavidxu				return (TD_DBERR);
303144519Sdavidxu			}
304144519Sdavidxu		}
305144519Sdavidxu	}
306144519Sdavidxu	free(keytable);
307144519Sdavidxu	return (TD_OK);
308144519Sdavidxu}
309132332Smarcel
310144519Sdavidxustatic td_err_e
311144519Sdavidxupt_ta_event_addr(const td_thragent_t *ta, td_event_e event, td_notify_t *ptr)
312144519Sdavidxu{
313144989Sdavidxu
314144519Sdavidxu	TDBG_FUNC();
315144922Sdavidxu
316144922Sdavidxu	switch (event) {
317144922Sdavidxu	case TD_CREATE:
318144922Sdavidxu		ptr->type = NOTIFY_BPT;
319144922Sdavidxu		ptr->u.bptaddr = ta->thread_bp_create_addr;
320144922Sdavidxu		return (0);
321144922Sdavidxu	case TD_DEATH:
322144922Sdavidxu		ptr->type = NOTIFY_BPT;
323144922Sdavidxu		ptr->u.bptaddr = ta->thread_bp_death_addr;
324144922Sdavidxu		return (0);
325144922Sdavidxu	default:
326144922Sdavidxu		return (TD_ERR);
327144922Sdavidxu	}
328144519Sdavidxu}
329132332Smarcel
330144519Sdavidxustatic td_err_e
331144519Sdavidxupt_ta_set_event(const td_thragent_t *ta, td_thr_events_t *events)
332144519Sdavidxu{
333144989Sdavidxu	td_thr_events_t mask;
334144922Sdavidxu	int ret;
335144922Sdavidxu
336144519Sdavidxu	TDBG_FUNC();
337144922Sdavidxu	ret = ps_pread(ta->ph, ta->thread_event_mask_addr, &mask,
338144922Sdavidxu		sizeof(mask));
339144922Sdavidxu	if (ret != 0)
340144922Sdavidxu		return (P2T(ret));
341144922Sdavidxu	mask |= *events;
342144922Sdavidxu	ret = ps_pwrite(ta->ph, ta->thread_event_mask_addr, &mask,
343144922Sdavidxu		sizeof(mask));
344144922Sdavidxu	return (P2T(ret));
345132332Smarcel}
346132332Smarcel
347132332Smarcelstatic td_err_e
348144519Sdavidxupt_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *events)
349132332Smarcel{
350144989Sdavidxu	td_thr_events_t mask;
351144922Sdavidxu	int ret;
352144922Sdavidxu
353144519Sdavidxu	TDBG_FUNC();
354144922Sdavidxu	ret = ps_pread(ta->ph, ta->thread_event_mask_addr, &mask,
355144922Sdavidxu		sizeof(mask));
356144922Sdavidxu	if (ret != 0)
357144922Sdavidxu		return (P2T(ret));
358144922Sdavidxu	mask &= ~*events;
359144922Sdavidxu	ret = ps_pwrite(ta->ph, ta->thread_event_mask_addr, &mask,
360144922Sdavidxu		sizeof(mask));
361144922Sdavidxu	return (P2T(ret));
362132332Smarcel}
363132332Smarcel
364132332Smarcelstatic td_err_e
365144519Sdavidxupt_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg)
366132332Smarcel{
367144922Sdavidxu	static td_thrhandle_t handle;
368144922Sdavidxu
369183021Smarcel	psaddr_t pt;
370144989Sdavidxu	td_thr_events_e	tmp;
371224693Smarius	int64_t lwp;
372144922Sdavidxu	int ret;
373144922Sdavidxu
374144519Sdavidxu	TDBG_FUNC();
375144922Sdavidxu
376183021Smarcel	ret = thr_pread_ptr(ta, ta->thread_last_event_addr, &pt);
377144922Sdavidxu	if (ret != 0)
378183021Smarcel		return (TD_ERR);
379180982Smarcel	if (pt == 0)
380144922Sdavidxu		return (TD_NOMSG);
381144989Sdavidxu	/*
382144989Sdavidxu	 * Take the event pointer, at the time, libthr only reports event
383144989Sdavidxu	 * once a time, so it is not a link list.
384144989Sdavidxu	 */
385183021Smarcel	thr_pwrite_ptr(ta, ta->thread_last_event_addr, 0);
386144922Sdavidxu
387144989Sdavidxu	/* Read event info */
388144922Sdavidxu	ret = ps_pread(ta->ph, pt + ta->thread_off_event_buf, msg, sizeof(*msg));
389144922Sdavidxu	if (ret != 0)
390144922Sdavidxu		return (P2T(ret));
391144922Sdavidxu	if (msg->event == 0)
392144922Sdavidxu		return (TD_NOMSG);
393144989Sdavidxu	/* Clear event */
394144922Sdavidxu	tmp = 0;
395144922Sdavidxu	ps_pwrite(ta->ph, pt + ta->thread_off_event_buf, &tmp, sizeof(tmp));
396144989Sdavidxu	/* Convert event */
397183021Smarcel	pt = msg->th_p;
398224683Smarius	ret = thr_pread_long(ta, pt + ta->thread_off_tid, &lwp);
399144922Sdavidxu	if (ret != 0)
400183021Smarcel		return (TD_ERR);
401144922Sdavidxu	handle.th_ta = ta;
402144989Sdavidxu	handle.th_tid = lwp;
403144922Sdavidxu	handle.th_thread = pt;
404183021Smarcel	msg->th_p = (uintptr_t)&handle;
405144922Sdavidxu	return (0);
406144519Sdavidxu}
407132332Smarcel
408144519Sdavidxustatic td_err_e
409144519Sdavidxupt_dbsuspend(const td_thrhandle_t *th, int suspend)
410144519Sdavidxu{
411181341Smarcel	const td_thragent_t *ta = th->th_ta;
412144519Sdavidxu	int ret;
413132332Smarcel
414144519Sdavidxu	TDBG_FUNC();
415144519Sdavidxu
416144519Sdavidxu	ret = pt_validate(th);
417144519Sdavidxu	if (ret)
418144519Sdavidxu		return (ret);
419144519Sdavidxu
420144519Sdavidxu	if (suspend)
421144989Sdavidxu		ret = ps_lstop(ta->ph, th->th_tid);
422144519Sdavidxu	else
423144989Sdavidxu		ret = ps_lcontinue(ta->ph, th->th_tid);
424144519Sdavidxu	return (P2T(ret));
425132332Smarcel}
426132332Smarcel
427132332Smarcelstatic td_err_e
428144519Sdavidxupt_thr_dbresume(const td_thrhandle_t *th)
429132332Smarcel{
430144519Sdavidxu	TDBG_FUNC();
431144519Sdavidxu
432144519Sdavidxu	return pt_dbsuspend(th, 0);
433132332Smarcel}
434132332Smarcel
435132332Smarcelstatic td_err_e
436144519Sdavidxupt_thr_dbsuspend(const td_thrhandle_t *th)
437133631Sdavidxu{
438144519Sdavidxu	TDBG_FUNC();
439133631Sdavidxu
440144519Sdavidxu	return pt_dbsuspend(th, 1);
441133631Sdavidxu}
442133631Sdavidxu
443133631Sdavidxustatic td_err_e
444144519Sdavidxupt_thr_validate(const td_thrhandle_t *th)
445133631Sdavidxu{
446144519Sdavidxu	td_thrhandle_t temp;
447144519Sdavidxu	int ret;
448133631Sdavidxu
449144519Sdavidxu	TDBG_FUNC();
450144519Sdavidxu
451144989Sdavidxu	ret = pt_ta_map_id2thr(th->th_ta, th->th_tid, &temp);
452144519Sdavidxu	return (ret);
453133631Sdavidxu}
454133631Sdavidxu
455133631Sdavidxustatic td_err_e
456209689Skibpt_thr_get_info_common(const td_thrhandle_t *th, td_thrinfo_t *info, int old)
457132332Smarcel{
458144519Sdavidxu	const td_thragent_t *ta = th->th_ta;
459155414Sdavidxu	struct ptrace_lwpinfo linfo;
460193826Sdes	int traceme;
461144519Sdavidxu	int state;
462144519Sdavidxu	int ret;
463144519Sdavidxu
464144519Sdavidxu	TDBG_FUNC();
465144519Sdavidxu
466155387Sdavidxu	bzero(info, sizeof(*info));
467144519Sdavidxu	ret = pt_validate(th);
468144519Sdavidxu	if (ret)
469144519Sdavidxu		return (ret);
470183021Smarcel	ret = thr_pread_int(ta, th->th_thread + ta->thread_off_state, &state);
471144519Sdavidxu	if (ret != 0)
472183021Smarcel		return (TD_ERR);
473183021Smarcel	ret = thr_pread_int(ta, th->th_thread + ta->thread_off_report_events,
474193826Sdes	    &traceme);
475193826Sdes	info->ti_traceme = traceme;
476144974Sdavidxu	if (ret != 0)
477183021Smarcel		return (TD_ERR);
478144989Sdavidxu	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
479144974Sdavidxu		&info->ti_events, sizeof(td_thr_events_t));
480144974Sdavidxu	if (ret != 0)
481144974Sdavidxu		return (P2T(ret));
482144989Sdavidxu	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_tcb,
483144989Sdavidxu		&info->ti_tls, sizeof(void *));
484144989Sdavidxu	info->ti_lid = th->th_tid;
485144519Sdavidxu	info->ti_tid = th->th_tid;
486144989Sdavidxu	info->ti_thread = th->th_thread;
487144519Sdavidxu	info->ti_ta_p = th->th_ta;
488155414Sdavidxu	ret = ps_linfo(ta->ph, th->th_tid, &linfo);
489155414Sdavidxu	if (ret == PS_OK) {
490155414Sdavidxu		info->ti_sigmask = linfo.pl_sigmask;
491155414Sdavidxu		info->ti_pending = linfo.pl_siglist;
492209689Skib		if (!old) {
493209689Skib			if ((linfo.pl_flags & PL_FLAG_SI) != 0)
494209689Skib				info->ti_siginfo = linfo.pl_siginfo;
495209689Skib			else
496209689Skib				bzero(&info->ti_siginfo,
497209689Skib				    sizeof(info->ti_siginfo));
498209689Skib		}
499158681Sdavidxu	} else
500158681Sdavidxu		return (ret);
501144519Sdavidxu	if (state == ta->thread_state_running)
502144519Sdavidxu		info->ti_state = TD_THR_RUN;
503144519Sdavidxu	else if (state == ta->thread_state_zoombie)
504144519Sdavidxu		info->ti_state = TD_THR_ZOMBIE;
505144519Sdavidxu	else
506144519Sdavidxu		info->ti_state = TD_THR_SLEEP;
507144519Sdavidxu	info->ti_type = TD_THR_USER;
508144519Sdavidxu	return (0);
509132332Smarcel}
510132332Smarcel
511209689Skibstatic td_err_e
512209689Skibpt_thr_old_get_info(const td_thrhandle_t *th, td_old_thrinfo_t *info)
513209689Skib{
514209689Skib
515209689Skib	return (pt_thr_get_info_common(th, (td_thrinfo_t *)info, 1));
516209689Skib}
517209689Skib
518209689Skibstatic td_err_e
519209689Skibpt_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
520209689Skib{
521209689Skib
522209689Skib	return (pt_thr_get_info_common(th, info, 0));
523209689Skib}
524209689Skib
525146818Sdfr#ifdef __i386__
526132332Smarcelstatic td_err_e
527146818Sdfrpt_thr_getxmmregs(const td_thrhandle_t *th, char *fxsave)
528146818Sdfr{
529146818Sdfr	const td_thragent_t *ta = th->th_ta;
530146818Sdfr	int ret;
531146818Sdfr
532146818Sdfr	TDBG_FUNC();
533146818Sdfr
534146818Sdfr	ret = pt_validate(th);
535146818Sdfr	if (ret)
536146818Sdfr		return (ret);
537146818Sdfr
538146818Sdfr	ret = ps_lgetxmmregs(ta->ph, th->th_tid, fxsave);
539146818Sdfr	return (P2T(ret));
540146818Sdfr}
541146818Sdfr#endif
542146818Sdfr
543146818Sdfrstatic td_err_e
544144519Sdavidxupt_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregs)
545132332Smarcel{
546144519Sdavidxu	const td_thragent_t *ta = th->th_ta;
547144519Sdavidxu	int ret;
548144519Sdavidxu
549144519Sdavidxu	TDBG_FUNC();
550144519Sdavidxu
551144519Sdavidxu	ret = pt_validate(th);
552144519Sdavidxu	if (ret)
553144519Sdavidxu		return (ret);
554144519Sdavidxu
555144989Sdavidxu	ret = ps_lgetfpregs(ta->ph, th->th_tid, fpregs);
556144519Sdavidxu	return (P2T(ret));
557132332Smarcel}
558132332Smarcel
559132332Smarcelstatic td_err_e
560144519Sdavidxupt_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
561132332Smarcel{
562144519Sdavidxu	const td_thragent_t *ta = th->th_ta;
563144519Sdavidxu	int ret;
564132332Smarcel
565144519Sdavidxu	TDBG_FUNC();
566144519Sdavidxu
567144519Sdavidxu	ret = pt_validate(th);
568144519Sdavidxu	if (ret)
569144519Sdavidxu		return (ret);
570144519Sdavidxu
571144989Sdavidxu	ret = ps_lgetregs(ta->ph, th->th_tid, gregs);
572144519Sdavidxu	return (P2T(ret));
573132332Smarcel}
574132332Smarcel
575146818Sdfr#ifdef __i386__
576132332Smarcelstatic td_err_e
577146818Sdfrpt_thr_setxmmregs(const td_thrhandle_t *th, const char *fxsave)
578146818Sdfr{
579146818Sdfr	const td_thragent_t *ta = th->th_ta;
580146818Sdfr	int ret;
581146818Sdfr
582146818Sdfr	TDBG_FUNC();
583146818Sdfr
584146818Sdfr	ret = pt_validate(th);
585146818Sdfr	if (ret)
586146818Sdfr		return (ret);
587146818Sdfr
588146818Sdfr	ret = ps_lsetxmmregs(ta->ph, th->th_tid, fxsave);
589146818Sdfr	return (P2T(ret));
590146818Sdfr}
591146818Sdfr#endif
592146818Sdfr
593146818Sdfrstatic td_err_e
594144519Sdavidxupt_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
595132332Smarcel{
596144519Sdavidxu	const td_thragent_t *ta = th->th_ta;
597144519Sdavidxu	int ret;
598132332Smarcel
599144519Sdavidxu	TDBG_FUNC();
600144519Sdavidxu
601144519Sdavidxu	ret = pt_validate(th);
602144519Sdavidxu	if (ret)
603144519Sdavidxu		return (ret);
604144519Sdavidxu
605144989Sdavidxu	ret = ps_lsetfpregs(ta->ph, th->th_tid, fpregs);
606144519Sdavidxu	return (P2T(ret));
607132332Smarcel}
608132332Smarcel
609132332Smarcelstatic td_err_e
610144519Sdavidxupt_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs)
611132332Smarcel{
612144519Sdavidxu	const td_thragent_t *ta = th->th_ta;
613144519Sdavidxu	int ret;
614132332Smarcel
615144519Sdavidxu	TDBG_FUNC();
616144519Sdavidxu
617144519Sdavidxu	ret = pt_validate(th);
618144519Sdavidxu	if (ret)
619144519Sdavidxu		return (ret);
620144519Sdavidxu
621144989Sdavidxu	ret = ps_lsetregs(ta->ph, th->th_tid, gregs);
622144519Sdavidxu	return (P2T(ret));
623132332Smarcel}
624132332Smarcel
625132332Smarcelstatic td_err_e
626144519Sdavidxupt_thr_event_enable(const td_thrhandle_t *th, int en)
627132332Smarcel{
628144922Sdavidxu	const td_thragent_t *ta = th->th_ta;
629144922Sdavidxu	int ret;
630144922Sdavidxu
631144519Sdavidxu	TDBG_FUNC();
632144922Sdavidxu	ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_report_events,
633144922Sdavidxu		&en, sizeof(int));
634144922Sdavidxu	return (P2T(ret));
635132332Smarcel}
636132332Smarcel
637132332Smarcelstatic td_err_e
638144519Sdavidxupt_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *setp)
639132332Smarcel{
640144922Sdavidxu	const td_thragent_t *ta = th->th_ta;
641144922Sdavidxu	td_thr_events_t mask;
642144922Sdavidxu	int ret;
643144922Sdavidxu
644144519Sdavidxu	TDBG_FUNC();
645144922Sdavidxu	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
646144922Sdavidxu			&mask, sizeof(mask));
647144922Sdavidxu	mask |= *setp;
648144922Sdavidxu	ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_event_mask,
649144922Sdavidxu			&mask, sizeof(mask));
650144922Sdavidxu	return (P2T(ret));
651144519Sdavidxu}
652133631Sdavidxu
653144519Sdavidxustatic td_err_e
654144519Sdavidxupt_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *setp)
655144519Sdavidxu{
656144922Sdavidxu	const td_thragent_t *ta = th->th_ta;
657144922Sdavidxu	td_thr_events_t mask;
658144922Sdavidxu	int ret;
659144922Sdavidxu
660144519Sdavidxu	TDBG_FUNC();
661144922Sdavidxu	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
662144922Sdavidxu			&mask, sizeof(mask));
663144922Sdavidxu	mask &= ~*setp;
664144922Sdavidxu	ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_event_mask,
665144922Sdavidxu			&mask, sizeof(mask));
666144922Sdavidxu	return (P2T(ret));
667132332Smarcel}
668132332Smarcel
669132332Smarcelstatic td_err_e
670144519Sdavidxupt_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg)
671132332Smarcel{
672144922Sdavidxu	static td_thrhandle_t handle;
673181341Smarcel	const td_thragent_t *ta = th->th_ta;
674144922Sdavidxu	psaddr_t pt, pt_temp;
675224693Smarius	int64_t lwp;
676144922Sdavidxu	int ret;
677144922Sdavidxu	td_thr_events_e	tmp;
678144922Sdavidxu
679144519Sdavidxu	TDBG_FUNC();
680144922Sdavidxu	pt = th->th_thread;
681183021Smarcel	ret = thr_pread_ptr(ta, ta->thread_last_event_addr, &pt_temp);
682144922Sdavidxu	if (ret != 0)
683183021Smarcel		return (TD_ERR);
684144989Sdavidxu	/* Get event */
685144922Sdavidxu	ret = ps_pread(ta->ph, pt + ta->thread_off_event_buf, msg, sizeof(*msg));
686144922Sdavidxu	if (ret != 0)
687144922Sdavidxu		return (P2T(ret));
688144922Sdavidxu	if (msg->event == 0)
689144922Sdavidxu		return (TD_NOMSG);
690144989Sdavidxu	/*
691144989Sdavidxu	 * Take the event pointer, at the time, libthr only reports event
692144989Sdavidxu	 * once a time, so it is not a link list.
693144989Sdavidxu	 */
694183021Smarcel	if (pt == pt_temp)
695183021Smarcel		thr_pwrite_ptr(ta, ta->thread_last_event_addr, 0);
696183021Smarcel
697144989Sdavidxu	/* Clear event */
698144922Sdavidxu	tmp = 0;
699144922Sdavidxu	ps_pwrite(ta->ph, pt + ta->thread_off_event_buf, &tmp, sizeof(tmp));
700144989Sdavidxu	/* Convert event */
701183021Smarcel	pt = msg->th_p;
702224683Smarius	ret = thr_pread_long(ta, pt + ta->thread_off_tid, &lwp);
703144922Sdavidxu	if (ret != 0)
704183021Smarcel		return (TD_ERR);
705144922Sdavidxu	handle.th_ta = ta;
706144989Sdavidxu	handle.th_tid = lwp;
707144922Sdavidxu	handle.th_thread = pt;
708183021Smarcel	msg->th_p = (uintptr_t)&handle;
709144922Sdavidxu	return (0);
710132332Smarcel}
711132332Smarcel
712132332Smarcelstatic td_err_e
713181341Smarcelpt_thr_sstep(const td_thrhandle_t *th, int step __unused)
714132332Smarcel{
715144519Sdavidxu	TDBG_FUNC();
716144519Sdavidxu
717144989Sdavidxu	return pt_validate(th);
718132332Smarcel}
719132332Smarcel
720144519Sdavidxustatic int
721144519Sdavidxupt_validate(const td_thrhandle_t *th)
722144519Sdavidxu{
723144519Sdavidxu
724180982Smarcel	if (th->th_tid == 0 || th->th_thread == 0)
725144989Sdavidxu		return (TD_ERR);
726144519Sdavidxu	return (TD_OK);
727144519Sdavidxu}
728144519Sdavidxu
729133631Sdavidxustatic td_err_e
730180982Smarcelpt_thr_tls_get_addr(const td_thrhandle_t *th, psaddr_t _linkmap, size_t offset,
731180982Smarcel    psaddr_t *address)
732133631Sdavidxu{
733144519Sdavidxu	const td_thragent_t *ta = th->th_ta;
734180982Smarcel	psaddr_t dtv_addr, obj_entry, tcb_addr;
735144519Sdavidxu	int tls_index, ret;
736144519Sdavidxu
737144519Sdavidxu	/* linkmap is a member of Obj_Entry */
738180982Smarcel	obj_entry = _linkmap - ta->thread_off_linkmap;
739144519Sdavidxu
740144519Sdavidxu	/* get tlsindex of the object file */
741144519Sdavidxu	ret = ps_pread(ta->ph,
742144519Sdavidxu		obj_entry + ta->thread_off_tlsindex,
743144519Sdavidxu		&tls_index, sizeof(tls_index));
744144519Sdavidxu	if (ret != 0)
745144519Sdavidxu		return (P2T(ret));
746144519Sdavidxu
747144519Sdavidxu	/* get thread tcb */
748144989Sdavidxu	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_tcb,
749144519Sdavidxu		&tcb_addr, sizeof(tcb_addr));
750144519Sdavidxu	if (ret != 0)
751144519Sdavidxu		return (P2T(ret));
752144519Sdavidxu
753144519Sdavidxu	/* get dtv array address */
754144519Sdavidxu	ret = ps_pread(ta->ph, tcb_addr + ta->thread_off_dtv,
755144519Sdavidxu		&dtv_addr, sizeof(dtv_addr));
756144519Sdavidxu	if (ret != 0)
757144519Sdavidxu		return (P2T(ret));
758144519Sdavidxu	/* now get the object's tls block base address */
759180982Smarcel	ret = ps_pread(ta->ph, dtv_addr + sizeof(void *) * (tls_index+1),
760180982Smarcel	    address, sizeof(*address));
761144519Sdavidxu	if (ret != 0)
762144519Sdavidxu		return (P2T(ret));
763144519Sdavidxu
764144519Sdavidxu	*address += offset;
765133631Sdavidxu	return (TD_OK);
766133631Sdavidxu}
767133631Sdavidxu
768241720Sedstatic struct ta_ops libthr_db_ops = {
769144519Sdavidxu	.to_init		= pt_init,
770144519Sdavidxu	.to_ta_clear_event	= pt_ta_clear_event,
771144519Sdavidxu	.to_ta_delete		= pt_ta_delete,
772144519Sdavidxu	.to_ta_event_addr	= pt_ta_event_addr,
773144519Sdavidxu	.to_ta_event_getmsg	= pt_ta_event_getmsg,
774144519Sdavidxu	.to_ta_map_id2thr	= pt_ta_map_id2thr,
775144519Sdavidxu	.to_ta_map_lwp2thr	= pt_ta_map_lwp2thr,
776144519Sdavidxu	.to_ta_new		= pt_ta_new,
777144519Sdavidxu	.to_ta_set_event	= pt_ta_set_event,
778144519Sdavidxu	.to_ta_thr_iter		= pt_ta_thr_iter,
779144519Sdavidxu	.to_ta_tsd_iter		= pt_ta_tsd_iter,
780144519Sdavidxu	.to_thr_clear_event	= pt_thr_clear_event,
781144519Sdavidxu	.to_thr_dbresume	= pt_thr_dbresume,
782144519Sdavidxu	.to_thr_dbsuspend	= pt_thr_dbsuspend,
783144519Sdavidxu	.to_thr_event_enable	= pt_thr_event_enable,
784144519Sdavidxu	.to_thr_event_getmsg	= pt_thr_event_getmsg,
785209689Skib	.to_thr_old_get_info	= pt_thr_old_get_info,
786144519Sdavidxu	.to_thr_get_info	= pt_thr_get_info,
787144519Sdavidxu	.to_thr_getfpregs	= pt_thr_getfpregs,
788144519Sdavidxu	.to_thr_getgregs	= pt_thr_getgregs,
789144519Sdavidxu	.to_thr_set_event	= pt_thr_set_event,
790144519Sdavidxu	.to_thr_setfpregs	= pt_thr_setfpregs,
791144519Sdavidxu	.to_thr_setgregs	= pt_thr_setgregs,
792144519Sdavidxu	.to_thr_validate	= pt_thr_validate,
793144519Sdavidxu	.to_thr_tls_get_addr	= pt_thr_tls_get_addr,
794132332Smarcel
795133631Sdavidxu	/* FreeBSD specific extensions. */
796144519Sdavidxu	.to_thr_sstep		= pt_thr_sstep,
797146818Sdfr#ifdef __i386__
798146818Sdfr	.to_thr_getxmmregs	= pt_thr_getxmmregs,
799146818Sdfr	.to_thr_setxmmregs	= pt_thr_setxmmregs,
800146818Sdfr#endif
801132332Smarcel};
802177490Sdavidxu
803177490SdavidxuDATA_SET(__ta_ops, libthr_db_ops);
804