libthr_db.c revision 144663
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 144663 2005-04-05 11:38:30Z 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 42struct pt_map { 43 int used; 44 lwpid_t lwp; 45 psaddr_t thr; 46}; 47 48struct td_thragent { 49 TD_THRAGENT_FIELDS; 50 psaddr_t libthr_debug_addr; 51 psaddr_t thread_list_addr; 52 psaddr_t thread_listgen_addr; 53 psaddr_t thread_active_threads_addr; 54 psaddr_t thread_keytable_addr; 55 int thread_inited; 56 int thread_off_dtv; 57 int thread_off_tlsindex; 58 int thread_off_attr_flags; 59 int thread_size_key; 60 int thread_off_tcb; 61 int thread_off_linkmap; 62 int thread_off_thr_locklevel; 63 int thread_off_next; 64 int thread_off_state; 65 int thread_off_isdead; 66 int thread_off_tid; 67 int thread_max_keys; 68 int thread_off_key_allocated; 69 int thread_off_key_destructor; 70 int thread_state_zoombie; 71 int thread_state_running; 72 struct pt_map *map; 73 int map_len; 74}; 75 76#define P2T(c) ps2td(c) 77 78static void pt_unmap_lwp(const td_thragent_t *ta, lwpid_t lwp); 79static int pt_validate(const td_thrhandle_t *th); 80 81static int 82ps2td(int c) 83{ 84 switch (c) { 85 case PS_OK: 86 return TD_OK; 87 case PS_ERR: 88 return TD_ERR; 89 case PS_BADPID: 90 return TD_BADPH; 91 case PS_BADLID: 92 return TD_NOLWP; 93 case PS_BADADDR: 94 return TD_ERR; 95 case PS_NOSYM: 96 return TD_NOLIBTHREAD; 97 case PS_NOFREGS: 98 return TD_NOFPREGS; 99 default: 100 return TD_ERR; 101 } 102} 103 104static long 105pt_map_thread(const td_thragent_t *const_ta, long lwp, psaddr_t pt) 106{ 107 td_thragent_t *ta = __DECONST(td_thragent_t *, const_ta); 108 struct pt_map *new; 109 int i, first = -1; 110 111 /* leave zero out */ 112 for (i = 1; i < ta->map_len; ++i) { 113 if (ta->map[i].used == 0) { 114 if (first == -1) 115 first = i; 116 } else if (ta->map[i].lwp == lwp) { 117 ta->map[i].thr = pt; 118 return (i); 119 } 120 } 121 122 if (first == -1) { 123 if (ta->map_len == 0) { 124 ta->map = calloc(20, sizeof(struct pt_map)); 125 if (ta->map == NULL) 126 return (-1); 127 ta->map_len = 20; 128 first = 1; 129 } else { 130 new = realloc(ta->map, 131 sizeof(struct pt_map) * ta->map_len * 2); 132 if (new == NULL) 133 return (-1); 134 memset(new + ta->map_len, '\0', sizeof(struct pt_map) * 135 ta->map_len); 136 first = ta->map_len; 137 ta->map = new; 138 ta->map_len *= 2; 139 } 140 } 141 142 ta->map[first].used = 1; 143 ta->map[first].thr = pt; 144 ta->map[first].lwp = lwp; 145 return (first); 146} 147 148static td_err_e 149pt_init(void) 150{ 151 return (0); 152} 153 154static td_err_e 155pt_ta_new(struct ps_prochandle *ph, td_thragent_t **pta) 156{ 157#define LOOKUP_SYM(proc, sym, addr) \ 158 ret = ps_pglobal_lookup(proc, NULL, sym, addr); \ 159 if (ret != 0) { \ 160 TDBG("can not find symbol: %s\n", sym); \ 161 ret = TD_NOLIBTHREAD; \ 162 goto error; \ 163 } 164 165#define LOOKUP_VAL(proc, sym, val) \ 166 ret = ps_pglobal_lookup(proc, NULL, sym, &vaddr);\ 167 if (ret != 0) { \ 168 TDBG("can not find symbol: %s\n", sym); \ 169 ret = TD_NOLIBTHREAD; \ 170 goto error; \ 171 } \ 172 ret = ps_pread(proc, vaddr, val, sizeof(int)); \ 173 if (ret != 0) { \ 174 TDBG("can not read value of %s\n", sym);\ 175 ret = TD_NOLIBTHREAD; \ 176 goto error; \ 177 } 178 179 td_thragent_t *ta; 180 psaddr_t vaddr; 181 int dbg; 182 int ret; 183 184 TDBG_FUNC(); 185 186 ta = malloc(sizeof(td_thragent_t)); 187 if (ta == NULL) 188 return (TD_MALLOC); 189 190 ta->ph = ph; 191 ta->map = NULL; 192 ta->map_len = 0; 193 194 LOOKUP_SYM(ph, "_libthr_debug", &ta->libthr_debug_addr); 195 LOOKUP_SYM(ph, "_thread_list", &ta->thread_list_addr); 196 LOOKUP_SYM(ph, "_thread_active_threads",&ta->thread_active_threads_addr); 197 LOOKUP_SYM(ph, "_thread_keytable", &ta->thread_keytable_addr); 198 LOOKUP_VAL(ph, "_thread_off_dtv", &ta->thread_off_dtv); 199 LOOKUP_VAL(ph, "_thread_off_tlsindex", &ta->thread_off_tlsindex); 200 LOOKUP_VAL(ph, "_thread_off_attr_flags", &ta->thread_off_attr_flags); 201 LOOKUP_VAL(ph, "_thread_size_key", &ta->thread_size_key); 202 LOOKUP_VAL(ph, "_thread_off_tcb", &ta->thread_off_tcb); 203 LOOKUP_VAL(ph, "_thread_off_tid", &ta->thread_off_tid); 204 LOOKUP_VAL(ph, "_thread_off_linkmap", &ta->thread_off_linkmap); 205 LOOKUP_VAL(ph, "_thread_off_thr_locklevel", &ta->thread_off_thr_locklevel); 206 LOOKUP_VAL(ph, "_thread_off_next", &ta->thread_off_next); 207 LOOKUP_VAL(ph, "_thread_off_state", &ta->thread_off_state); 208 LOOKUP_VAL(ph, "_thread_off_isdead", &ta->thread_off_isdead); 209 LOOKUP_VAL(ph, "_thread_max_keys", &ta->thread_max_keys); 210 LOOKUP_VAL(ph, "_thread_off_key_allocated", &ta->thread_off_key_allocated); 211 LOOKUP_VAL(ph, "_thread_off_key_destructor", &ta->thread_off_key_destructor); 212 LOOKUP_VAL(ph, "_thread_state_running", &ta->thread_state_running); 213 LOOKUP_VAL(ph, "_thread_state_zoombie", &ta->thread_state_zoombie); 214 dbg = getpid(); 215 /* 216 * If this fails it probably means we're debugging a core file and 217 * can't write to it. 218 */ 219 ps_pwrite(ph, ta->libthr_debug_addr, &dbg, sizeof(int)); 220 *pta = ta; 221 return (0); 222 223error: 224 free(ta); 225 return (ret); 226} 227 228static td_err_e 229pt_ta_delete(td_thragent_t *ta) 230{ 231 int dbg; 232 233 TDBG_FUNC(); 234 235 dbg = 0; 236 /* 237 * Error returns from this write are not really a problem; 238 * the process doesn't exist any more. 239 */ 240 ps_pwrite(ta->ph, ta->libthr_debug_addr, &dbg, sizeof(int)); 241 if (ta->map) 242 free(ta->map); 243 free(ta); 244 return (TD_OK); 245} 246 247static td_err_e 248pt_ta_map_id2thr(const td_thragent_t *ta, thread_t id, td_thrhandle_t *th) 249{ 250 prgregset_t gregs; 251 TAILQ_HEAD(, pthread) thread_list; 252 psaddr_t pt; 253 int ret, isdead; 254 255 TDBG_FUNC(); 256 257 if (id < 0 || id >= ta->map_len || ta->map[id].used == 0) 258 return (TD_NOTHR); 259 260 if (ta->map[id].thr == NULL) { 261 /* check lwp */ 262 ret = ptrace(PT_GETREGS, ta->map[id].lwp, (caddr_t)&gregs, 0); 263 if (ret != 0) { 264 /* no longer exists */ 265 ta->map[id].used = 0; 266 return (TD_NOTHR); 267 } 268 } else { 269 ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list, 270 sizeof(thread_list)); 271 if (ret != 0) 272 return (P2T(ret)); 273 pt = (psaddr_t)thread_list.tqh_first; 274 while (pt != 0 && ta->map[id].thr != pt) { 275 /* get next thread */ 276 ret = ps_pread(ta->ph, 277 pt + ta->thread_off_next, 278 &pt, sizeof(pt)); 279 if (ret != 0) 280 return (P2T(ret)); 281 } 282 if (pt == 0) { 283 /* no longer exists */ 284 ta->map[id].used = 0; 285 return (TD_NOTHR); 286 } 287 ret = ps_pread(ta->ph, 288 pt + ta->thread_off_isdead, 289 &isdead, sizeof(isdead)); 290 if (ret != 0) 291 return (P2T(ret)); 292 if (isdead) { 293 ta->map[id].used = 0; 294 return (TD_NOTHR); 295 } 296 } 297 th->th_ta = ta; 298 th->th_tid = id; 299 th->th_thread = pt; 300 return (TD_OK); 301} 302 303static td_err_e 304pt_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwp, td_thrhandle_t *th) 305{ 306 TAILQ_HEAD(, pthread) thread_list; 307 psaddr_t pt; 308 long tmp_lwp; 309 int ret; 310 311 TDBG_FUNC(); 312 313 ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list, 314 sizeof(thread_list)); 315 if (ret != 0) 316 return (P2T(ret)); 317 pt = (psaddr_t)thread_list.tqh_first; 318 while (pt != 0) { 319 ret = ps_pread(ta->ph, pt + ta->thread_off_tid, 320 &tmp_lwp, sizeof(tmp_lwp)); 321 if (ret != 0) 322 return (P2T(ret)); 323 if (tmp_lwp == (long)lwp) 324 break; 325 326 /* get next thread */ 327 ret = ps_pread(ta->ph, 328 pt + ta->thread_off_next, 329 &pt, sizeof(pt)); 330 if (ret != 0) 331 return (P2T(ret)); 332 } 333 if (pt == 0) 334 return (TD_NOTHR); 335 th->th_ta = ta; 336 th->th_tid = pt_map_thread(ta, lwp, pt); 337 th->th_thread = pt; 338 if (th->th_tid == -1) 339 return (TD_MALLOC); 340 return (TD_OK); 341} 342 343static td_err_e 344pt_ta_thr_iter(const td_thragent_t *ta, 345 td_thr_iter_f *callback, void *cbdata_p, 346 td_thr_state_e state, int ti_pri, 347 sigset_t *ti_sigmask_p, 348 unsigned int ti_user_flags) 349{ 350 TAILQ_HEAD(, pthread) thread_list; 351 td_thrhandle_t th; 352 long tmp_lwp; 353 psaddr_t pt; 354 int ret, isdead; 355 356 TDBG_FUNC(); 357 358 ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list, 359 sizeof(thread_list)); 360 if (ret != 0) 361 return (P2T(ret)); 362 363 pt = (psaddr_t)thread_list.tqh_first; 364 while (pt != 0) { 365 ret = ps_pread(ta->ph, pt + ta->thread_off_isdead, &isdead, 366 sizeof(isdead)); 367 if (ret != 0) 368 return (P2T(ret)); 369 if (!isdead) { 370 ret = ps_pread(ta->ph, pt + ta->thread_off_tid, &tmp_lwp, 371 sizeof(tmp_lwp)); 372 if (ret != 0) 373 return (P2T(ret)); 374 if (tmp_lwp != 0) { 375 th.th_ta = ta; 376 th.th_tid = pt_map_thread(ta, tmp_lwp, pt); 377 th.th_thread = pt; 378 if (th.th_tid == -1) 379 return (TD_MALLOC); 380 if ((*callback)(&th, cbdata_p)) 381 return (TD_DBERR); 382 } 383 } 384 /* get next thread */ 385 ret = ps_pread(ta->ph, pt + ta->thread_off_next, &pt, 386 sizeof(pt)); 387 if (ret != 0) 388 return (P2T(ret)); 389 } 390 return (TD_OK); 391} 392 393static td_err_e 394pt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg) 395{ 396 char *keytable; 397 void *destructor; 398 int i, ret, allocated; 399 400 TDBG_FUNC(); 401 402 keytable = malloc(ta->thread_max_keys * ta->thread_size_key); 403 if (keytable == NULL) 404 return (TD_MALLOC); 405 ret = ps_pread(ta->ph, (psaddr_t)ta->thread_keytable_addr, keytable, 406 ta->thread_max_keys * ta->thread_size_key); 407 if (ret != 0) { 408 free(keytable); 409 return (P2T(ret)); 410 } 411 for (i = 0; i < ta->thread_max_keys; i++) { 412 allocated = *(int *)(keytable + i * ta->thread_size_key + 413 ta->thread_off_key_allocated); 414 destructor = *(void **)(keytable + i * ta->thread_size_key + 415 ta->thread_off_key_destructor); 416 if (allocated) { 417 ret = (ki)(i, destructor, arg); 418 if (ret != 0) { 419 free(keytable); 420 return (TD_DBERR); 421 } 422 } 423 } 424 free(keytable); 425 return (TD_OK); 426} 427 428static td_err_e 429pt_ta_event_addr(const td_thragent_t *ta, td_event_e event, td_notify_t *ptr) 430{ 431 TDBG_FUNC(); 432 return (TD_NOEVENT); 433} 434 435static td_err_e 436pt_ta_set_event(const td_thragent_t *ta, td_thr_events_t *events) 437{ 438 TDBG_FUNC(); 439 return (TD_ERR); 440} 441 442static td_err_e 443pt_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *events) 444{ 445 TDBG_FUNC(); 446 return (TD_ERR); 447} 448 449static td_err_e 450pt_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg) 451{ 452 TDBG_FUNC(); 453 return (TD_NOMSG); 454} 455 456static td_err_e 457pt_dbsuspend(const td_thrhandle_t *th, int suspend) 458{ 459 td_thragent_t *ta = (td_thragent_t *)th->th_ta; 460 int ret; 461 462 TDBG_FUNC(); 463 464 ret = pt_validate(th); 465 if (ret) 466 return (ret); 467 468 if (suspend) 469 ret = ps_lstop(ta->ph, ta->map[th->th_tid].lwp); 470 else 471 ret = ps_lcontinue(ta->ph, ta->map[th->th_tid].lwp); 472 return (P2T(ret)); 473} 474 475static td_err_e 476pt_thr_dbresume(const td_thrhandle_t *th) 477{ 478 TDBG_FUNC(); 479 480 return pt_dbsuspend(th, 0); 481} 482 483static td_err_e 484pt_thr_dbsuspend(const td_thrhandle_t *th) 485{ 486 TDBG_FUNC(); 487 488 return pt_dbsuspend(th, 1); 489} 490 491static td_err_e 492pt_thr_validate(const td_thrhandle_t *th) 493{ 494 td_thrhandle_t temp; 495 int ret; 496 497 TDBG_FUNC(); 498 499 ret = pt_ta_map_id2thr(th->th_ta, th->th_tid, 500 &temp); 501 return (ret); 502} 503 504static td_err_e 505pt_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info) 506{ 507 const td_thragent_t *ta = th->th_ta; 508 int state; 509 int ret; 510 511 TDBG_FUNC(); 512 513 ret = pt_validate(th); 514 if (ret) 515 return (ret); 516 517 memset(info, 0, sizeof(*info)); 518 if (ta->map[th->th_tid].thr == 0) { 519 info->ti_type = TD_THR_SYSTEM; 520 info->ti_lid = ta->map[th->th_tid].lwp; 521 info->ti_tid = th->th_tid; 522 info->ti_state = TD_THR_RUN; 523 info->ti_type = TD_THR_SYSTEM; 524 info->ti_thread = NULL; 525 return (TD_OK); 526 } 527 ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_state, 528 &state, sizeof(state)); 529 if (ret != 0) 530 return (P2T(ret)); 531 info->ti_lid = ta->map[th->th_tid].lwp; 532 info->ti_tid = th->th_tid; 533 info->ti_thread = ta->map[th->th_tid].thr; 534 info->ti_ta_p = th->th_ta; 535 if (state == ta->thread_state_running) 536 info->ti_state = TD_THR_RUN; 537 else if (state == ta->thread_state_zoombie) 538 info->ti_state = TD_THR_ZOMBIE; 539 else 540 info->ti_state = TD_THR_SLEEP; 541 info->ti_type = TD_THR_USER; 542 return (0); 543} 544 545static td_err_e 546pt_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregs) 547{ 548 const td_thragent_t *ta = th->th_ta; 549 int ret; 550 551 TDBG_FUNC(); 552 553 ret = pt_validate(th); 554 if (ret) 555 return (ret); 556 557 ret = ps_lgetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs); 558 return (P2T(ret)); 559} 560 561static td_err_e 562pt_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs) 563{ 564 const td_thragent_t *ta = th->th_ta; 565 int ret; 566 567 TDBG_FUNC(); 568 569 ret = pt_validate(th); 570 if (ret) 571 return (ret); 572 573 ret = ps_lgetregs(ta->ph, 574 ta->map[th->th_tid].lwp, gregs); 575 return (P2T(ret)); 576} 577 578static td_err_e 579pt_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs) 580{ 581 const td_thragent_t *ta = th->th_ta; 582 int ret; 583 584 TDBG_FUNC(); 585 586 ret = pt_validate(th); 587 if (ret) 588 return (ret); 589 590 ret = ps_lsetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs); 591 return (P2T(ret)); 592} 593 594static td_err_e 595pt_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs) 596{ 597 const td_thragent_t *ta = th->th_ta; 598 int ret; 599 600 TDBG_FUNC(); 601 602 ret = pt_validate(th); 603 if (ret) 604 return (ret); 605 606 ret = ps_lsetregs(ta->ph, ta->map[th->th_tid].lwp, gregs); 607 return (P2T(ret)); 608} 609 610static td_err_e 611pt_thr_event_enable(const td_thrhandle_t *th, int en) 612{ 613 TDBG_FUNC(); 614 return (TD_ERR); 615} 616 617static td_err_e 618pt_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *setp) 619{ 620 TDBG_FUNC(); 621 return (TD_ERR); 622} 623 624static td_err_e 625pt_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *setp) 626{ 627 TDBG_FUNC(); 628 return (TD_ERR); 629} 630 631static td_err_e 632pt_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg) 633{ 634 TDBG_FUNC(); 635 return (TD_NOMSG); 636} 637 638static td_err_e 639pt_thr_sstep(const td_thrhandle_t *th, int step) 640{ 641 const td_thragent_t *ta = th->th_ta; 642 int ret; 643 644 TDBG_FUNC(); 645 646 ret = pt_validate(th); 647 if (ret) 648 return (ret); 649 650 if (ta->map[th->th_tid].thr == 0) 651 return (TD_BADTH); 652 653 return (0); 654} 655 656static int 657pt_validate(const td_thrhandle_t *th) 658{ 659 660 if (th->th_tid <= 0 || th->th_tid >= th->th_ta->map_len || 661 th->th_ta->map[th->th_tid].used == 0) 662 return (TD_NOTHR); 663 return (TD_OK); 664} 665 666static td_err_e 667pt_thr_tls_get_addr(const td_thrhandle_t *th, void *_linkmap, size_t offset, 668 void **address) 669{ 670 char *obj_entry; 671 const td_thragent_t *ta = th->th_ta; 672 psaddr_t tcb_addr, *dtv_addr, tcb_tp; 673 int tls_index, ret; 674 675 /* linkmap is a member of Obj_Entry */ 676 obj_entry = (char *)_linkmap - ta->thread_off_linkmap; 677 678 /* get tlsindex of the object file */ 679 ret = ps_pread(ta->ph, 680 obj_entry + ta->thread_off_tlsindex, 681 &tls_index, sizeof(tls_index)); 682 if (ret != 0) 683 return (P2T(ret)); 684 685 /* get thread tcb */ 686 ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + 687 ta->thread_off_tcb, 688 &tcb_addr, sizeof(tcb_addr)); 689 if (ret != 0) 690 return (P2T(ret)); 691 692 /* get dtv array address */ 693 ret = ps_pread(ta->ph, tcb_addr + ta->thread_off_dtv, 694 &dtv_addr, sizeof(dtv_addr)); 695 if (ret != 0) 696 return (P2T(ret)); 697 /* now get the object's tls block base address */ 698 ret = ps_pread(ta->ph, &dtv_addr[tls_index+1], address, 699 sizeof(*address)); 700 if (ret != 0) 701 return (P2T(ret)); 702 703 *address += offset; 704 return (TD_OK); 705} 706 707struct ta_ops libthr_db_ops = { 708 .to_init = pt_init, 709 .to_ta_clear_event = pt_ta_clear_event, 710 .to_ta_delete = pt_ta_delete, 711 .to_ta_event_addr = pt_ta_event_addr, 712 .to_ta_event_getmsg = pt_ta_event_getmsg, 713 .to_ta_map_id2thr = pt_ta_map_id2thr, 714 .to_ta_map_lwp2thr = pt_ta_map_lwp2thr, 715 .to_ta_new = pt_ta_new, 716 .to_ta_set_event = pt_ta_set_event, 717 .to_ta_thr_iter = pt_ta_thr_iter, 718 .to_ta_tsd_iter = pt_ta_tsd_iter, 719 .to_thr_clear_event = pt_thr_clear_event, 720 .to_thr_dbresume = pt_thr_dbresume, 721 .to_thr_dbsuspend = pt_thr_dbsuspend, 722 .to_thr_event_enable = pt_thr_event_enable, 723 .to_thr_event_getmsg = pt_thr_event_getmsg, 724 .to_thr_get_info = pt_thr_get_info, 725 .to_thr_getfpregs = pt_thr_getfpregs, 726 .to_thr_getgregs = pt_thr_getgregs, 727 .to_thr_set_event = pt_thr_set_event, 728 .to_thr_setfpregs = pt_thr_setfpregs, 729 .to_thr_setgregs = pt_thr_setgregs, 730 .to_thr_validate = pt_thr_validate, 731 .to_thr_tls_get_addr = pt_thr_tls_get_addr, 732 733 /* FreeBSD specific extensions. */ 734 .to_thr_sstep = pt_thr_sstep, 735}; 736