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