libthr_db.c revision 144519
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 144519 2005-04-02 01:36:21Z 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 return (TD_OK); 300} 301 302static td_err_e 303pt_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwp, td_thrhandle_t *th) 304{ 305 TAILQ_HEAD(, pthread) thread_list; 306 psaddr_t pt; 307 long tmp_lwp; 308 int ret; 309 310 TDBG_FUNC(); 311 312 ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list, 313 sizeof(thread_list)); 314 if (ret != 0) 315 return (P2T(ret)); 316 pt = (psaddr_t)thread_list.tqh_first; 317 while (pt != 0) { 318 ret = ps_pread(ta->ph, pt + ta->thread_off_tid, 319 &tmp_lwp, sizeof(tmp_lwp)); 320 if (ret != 0) 321 return (P2T(ret)); 322 if (tmp_lwp == (long)lwp) 323 break; 324 325 /* get next thread */ 326 ret = ps_pread(ta->ph, 327 pt + ta->thread_off_next, 328 &pt, sizeof(pt)); 329 if (ret != 0) 330 return (P2T(ret)); 331 } 332 if (pt == 0) 333 return (TD_NOTHR); 334 th->th_ta = ta; 335 th->th_tid = pt_map_thread(ta, lwp, pt); 336 if (th->th_tid == -1) 337 return (TD_MALLOC); 338 return (TD_OK); 339} 340 341static td_err_e 342pt_ta_thr_iter(const td_thragent_t *ta, 343 td_thr_iter_f *callback, void *cbdata_p, 344 td_thr_state_e state, int ti_pri, 345 sigset_t *ti_sigmask_p, 346 unsigned int ti_user_flags) 347{ 348 TAILQ_HEAD(, pthread) thread_list; 349 td_thrhandle_t th; 350 long tmp_lwp; 351 psaddr_t pt; 352 int ret, isdead; 353 354 TDBG_FUNC(); 355 356 ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list, 357 sizeof(thread_list)); 358 if (ret != 0) 359 return (P2T(ret)); 360 361 pt = (psaddr_t)thread_list.tqh_first; 362 while (pt != 0) { 363 ret = ps_pread(ta->ph, pt + ta->thread_off_isdead, &isdead, 364 sizeof(isdead)); 365 if (ret != 0) 366 return (P2T(ret)); 367 if (!isdead) { 368 ret = ps_pread(ta->ph, pt + ta->thread_off_tid, &tmp_lwp, 369 sizeof(tmp_lwp)); 370 if (ret != 0) 371 return (P2T(ret)); 372 if (tmp_lwp != 0) { 373 th.th_ta = ta; 374 th.th_tid = pt_map_thread(ta, tmp_lwp, pt); 375 if (th.th_tid == -1) 376 return (TD_MALLOC); 377 if ((*callback)(&th, cbdata_p)) 378 return (TD_DBERR); 379 } 380 } 381 /* get next thread */ 382 ret = ps_pread(ta->ph, pt + ta->thread_off_next, &pt, 383 sizeof(pt)); 384 if (ret != 0) 385 return (P2T(ret)); 386 } 387 return (TD_OK); 388} 389 390static td_err_e 391pt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg) 392{ 393 char *keytable; 394 void *destructor; 395 int i, ret, allocated; 396 397 TDBG_FUNC(); 398 399 keytable = malloc(ta->thread_max_keys * ta->thread_size_key); 400 if (keytable == NULL) 401 return (TD_MALLOC); 402 ret = ps_pread(ta->ph, (psaddr_t)ta->thread_keytable_addr, keytable, 403 ta->thread_max_keys * ta->thread_size_key); 404 if (ret != 0) { 405 free(keytable); 406 return (P2T(ret)); 407 } 408 for (i = 0; i < ta->thread_max_keys; i++) { 409 allocated = *(int *)(keytable + i * ta->thread_size_key + 410 ta->thread_off_key_allocated); 411 destructor = *(void **)(keytable + i * ta->thread_size_key + 412 ta->thread_off_key_destructor); 413 if (allocated) { 414 ret = (ki)(i, destructor, arg); 415 if (ret != 0) { 416 free(keytable); 417 return (TD_DBERR); 418 } 419 } 420 } 421 free(keytable); 422 return (TD_OK); 423} 424 425static td_err_e 426pt_ta_event_addr(const td_thragent_t *ta, td_event_e event, td_notify_t *ptr) 427{ 428 TDBG_FUNC(); 429 return (TD_NOEVENT); 430} 431 432static td_err_e 433pt_ta_set_event(const td_thragent_t *ta, td_thr_events_t *events) 434{ 435 TDBG_FUNC(); 436 return (TD_ERR); 437} 438 439static td_err_e 440pt_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *events) 441{ 442 TDBG_FUNC(); 443 return (TD_ERR); 444} 445 446static td_err_e 447pt_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg) 448{ 449 TDBG_FUNC(); 450 return (TD_NOMSG); 451} 452 453static td_err_e 454pt_dbsuspend(const td_thrhandle_t *th, int suspend) 455{ 456 td_thragent_t *ta = (td_thragent_t *)th->th_ta; 457 int ret; 458 459 TDBG_FUNC(); 460 461 ret = pt_validate(th); 462 if (ret) 463 return (ret); 464 465 if (suspend) 466 ret = ps_lstop(ta->ph, ta->map[th->th_tid].lwp); 467 else 468 ret = ps_lcontinue(ta->ph, ta->map[th->th_tid].lwp); 469 return (P2T(ret)); 470} 471 472static td_err_e 473pt_thr_dbresume(const td_thrhandle_t *th) 474{ 475 TDBG_FUNC(); 476 477 return pt_dbsuspend(th, 0); 478} 479 480static td_err_e 481pt_thr_dbsuspend(const td_thrhandle_t *th) 482{ 483 TDBG_FUNC(); 484 485 return pt_dbsuspend(th, 1); 486} 487 488static td_err_e 489pt_thr_validate(const td_thrhandle_t *th) 490{ 491 td_thrhandle_t temp; 492 int ret; 493 494 TDBG_FUNC(); 495 496 ret = pt_ta_map_id2thr(th->th_ta, th->th_tid, 497 &temp); 498 return (ret); 499} 500 501static td_err_e 502pt_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info) 503{ 504 const td_thragent_t *ta = th->th_ta; 505 int state; 506 int ret; 507 508 TDBG_FUNC(); 509 510 ret = pt_validate(th); 511 if (ret) 512 return (ret); 513 514 memset(info, 0, sizeof(*info)); 515 if (ta->map[th->th_tid].thr == 0) { 516 info->ti_type = TD_THR_SYSTEM; 517 info->ti_lid = ta->map[th->th_tid].lwp; 518 info->ti_tid = th->th_tid; 519 info->ti_state = TD_THR_RUN; 520 info->ti_type = TD_THR_SYSTEM; 521 return (TD_OK); 522 } 523 ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_state, 524 &state, sizeof(state)); 525 if (ret != 0) 526 return (P2T(ret)); 527 info->ti_lid = ta->map[th->th_tid].lwp; 528 info->ti_tid = th->th_tid; 529 info->ti_ta_p = th->th_ta; 530 if (state == ta->thread_state_running) 531 info->ti_state = TD_THR_RUN; 532 else if (state == ta->thread_state_zoombie) 533 info->ti_state = TD_THR_ZOMBIE; 534 else 535 info->ti_state = TD_THR_SLEEP; 536 info->ti_type = TD_THR_USER; 537 return (0); 538} 539 540static td_err_e 541pt_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregs) 542{ 543 const td_thragent_t *ta = th->th_ta; 544 int ret; 545 546 TDBG_FUNC(); 547 548 ret = pt_validate(th); 549 if (ret) 550 return (ret); 551 552 ret = ps_lgetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs); 553 return (P2T(ret)); 554} 555 556static td_err_e 557pt_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs) 558{ 559 const td_thragent_t *ta = th->th_ta; 560 int ret; 561 562 TDBG_FUNC(); 563 564 ret = pt_validate(th); 565 if (ret) 566 return (ret); 567 568 ret = ps_lgetregs(ta->ph, 569 ta->map[th->th_tid].lwp, gregs); 570 return (P2T(ret)); 571} 572 573static td_err_e 574pt_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs) 575{ 576 const td_thragent_t *ta = th->th_ta; 577 int ret; 578 579 TDBG_FUNC(); 580 581 ret = pt_validate(th); 582 if (ret) 583 return (ret); 584 585 ret = ps_lsetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs); 586 return (P2T(ret)); 587} 588 589static td_err_e 590pt_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs) 591{ 592 const td_thragent_t *ta = th->th_ta; 593 int ret; 594 595 TDBG_FUNC(); 596 597 ret = pt_validate(th); 598 if (ret) 599 return (ret); 600 601 ret = ps_lsetregs(ta->ph, ta->map[th->th_tid].lwp, gregs); 602 return (P2T(ret)); 603} 604 605static td_err_e 606pt_thr_event_enable(const td_thrhandle_t *th, int en) 607{ 608 TDBG_FUNC(); 609 return (TD_ERR); 610} 611 612static td_err_e 613pt_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *setp) 614{ 615 TDBG_FUNC(); 616 return (TD_ERR); 617} 618 619static td_err_e 620pt_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *setp) 621{ 622 TDBG_FUNC(); 623 return (TD_ERR); 624} 625 626static td_err_e 627pt_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg) 628{ 629 TDBG_FUNC(); 630 return (TD_NOMSG); 631} 632 633static td_err_e 634pt_thr_sstep(const td_thrhandle_t *th, int step) 635{ 636 const td_thragent_t *ta = th->th_ta; 637 int ret; 638 639 TDBG_FUNC(); 640 641 ret = pt_validate(th); 642 if (ret) 643 return (ret); 644 645 if (ta->map[th->th_tid].thr == 0) 646 return (TD_BADTH); 647 648 return (0); 649} 650 651static int 652pt_validate(const td_thrhandle_t *th) 653{ 654 655 if (th->th_tid <= 0 || th->th_tid >= th->th_ta->map_len || 656 th->th_ta->map[th->th_tid].used == 0) 657 return (TD_NOTHR); 658 return (TD_OK); 659} 660 661static td_err_e 662pt_thr_tls_get_addr(const td_thrhandle_t *th, void *_linkmap, size_t offset, 663 void **address) 664{ 665 char *obj_entry; 666 const td_thragent_t *ta = th->th_ta; 667 psaddr_t tcb_addr, *dtv_addr, tcb_tp; 668 int tls_index, ret; 669 670 /* linkmap is a member of Obj_Entry */ 671 obj_entry = (char *)_linkmap - ta->thread_off_linkmap; 672 673 /* get tlsindex of the object file */ 674 ret = ps_pread(ta->ph, 675 obj_entry + ta->thread_off_tlsindex, 676 &tls_index, sizeof(tls_index)); 677 if (ret != 0) 678 return (P2T(ret)); 679 680 /* get thread tcb */ 681 ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + 682 ta->thread_off_tcb, 683 &tcb_addr, sizeof(tcb_addr)); 684 if (ret != 0) 685 return (P2T(ret)); 686 687 /* get dtv array address */ 688 ret = ps_pread(ta->ph, tcb_addr + ta->thread_off_dtv, 689 &dtv_addr, sizeof(dtv_addr)); 690 if (ret != 0) 691 return (P2T(ret)); 692 /* now get the object's tls block base address */ 693 ret = ps_pread(ta->ph, &dtv_addr[tls_index+1], address, 694 sizeof(*address)); 695 if (ret != 0) 696 return (P2T(ret)); 697 698 *address += offset; 699 return (TD_OK); 700} 701 702struct ta_ops libthr_db_ops = { 703 .to_init = pt_init, 704 .to_ta_clear_event = pt_ta_clear_event, 705 .to_ta_delete = pt_ta_delete, 706 .to_ta_event_addr = pt_ta_event_addr, 707 .to_ta_event_getmsg = pt_ta_event_getmsg, 708 .to_ta_map_id2thr = pt_ta_map_id2thr, 709 .to_ta_map_lwp2thr = pt_ta_map_lwp2thr, 710 .to_ta_new = pt_ta_new, 711 .to_ta_set_event = pt_ta_set_event, 712 .to_ta_thr_iter = pt_ta_thr_iter, 713 .to_ta_tsd_iter = pt_ta_tsd_iter, 714 .to_thr_clear_event = pt_thr_clear_event, 715 .to_thr_dbresume = pt_thr_dbresume, 716 .to_thr_dbsuspend = pt_thr_dbsuspend, 717 .to_thr_event_enable = pt_thr_event_enable, 718 .to_thr_event_getmsg = pt_thr_event_getmsg, 719 .to_thr_get_info = pt_thr_get_info, 720 .to_thr_getfpregs = pt_thr_getfpregs, 721 .to_thr_getgregs = pt_thr_getgregs, 722 .to_thr_set_event = pt_thr_set_event, 723 .to_thr_setfpregs = pt_thr_setfpregs, 724 .to_thr_setgregs = pt_thr_setgregs, 725 .to_thr_validate = pt_thr_validate, 726 .to_thr_tls_get_addr = pt_thr_tls_get_addr, 727 728 /* FreeBSD specific extensions. */ 729 .to_thr_sstep = pt_thr_sstep, 730}; 731