libthr_db.c revision 144974
1/*
2 * Copyright (c) 2004 Marcel Moolenaar
3 * Copyright (c) 2005 David Xu
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/lib/libthread_db/libthr_db.c 144974 2005-04-12 23:33:08Z davidxu $");
30
31#include <proc_service.h>
32#include <stddef.h>
33#include <stdlib.h>
34#include <string.h>
35#include <sys/types.h>
36#include <sys/ptrace.h>
37#include <thread_db.h>
38#include <unistd.h>
39
40#include "thread_db_int.h"
41
42#define	TERMINATED	1
43
44struct pt_map {
45	int		used;
46	lwpid_t		lwp;
47	psaddr_t	thr;
48};
49
50struct td_thragent {
51	TD_THRAGENT_FIELDS;
52	psaddr_t	libthr_debug_addr;
53	psaddr_t	thread_list_addr;
54	psaddr_t	thread_active_threads_addr;
55	psaddr_t	thread_keytable_addr;
56	psaddr_t	thread_last_event_addr;
57	psaddr_t	thread_event_mask_addr;
58	psaddr_t	thread_bp_create_addr;
59	psaddr_t	thread_bp_death_addr;
60	int		thread_off_dtv;
61	int		thread_off_tlsindex;
62	int		thread_off_attr_flags;
63	int		thread_size_key;
64	int		thread_off_tcb;
65	int		thread_off_linkmap;
66	int		thread_off_next;
67	int		thread_off_state;
68	int		thread_off_tid;
69	int		thread_max_keys;
70	int		thread_off_key_allocated;
71	int		thread_off_key_destructor;
72	int		thread_off_report_events;
73	int		thread_off_event_mask;
74	int		thread_off_event_buf;
75	int		thread_state_zoombie;
76	int		thread_state_running;
77	struct pt_map	*map;
78	int		map_len;
79};
80
81#define P2T(c) ps2td(c)
82
83static void pt_unmap_lwp(const td_thragent_t *ta, lwpid_t lwp);
84static int pt_validate(const td_thrhandle_t *th);
85
86static int
87ps2td(int c)
88{
89	switch (c) {
90	case PS_OK:
91		return TD_OK;
92	case PS_ERR:
93		return TD_ERR;
94	case PS_BADPID:
95		return TD_BADPH;
96	case PS_BADLID:
97		return TD_NOLWP;
98	case PS_BADADDR:
99		return TD_ERR;
100	case PS_NOSYM:
101		return TD_NOLIBTHREAD;
102	case PS_NOFREGS:
103		return TD_NOFPREGS;
104	default:
105		return TD_ERR;
106	}
107}
108
109static long
110pt_map_thread(const td_thragent_t *const_ta, long lwp, psaddr_t pt)
111{
112	td_thragent_t *ta = __DECONST(td_thragent_t *, const_ta);
113	struct pt_map *new;
114	int i, first = -1;
115
116	/* leave zero out */
117	for (i = 1; i < ta->map_len; ++i) {
118		if (ta->map[i].used == 0) {
119			if (first == -1)
120				first = i;
121		} else if (ta->map[i].lwp == lwp) {
122			ta->map[i].thr = pt;
123			return (i);
124		}
125	}
126
127	if (first == -1) {
128		if (ta->map_len == 0) {
129			ta->map = calloc(20, sizeof(struct pt_map));
130			if (ta->map == NULL)
131				return (-1);
132			ta->map_len = 20;
133			first = 1;
134		} else {
135			new = realloc(ta->map,
136			              sizeof(struct pt_map) * ta->map_len * 2);
137			if (new == NULL)
138				return (-1);
139			memset(new + ta->map_len, 0, sizeof(struct pt_map) *
140			       ta->map_len);
141			first = ta->map_len;
142			ta->map = new;
143			ta->map_len *= 2;
144		}
145	}
146
147	ta->map[first].used = 1;
148	ta->map[first].thr  = pt;
149	ta->map[first].lwp  = lwp;
150	return (first);
151}
152
153static td_err_e
154pt_init(void)
155{
156	return (0);
157}
158
159static td_err_e
160pt_ta_new(struct ps_prochandle *ph, td_thragent_t **pta)
161{
162#define LOOKUP_SYM(proc, sym, addr) 			\
163	ret = ps_pglobal_lookup(proc, NULL, sym, addr);	\
164	if (ret != 0) {					\
165		TDBG("can not find symbol: %s\n", sym);	\
166		ret = TD_NOLIBTHREAD;			\
167		goto error;				\
168	}
169
170#define	LOOKUP_VAL(proc, sym, val)			\
171	ret = ps_pglobal_lookup(proc, NULL, sym, &vaddr);\
172	if (ret != 0) {					\
173		TDBG("can not find symbol: %s\n", sym);	\
174		ret = TD_NOLIBTHREAD;			\
175		goto error;				\
176	}						\
177	ret = ps_pread(proc, vaddr, val, sizeof(int));	\
178	if (ret != 0) {					\
179		TDBG("can not read value of %s\n", sym);\
180		ret = TD_NOLIBTHREAD;			\
181		goto error;				\
182	}
183
184	td_thragent_t *ta;
185	psaddr_t vaddr;
186	int dbg;
187	int ret;
188
189	TDBG_FUNC();
190
191	ta = malloc(sizeof(td_thragent_t));
192	if (ta == NULL)
193		return (TD_MALLOC);
194
195	ta->ph = ph;
196	ta->map = NULL;
197	ta->map_len = 0;
198
199	LOOKUP_SYM(ph, "_libthr_debug",		&ta->libthr_debug_addr);
200	LOOKUP_SYM(ph, "_thread_list",		&ta->thread_list_addr);
201	LOOKUP_SYM(ph, "_thread_active_threads",&ta->thread_active_threads_addr);
202	LOOKUP_SYM(ph, "_thread_keytable",	&ta->thread_keytable_addr);
203	LOOKUP_SYM(ph, "_thread_last_event",	&ta->thread_last_event_addr);
204	LOOKUP_SYM(ph, "_thread_event_mask",	&ta->thread_event_mask_addr);
205	LOOKUP_SYM(ph, "_thread_bp_create",	&ta->thread_bp_create_addr);
206	LOOKUP_SYM(ph, "_thread_bp_death",	&ta->thread_bp_death_addr);
207	LOOKUP_VAL(ph, "_thread_off_dtv",	&ta->thread_off_dtv);
208	LOOKUP_VAL(ph, "_thread_off_tlsindex",	&ta->thread_off_tlsindex);
209	LOOKUP_VAL(ph, "_thread_off_attr_flags",	&ta->thread_off_attr_flags);
210	LOOKUP_VAL(ph, "_thread_size_key",	&ta->thread_size_key);
211	LOOKUP_VAL(ph, "_thread_off_tcb",	&ta->thread_off_tcb);
212	LOOKUP_VAL(ph, "_thread_off_tid",	&ta->thread_off_tid);
213	LOOKUP_VAL(ph, "_thread_off_linkmap",	&ta->thread_off_linkmap);
214	LOOKUP_VAL(ph, "_thread_off_next",	&ta->thread_off_next);
215	LOOKUP_VAL(ph, "_thread_off_state",	&ta->thread_off_state);
216	LOOKUP_VAL(ph, "_thread_max_keys",	&ta->thread_max_keys);
217	LOOKUP_VAL(ph, "_thread_off_key_allocated", &ta->thread_off_key_allocated);
218	LOOKUP_VAL(ph, "_thread_off_key_destructor", &ta->thread_off_key_destructor);
219	LOOKUP_VAL(ph, "_thread_state_running", &ta->thread_state_running);
220	LOOKUP_VAL(ph, "_thread_state_zoombie", &ta->thread_state_zoombie);
221	LOOKUP_VAL(ph, "_thread_off_report_events", &ta->thread_off_report_events);
222	LOOKUP_VAL(ph, "_thread_off_event_mask", &ta->thread_off_event_mask);
223	LOOKUP_VAL(ph, "_thread_off_event_buf", &ta->thread_off_event_buf);
224	dbg = getpid();
225	/*
226	 * If this fails it probably means we're debugging a core file and
227	 * can't write to it.
228	 */
229	ps_pwrite(ph, ta->libthr_debug_addr, &dbg, sizeof(int));
230	*pta = ta;
231	return (0);
232
233error:
234	free(ta);
235	return (ret);
236}
237
238static td_err_e
239pt_ta_delete(td_thragent_t *ta)
240{
241	int dbg;
242
243	TDBG_FUNC();
244
245	dbg = 0;
246	/*
247	 * Error returns from this write are not really a problem;
248	 * the process doesn't exist any more.
249	 */
250	ps_pwrite(ta->ph, ta->libthr_debug_addr, &dbg, sizeof(int));
251	if (ta->map)
252		free(ta->map);
253	free(ta);
254	return (TD_OK);
255}
256
257static td_err_e
258pt_ta_map_id2thr(const td_thragent_t *ta, thread_t id, td_thrhandle_t *th)
259{
260	prgregset_t gregs;
261	TAILQ_HEAD(, pthread) thread_list;
262	psaddr_t pt;
263	int ret;
264	long lwp;
265
266	TDBG_FUNC();
267
268	if (id < 0 || id >= ta->map_len || ta->map[id].used == 0)
269		return (TD_NOTHR);
270
271	if (ta->map[id].thr == NULL) {
272		/* check lwp */
273		ret = ptrace(PT_GETREGS, ta->map[id].lwp, (caddr_t)&gregs, 0);
274		if (ret != 0) {
275			/* no longer exists */
276			ta->map[id].used = 0;
277			return (TD_NOTHR);
278		}
279	} else {
280		ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
281		       sizeof(thread_list));
282		if (ret != 0)
283			return (P2T(ret));
284		pt = (psaddr_t)thread_list.tqh_first;
285		while (pt != 0 && ta->map[id].thr != pt) {
286			/* get next thread */
287			ret = ps_pread(ta->ph,
288				pt + ta->thread_off_next,
289				&pt, sizeof(pt));
290			if (ret != 0)
291				return (P2T(ret));
292		}
293		if (pt == 0) {
294			/* no longer exists */
295			ta->map[id].used = 0;
296			return (TD_NOTHR);
297		}
298		ret = ps_pread(ta->ph,
299			pt + ta->thread_off_tid,
300			&lwp, sizeof(long));
301		if (ret != 0)
302			return (P2T(ret));
303		if (lwp == TERMINATED) {
304			ta->map[id].used = 0;
305			return (TD_NOTHR);
306		}
307	}
308	th->th_ta  = ta;
309	th->th_tid = id;
310	th->th_thread = pt;
311	return (TD_OK);
312}
313
314static td_err_e
315pt_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwp, td_thrhandle_t *th)
316{
317	TAILQ_HEAD(, pthread) thread_list;
318	psaddr_t pt;
319	long tmp_lwp;
320	int ret;
321
322	TDBG_FUNC();
323
324	ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
325	               sizeof(thread_list));
326	if (ret != 0)
327		return (P2T(ret));
328	pt = (psaddr_t)thread_list.tqh_first;
329	while (pt != 0) {
330		ret = ps_pread(ta->ph, pt + ta->thread_off_tid,
331			       &tmp_lwp, sizeof(tmp_lwp));
332		if (ret != 0)
333			return (P2T(ret));
334		if (tmp_lwp == (long)lwp)
335			break;
336
337		/* get next thread */
338		ret = ps_pread(ta->ph,
339		           pt + ta->thread_off_next,
340		           &pt, sizeof(pt));
341		if (ret != 0)
342			return (P2T(ret));
343	}
344	if (pt == 0)
345		return (TD_NOTHR);
346	th->th_ta  = ta;
347	th->th_tid = pt_map_thread(ta, lwp, pt);
348	th->th_thread = pt;
349	if (th->th_tid == -1)
350		return (TD_MALLOC);
351	return (TD_OK);
352}
353
354static td_err_e
355pt_ta_thr_iter(const td_thragent_t *ta,
356               td_thr_iter_f *callback, void *cbdata_p,
357               td_thr_state_e state, int ti_pri,
358               sigset_t *ti_sigmask_p,
359               unsigned int ti_user_flags)
360{
361	TAILQ_HEAD(, pthread) thread_list;
362	td_thrhandle_t th;
363	long tmp_lwp;
364	psaddr_t pt;
365	int ret, isdead;
366
367	TDBG_FUNC();
368
369	ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
370		       sizeof(thread_list));
371	if (ret != 0)
372		return (P2T(ret));
373
374	pt = (psaddr_t)thread_list.tqh_first;
375	while (pt != 0) {
376		ret = ps_pread(ta->ph, pt + ta->thread_off_tid, &tmp_lwp,
377			      sizeof(tmp_lwp));
378		if (ret != 0)
379			return (P2T(ret));
380		if (tmp_lwp != 0 && tmp_lwp != TERMINATED) {
381			th.th_ta  = ta;
382			th.th_tid = pt_map_thread(ta, tmp_lwp, pt);
383			th.th_thread = pt;
384			if (th.th_tid == -1)
385				return (TD_MALLOC);
386			if ((*callback)(&th, cbdata_p))
387				return (TD_DBERR);
388		}
389		/* get next thread */
390		ret = ps_pread(ta->ph, pt + ta->thread_off_next, &pt,
391			       sizeof(pt));
392		if (ret != 0)
393			return (P2T(ret));
394	}
395	return (TD_OK);
396}
397
398static td_err_e
399pt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg)
400{
401	char *keytable;
402	void *destructor;
403	int i, ret, allocated;
404
405	TDBG_FUNC();
406
407	keytable = malloc(ta->thread_max_keys * ta->thread_size_key);
408	if (keytable == NULL)
409		return (TD_MALLOC);
410	ret = ps_pread(ta->ph, (psaddr_t)ta->thread_keytable_addr, keytable,
411	               ta->thread_max_keys * ta->thread_size_key);
412	if (ret != 0) {
413		free(keytable);
414		return (P2T(ret));
415	}
416	for (i = 0; i < ta->thread_max_keys; i++) {
417		allocated = *(int *)(keytable + i * ta->thread_size_key +
418			ta->thread_off_key_allocated);
419		destructor = *(void **)(keytable + i * ta->thread_size_key +
420			ta->thread_off_key_destructor);
421		if (allocated) {
422			ret = (ki)(i, destructor, arg);
423			if (ret != 0) {
424				free(keytable);
425				return (TD_DBERR);
426			}
427		}
428	}
429	free(keytable);
430	return (TD_OK);
431}
432
433static td_err_e
434pt_ta_event_addr(const td_thragent_t *ta, td_event_e event, td_notify_t *ptr)
435{
436	TDBG_FUNC();
437
438	switch (event) {
439	case TD_CREATE:
440		ptr->type = NOTIFY_BPT;
441		ptr->u.bptaddr = ta->thread_bp_create_addr;
442		return (0);
443	case TD_DEATH:
444		ptr->type = NOTIFY_BPT;
445		ptr->u.bptaddr = ta->thread_bp_death_addr;
446		return (0);
447	default:
448		return (TD_ERR);
449	}
450}
451
452static td_err_e
453pt_ta_set_event(const td_thragent_t *ta, td_thr_events_t *events)
454{
455	int ret;
456	td_thr_events_t mask;
457
458	TDBG_FUNC();
459	ret = ps_pread(ta->ph, ta->thread_event_mask_addr, &mask,
460		sizeof(mask));
461	if (ret != 0)
462		return (P2T(ret));
463	mask |= *events;
464	ret = ps_pwrite(ta->ph, ta->thread_event_mask_addr, &mask,
465		sizeof(mask));
466	return (P2T(ret));
467}
468
469static td_err_e
470pt_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *events)
471{
472	int ret;
473	td_thr_events_t mask;
474
475	TDBG_FUNC();
476	ret = ps_pread(ta->ph, ta->thread_event_mask_addr, &mask,
477		sizeof(mask));
478	if (ret != 0)
479		return (P2T(ret));
480	mask &= ~*events;
481	ret = ps_pwrite(ta->ph, ta->thread_event_mask_addr, &mask,
482		sizeof(mask));
483	return (P2T(ret));
484}
485
486static td_err_e
487pt_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg)
488{
489	static td_thrhandle_t handle;
490
491	psaddr_t pt, pt_temp;
492	long lwp;
493	int ret;
494	td_thr_events_e	tmp;
495
496	TDBG_FUNC();
497
498	ret = ps_pread(ta->ph, ta->thread_last_event_addr, &pt, sizeof(pt));
499	if (ret != 0)
500		return (P2T(ret));
501	if (pt == NULL)
502		return (TD_NOMSG);
503	/* At the time, libthr only reports event once a time. */
504	pt_temp = NULL;
505	ps_pwrite(ta->ph, ta->thread_last_event_addr, &pt_temp, sizeof(pt_temp));
506
507	ret = ps_pread(ta->ph, pt + ta->thread_off_event_buf, msg, sizeof(*msg));
508	if (ret != 0)
509		return (P2T(ret));
510	if (msg->event == 0)
511		return (TD_NOMSG);
512	/* clear event */
513	tmp = 0;
514	ps_pwrite(ta->ph, pt + ta->thread_off_event_buf, &tmp, sizeof(tmp));
515	pt = (psaddr_t)msg->th_p;
516	ret = ps_pread(ta->ph, pt + ta->thread_off_tid, &lwp, sizeof(lwp));
517	if (ret != 0)
518		return (P2T(ret));
519	handle.th_ta = ta;
520	handle.th_tid = pt_map_thread(ta, lwp, pt);
521	handle.th_thread = pt;
522	msg->th_p = &handle;
523	return (0);
524}
525
526static td_err_e
527pt_dbsuspend(const td_thrhandle_t *th, int suspend)
528{
529	td_thragent_t *ta = (td_thragent_t *)th->th_ta;
530	int ret;
531
532	TDBG_FUNC();
533
534	ret = pt_validate(th);
535	if (ret)
536		return (ret);
537
538	if (suspend)
539		ret = ps_lstop(ta->ph, ta->map[th->th_tid].lwp);
540	else
541		ret = ps_lcontinue(ta->ph, ta->map[th->th_tid].lwp);
542	return (P2T(ret));
543}
544
545static td_err_e
546pt_thr_dbresume(const td_thrhandle_t *th)
547{
548	TDBG_FUNC();
549
550	return pt_dbsuspend(th, 0);
551}
552
553static td_err_e
554pt_thr_dbsuspend(const td_thrhandle_t *th)
555{
556	TDBG_FUNC();
557
558	return pt_dbsuspend(th, 1);
559}
560
561static td_err_e
562pt_thr_validate(const td_thrhandle_t *th)
563{
564	td_thrhandle_t temp;
565	int ret;
566
567	TDBG_FUNC();
568
569	ret = pt_ta_map_id2thr(th->th_ta, th->th_tid,
570	                       &temp);
571	return (ret);
572}
573
574static td_err_e
575pt_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
576{
577	const td_thragent_t *ta = th->th_ta;
578	int state;
579	int ret;
580
581	TDBG_FUNC();
582
583	ret = pt_validate(th);
584	if (ret)
585		return (ret);
586
587	memset(info, 0, sizeof(*info));
588	if (ta->map[th->th_tid].thr == 0) {
589		info->ti_type = TD_THR_SYSTEM;
590		info->ti_lid = ta->map[th->th_tid].lwp;
591		info->ti_tid = th->th_tid;
592		info->ti_state = TD_THR_RUN;
593		info->ti_type = TD_THR_SYSTEM;
594		info->ti_thread = NULL;
595		return (TD_OK);
596	}
597	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_state,
598	               &state, sizeof(state));
599	if (ret != 0)
600		return (P2T(ret));
601	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_report_events,
602		&info->ti_traceme, sizeof(int));
603	if (ret != 0)
604		return (P2T(ret));
605	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_event_mask,
606		&info->ti_events, sizeof(td_thr_events_t));
607	if (ret != 0)
608		return (P2T(ret));
609	info->ti_lid = ta->map[th->th_tid].lwp;
610	info->ti_tid = th->th_tid;
611	info->ti_thread = ta->map[th->th_tid].thr;
612	info->ti_ta_p = th->th_ta;
613	if (state == ta->thread_state_running)
614		info->ti_state = TD_THR_RUN;
615	else if (state == ta->thread_state_zoombie)
616		info->ti_state = TD_THR_ZOMBIE;
617	else
618		info->ti_state = TD_THR_SLEEP;
619	info->ti_type = TD_THR_USER;
620	return (0);
621}
622
623static td_err_e
624pt_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregs)
625{
626	const td_thragent_t *ta = th->th_ta;
627	int ret;
628
629	TDBG_FUNC();
630
631	ret = pt_validate(th);
632	if (ret)
633		return (ret);
634
635	ret = ps_lgetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs);
636	return (P2T(ret));
637}
638
639static td_err_e
640pt_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
641{
642	const td_thragent_t *ta = th->th_ta;
643	int ret;
644
645	TDBG_FUNC();
646
647	ret = pt_validate(th);
648	if (ret)
649		return (ret);
650
651	ret = ps_lgetregs(ta->ph,
652	                  ta->map[th->th_tid].lwp, gregs);
653	return (P2T(ret));
654}
655
656static td_err_e
657pt_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
658{
659	const td_thragent_t *ta = th->th_ta;
660	int ret;
661
662	TDBG_FUNC();
663
664	ret = pt_validate(th);
665	if (ret)
666		return (ret);
667
668	ret = ps_lsetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs);
669	return (P2T(ret));
670}
671
672static td_err_e
673pt_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs)
674{
675	const td_thragent_t *ta = th->th_ta;
676	int ret;
677
678	TDBG_FUNC();
679
680	ret = pt_validate(th);
681	if (ret)
682		return (ret);
683
684	ret = ps_lsetregs(ta->ph, ta->map[th->th_tid].lwp, gregs);
685	return (P2T(ret));
686}
687
688static td_err_e
689pt_thr_event_enable(const td_thrhandle_t *th, int en)
690{
691	const td_thragent_t *ta = th->th_ta;
692	int ret;
693
694	TDBG_FUNC();
695	ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_report_events,
696		&en, sizeof(int));
697	return (P2T(ret));
698}
699
700static td_err_e
701pt_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *setp)
702{
703	const td_thragent_t *ta = th->th_ta;
704	td_thr_events_t mask;
705	int ret;
706
707	TDBG_FUNC();
708	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
709			&mask, sizeof(mask));
710	mask |= *setp;
711	ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_event_mask,
712			&mask, sizeof(mask));
713	return (P2T(ret));
714}
715
716static td_err_e
717pt_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *setp)
718{
719	const td_thragent_t *ta = th->th_ta;
720	td_thr_events_t mask;
721	int ret;
722
723	TDBG_FUNC();
724	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
725			&mask, sizeof(mask));
726	mask &= ~*setp;
727	ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_event_mask,
728			&mask, sizeof(mask));
729	return (P2T(ret));
730}
731
732static td_err_e
733pt_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg)
734{
735	static td_thrhandle_t handle;
736	td_thragent_t *ta = (td_thragent_t *)th->th_ta;
737	psaddr_t pt, pt_temp;
738	long lwp;
739	int ret;
740	td_thr_events_e	tmp;
741
742	TDBG_FUNC();
743	pt = th->th_thread;
744	ret = ps_pread(ta->ph, ta->thread_last_event_addr, &pt_temp, sizeof(pt_temp));
745	if (ret != 0)
746		return (P2T(ret));
747	/* get event */
748	ret = ps_pread(ta->ph, pt + ta->thread_off_event_buf, msg, sizeof(*msg));
749	if (ret != 0)
750		return (P2T(ret));
751	if (msg->event == 0)
752		return (TD_NOMSG);
753	/* clear event, at the time, libthr only reports event once a time. */
754	if (pt == pt_temp) {
755		pt_temp = NULL;
756		ps_pwrite(ta->ph, ta->thread_last_event_addr, &pt_temp, sizeof(pt_temp));
757	}
758	tmp = 0;
759	ps_pwrite(ta->ph, pt + ta->thread_off_event_buf, &tmp, sizeof(tmp));
760	/* convert event */
761	pt = (psaddr_t)msg->th_p;
762	ret = ps_pread(ta->ph, pt + ta->thread_off_tid, &lwp, sizeof(lwp));
763	if (ret != 0)
764		return (P2T(ret));
765	handle.th_ta = ta;
766	handle.th_tid = pt_map_thread(ta, lwp, pt);
767	handle.th_thread = pt;
768	msg->th_p = &handle;
769	return (0);
770}
771
772static td_err_e
773pt_thr_sstep(const td_thrhandle_t *th, int step)
774{
775	const td_thragent_t *ta = th->th_ta;
776	int ret;
777
778	TDBG_FUNC();
779
780	ret = pt_validate(th);
781	if (ret)
782		return (ret);
783
784	if (ta->map[th->th_tid].thr == 0)
785		return (TD_BADTH);
786
787	return (0);
788}
789
790static int
791pt_validate(const td_thrhandle_t *th)
792{
793
794	if (th->th_tid <= 0 || th->th_tid >= th->th_ta->map_len ||
795	    th->th_ta->map[th->th_tid].used == 0)
796		return (TD_NOTHR);
797	return (TD_OK);
798}
799
800static td_err_e
801pt_thr_tls_get_addr(const td_thrhandle_t *th, void *_linkmap, size_t offset,
802		    void **address)
803{
804	char *obj_entry;
805	const td_thragent_t *ta = th->th_ta;
806	psaddr_t tcb_addr, *dtv_addr, tcb_tp;
807	int tls_index, ret;
808
809	/* linkmap is a member of Obj_Entry */
810	obj_entry = (char *)_linkmap - ta->thread_off_linkmap;
811
812	/* get tlsindex of the object file */
813	ret = ps_pread(ta->ph,
814		obj_entry + ta->thread_off_tlsindex,
815		&tls_index, sizeof(tls_index));
816	if (ret != 0)
817		return (P2T(ret));
818
819	/* get thread tcb */
820	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
821		ta->thread_off_tcb,
822		&tcb_addr, sizeof(tcb_addr));
823	if (ret != 0)
824		return (P2T(ret));
825
826	/* get dtv array address */
827	ret = ps_pread(ta->ph, tcb_addr + ta->thread_off_dtv,
828		&dtv_addr, sizeof(dtv_addr));
829	if (ret != 0)
830		return (P2T(ret));
831	/* now get the object's tls block base address */
832	ret = ps_pread(ta->ph, &dtv_addr[tls_index+1], address,
833		sizeof(*address));
834	if (ret != 0)
835		return (P2T(ret));
836
837	*address += offset;
838	return (TD_OK);
839}
840
841struct ta_ops libthr_db_ops = {
842	.to_init		= pt_init,
843	.to_ta_clear_event	= pt_ta_clear_event,
844	.to_ta_delete		= pt_ta_delete,
845	.to_ta_event_addr	= pt_ta_event_addr,
846	.to_ta_event_getmsg	= pt_ta_event_getmsg,
847	.to_ta_map_id2thr	= pt_ta_map_id2thr,
848	.to_ta_map_lwp2thr	= pt_ta_map_lwp2thr,
849	.to_ta_new		= pt_ta_new,
850	.to_ta_set_event	= pt_ta_set_event,
851	.to_ta_thr_iter		= pt_ta_thr_iter,
852	.to_ta_tsd_iter		= pt_ta_tsd_iter,
853	.to_thr_clear_event	= pt_thr_clear_event,
854	.to_thr_dbresume	= pt_thr_dbresume,
855	.to_thr_dbsuspend	= pt_thr_dbsuspend,
856	.to_thr_event_enable	= pt_thr_event_enable,
857	.to_thr_event_getmsg	= pt_thr_event_getmsg,
858	.to_thr_get_info	= pt_thr_get_info,
859	.to_thr_getfpregs	= pt_thr_getfpregs,
860	.to_thr_getgregs	= pt_thr_getgregs,
861	.to_thr_set_event	= pt_thr_set_event,
862	.to_thr_setfpregs	= pt_thr_setfpregs,
863	.to_thr_setgregs	= pt_thr_setgregs,
864	.to_thr_validate	= pt_thr_validate,
865	.to_thr_tls_get_addr	= pt_thr_tls_get_addr,
866
867	/* FreeBSD specific extensions. */
868	.to_thr_sstep		= pt_thr_sstep,
869};
870