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