1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Includes 31 */ 32 33 #ifndef DEBUG 34 #define NDEBUG 1 35 #endif 36 37 #include <thread.h> 38 #include <pthread.h> 39 #include <sys/lwp.h> 40 #include <synch.h> 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <sys/param.h> 44 #include <fcntl.h> 45 #include <dlfcn.h> 46 #include <string.h> 47 #include <unistd.h> 48 #include <stdlib.h> 49 #include <assert.h> 50 #include <stdio.h> 51 #include <errno.h> 52 #ifdef sparc 53 #include <setjmp.h> 54 #endif /* sparc */ 55 56 #include "tnf_trace.h" 57 58 /* 59 * Typedefs 60 */ 61 62 typedef tnf_ops_t *(*tnf_context_t)(void); 63 64 typedef void * (*start_func_t)(void *arg); 65 66 typedef int (*tnf_thr_create_func_t)(void *stk, 67 size_t stksize, 68 start_func_t startfunc, 69 void *arg, 70 long flags, 71 thread_t *newthread); 72 73 typedef int (*tnf_pthread_create_func_t)(pthread_t *thr, 74 const pthread_attr_t *attr, 75 start_func_t startfunc, 76 void * arg); 77 78 typedef void (*tnf_thr_exit_func_t)(void *) __NORETURN; 79 80 typedef void (*tnf_pthread_exit_func_t)(void *) __NORETURN; 81 82 typedef pid_t (*fork_t)(void); 83 84 typedef int (*tnf_thr_stksegment_func_t)(stack_t *s); 85 86 typedef struct args { 87 start_func_t real_func; 88 void *real_arg; 89 } args_t; 90 91 /* 92 * Local Declarations 93 */ 94 95 static void * tnf_threaded_test(void *dummy, 96 tnf_probe_control_t *probe_p, 97 tnf_probe_setup_t *set_p); 98 static void * tnf_non_threaded_test(void *dummy, 99 tnf_probe_control_t *probe_p, 100 tnf_probe_setup_t *set_p); 101 static tnf_ops_t *tnf_probe_getfunc(void); 102 static void *probestart(void *arg); 103 static pid_t common_fork(fork_t real_fork); 104 static void probe_setup(void *data); 105 static tnf_ops_t *tnf_get_ops(); 106 107 /* 108 * Static Globals 109 */ 110 111 extern tnf_ops_t tnf_trace_initial_tpd; 112 static void *tpd = &tnf_trace_initial_tpd; 113 #ifdef sparc 114 static size_t tnf_probe_dsize = 0; 115 #endif /* sparc */ 116 117 /* 118 * Project Private interfaces: 119 * These are interfaces between prex and libtnfw, or 120 * between libtnfw and libtthread. 121 */ 122 123 /* variable indicates if libtnfw has sync'ed up with libthread or not */ 124 long __tnf_probe_thr_sync = 0; 125 126 /* head of the list that is used to chain all probes */ 127 tnf_probe_control_t *__tnf_probe_list_head = NULL; 128 int __tnf_probe_list_valid = 0; 129 130 /* notify function that libthread calls after primordial thread is created */ 131 void __tnf_probe_notify(void); 132 133 tnf_probe_test_func_t tnf_threaded_test_addr = tnf_threaded_test; 134 tnf_probe_test_func_t tnf_non_threaded_test_addr = tnf_non_threaded_test; 135 136 137 /* 138 * Externs 139 */ 140 #pragma weak thr_probe_getfunc_addr 141 extern tnf_context_t thr_probe_getfunc_addr; 142 143 #pragma weak thr_probe_setup 144 extern void thr_probe_setup(void *); 145 146 /* ---------------------------------------------------------------- */ 147 /* ----------------------- Public Functions ----------------------- */ 148 /* ---------------------------------------------------------------- */ 149 150 /* 151 * probe_setup() - the thread probe setup function for the non-threaded 152 * case. 153 */ 154 static void 155 probe_setup(void *data) 156 { 157 #ifdef DEBUG 158 /* #### - TEMPORARY */ 159 fprintf(stderr, "probe_setup: \n"); 160 #endif 161 tpd = data; 162 163 } /* end probe_setup */ 164 165 /* 166 * __tnf_probe_notify() - libthread calls this function to notify us 167 * that the primordial thread has been created. 168 */ 169 170 void 171 __tnf_probe_notify(void) 172 { 173 tnf_probe_control_t *prbctl_p; 174 tnf_probe_test_func_t test_func; 175 176 /* paranoia: thr_probe_setup should be defined */ 177 assert(thr_probe_setup != 0); 178 if (thr_probe_setup != 0) thr_probe_setup(tpd); 179 180 /* 181 * no race with prex if we set flag first 182 * - this is an idempotent operation 183 */ 184 __tnf_probe_thr_sync = 1; 185 186 #ifdef DEBUG 187 { 188 char tmp_buf[512]; 189 (void) sprintf(tmp_buf, "__tnf_probe_notify: \n"); 190 (void) write(2, tmp_buf, strlen(tmp_buf)); 191 } 192 #endif 193 /* 194 * Use dlsym to test for the present of "thr_probe_getfunc_addr" . 195 */ 196 197 test_func = (((int(*)())dlsym(RTLD_DEFAULT, 198 "thr_probe_getfunc_addr")) != NULL) ? tnf_threaded_test : 0; 199 200 assert(test_func); 201 202 /* 203 * I think in this case that we do not need to check the 204 * __tnf_probe_list_valid flag since __tnf_probe_notify is 205 * called very early. 206 */ 207 208 /* replace all existing test functions with libthread's test func */ 209 for (prbctl_p = __tnf_probe_list_head; prbctl_p; 210 prbctl_p = prbctl_p->next) 211 if (prbctl_p->test_func) 212 prbctl_p->test_func = test_func; 213 214 return; 215 216 } /* end __tnf_probe_notify */ 217 218 /* 219 * _tnf_fork_thread_setup - function called by buffering layer 220 * whenever it finds a thread in the newly forked process that 221 * hasn't been re-initialized in this process. 222 */ 223 void 224 _tnf_fork_thread_setup(void) 225 { 226 tnf_ops_t *ops; 227 228 #ifdef DEBUGFUNCS 229 { 230 char tmp_buf[512]; 231 (void) sprintf(tmp_buf, "in _tnf_fork_thread_setup: \n"); 232 (void) write(2, tmp_buf, strlen(tmp_buf)); 233 } 234 #endif 235 /* get the tpd */ 236 ops = tnf_get_ops(); 237 if (!ops) 238 return; 239 /* null out tag_index, so that a new one is initialized and written */ 240 ops->schedule.record_p = 0; 241 return; 242 243 } 244 245 /* ---------------------------------------------------------------- */ 246 /* ---------------------- Interposed Functions -------------------- */ 247 /* ---------------------------------------------------------------- */ 248 249 /* 250 * thr_create() - this function is interposed in front of the 251 * actual thread create function in libthread. 252 */ 253 254 int 255 thr_create(void *stk, 256 size_t stksize, 257 void * (*real_func)(void *), 258 void *real_arg, 259 long flags, 260 thread_t *new_thread) 261 { 262 static tnf_thr_create_func_t real_thr_create = NULL; 263 args_t *arg_p; 264 265 #ifdef VERYVERBOSE 266 fprintf(stderr, "hello from the interposed thr_create parent\n"); 267 #endif 268 269 /* use dlsym to find the address of the "real" thr_create function */ 270 if (real_thr_create == NULL) { 271 real_thr_create = (tnf_thr_create_func_t) 272 dlsym(RTLD_NEXT, "thr_create"); 273 } 274 assert(real_thr_create); 275 276 /* set up the interposed argument block */ 277 arg_p = (args_t *)malloc(sizeof (args_t)); 278 assert(arg_p); 279 arg_p->real_func = real_func; 280 arg_p->real_arg = real_arg; 281 282 return ((*real_thr_create)(stk, stksize, probestart, (void *) arg_p, 283 flags, new_thread)); 284 285 } /* end thr_create */ 286 287 288 int 289 pthread_create(pthread_t *new_thread_id, 290 const pthread_attr_t *attr, 291 void * (*real_func)(void *), 292 void *real_arg) 293 { 294 static tnf_pthread_create_func_t real_pthread_create = NULL; 295 args_t *arg_p; 296 297 #ifdef VERYVERBOSE 298 fprintf(stderr, "hello from the interposed pthread_create parent\n"); 299 #endif 300 301 /* use dlsym to find the address of the "real" pthread_create func */ 302 if (real_pthread_create == NULL) { 303 real_pthread_create = (tnf_pthread_create_func_t) 304 dlsym(RTLD_NEXT, "pthread_create"); 305 } 306 assert(real_pthread_create); 307 308 /* set up the interposed argument block */ 309 arg_p = (args_t *)malloc(sizeof (args_t)); 310 assert(arg_p); 311 arg_p->real_func = real_func; 312 arg_p->real_arg = real_arg; 313 314 return ((*real_pthread_create)(new_thread_id, attr, probestart, 315 (void *) arg_p)); 316 317 } /* end pthread_create */ 318 319 void 320 thr_exit(void * status) 321 { 322 static tnf_thr_exit_func_t real_thr_exit = NULL; 323 /* use dlsym to find the address of the "real" pthread_create func */ 324 if (real_thr_exit == NULL) { 325 real_thr_exit = (tnf_thr_exit_func_t) 326 dlsym(RTLD_NEXT, "thr_exit"); 327 } 328 assert(real_thr_exit); 329 330 331 /* 332 * Calling tnf_thread_disable() whenever a thread exits... 333 * This has the side-effect of unlocking our currently 334 * locked block in the trace buffer. This keeps a dying 335 * thread from taking a block with it when it dies, but 336 * it means that we won't be able to trace events from 337 * the thread-specific data destructors. We will lose 338 * out on any events a thread spits out AFTER is calls thr_exit(). 339 * This code was added to fix a bug where tracing breaks when trying 340 * to trace a program with large numbers of thread-ids. 341 * 342 * Addendum: 343 * Now you can't get events for thr_exit using an interposition library. 344 * Since thr_exit is a really helpful event, this is a problem. 345 * Also, breaking this interposition will probably break 346 * BAT, the DevPro TNF perf tool. 347 * 348 * Addendum: 349 * Correction: You can get interposition events if the interposition 350 * library comes BEFORE libtnfprobe.so. But not, if the interp. 351 * library comes AFTER libtnfprobe.so. This is a more difficult 352 * constraint that it might sound like because of the following: 353 * The tnfctl functional interface and the prex command line 354 * interface provide convenience features where you can supply 355 * a character string argument which will be put into LD_PRELOAD 356 * for you. Unfortunately, this string gets appended AFTER 357 * libtnfprobe.so by the tnfctl library(and also hence by the 358 * prex -l option). 359 * Luckily, when libtnfprobe is added by the tnfctl library, it is 360 * added AFTER an existing contents of the LD_PRELOAD variable. 361 * 362 * Therefore, if you are using an interposition library to collect 363 * thr_exit and pthread_exit events, THEN you should NOT use 'prex -l' 364 * or the 'ld_preload' argument to tnfctl_exec_open(), instead, you 365 * should be sure to put the interposition library into the LD_PRELOAD 366 * variable yourself. 367 * 368 */ 369 370 tnf_thread_disable(); 371 372 ((*real_thr_exit)(status)); 373 } 374 375 void 376 pthread_exit(void * status) 377 { 378 static tnf_pthread_exit_func_t real_pthread_exit = NULL; 379 /* use dlsym to find the address of the "real" pthread_create func */ 380 if (real_pthread_exit == NULL) { 381 real_pthread_exit = (tnf_pthread_exit_func_t) 382 dlsym(RTLD_NEXT, "pthread_exit"); 383 } 384 assert(real_pthread_exit); 385 /* see the comment in thr_exit about tnf_thread_disable() */ 386 tnf_thread_disable(); 387 ((*real_pthread_exit)(status)); 388 } 389 390 /* 391 * function to be interposed in front of _resume. We invalidate the 392 * schedule record in case the lwpid changes the next time this 393 * thread is scheduled. 394 */ 395 396 #pragma weak _resume_ret = _tnf_resume_ret 397 void 398 _tnf_resume_ret(void *arg1) 399 { 400 static void (*real_resume_ret)(void *) = NULL; 401 tnf_ops_t *ops; 402 403 if (real_resume_ret == NULL) { 404 real_resume_ret = (void (*)(void *)) dlsym(RTLD_NEXT, 405 "_resume_ret"); 406 } 407 assert(real_resume_ret); 408 409 ops = tnf_get_ops(); 410 if (ops) { 411 /* 412 * invalidate the schedule record. This forces it 413 * to get re-initialized with the new lwpid the next 414 * time this thread gets scheduled 415 */ 416 if (ops->schedule.lwpid != _lwp_self()) 417 ops->schedule.record_p = 0; 418 } 419 420 real_resume_ret(arg1); 421 } 422 423 /* 424 * Functions to be interposed in front of fork and fork1. 425 * 426 * NOTE: we can't handle vfork, because the child would ruin the parent's 427 * data structures. We therefore don't interpose, letting the child's 428 * events appear as though they were the parent's. A slightly cleaner 429 * way to handle vfork would be to interpose on vfork separately to 430 * change the pid and anything else needed to show any events caused 431 * by the child as its events, and then interpose on the exec's as 432 * well to set things back to the way they should be for the parent. 433 * But this is a lot of work, and it makes almost no difference, since the 434 * child typically exec's very quickly after a vfork. 435 */ 436 437 #pragma weak fork = _tnf_fork 438 pid_t 439 _tnf_fork(void) 440 { 441 static fork_t real_fork = NULL; 442 443 if (real_fork == NULL) { 444 real_fork = (fork_t)dlsym(RTLD_NEXT, "fork"); 445 } 446 assert(real_fork); 447 return (common_fork(real_fork)); 448 } 449 450 #pragma weak fork1 = _tnf_fork1 451 pid_t 452 _tnf_fork1(void) 453 { 454 static fork_t real_fork = NULL; 455 456 if (real_fork == NULL) { 457 real_fork = (fork_t)dlsym(RTLD_NEXT, "fork1"); 458 } 459 assert(real_fork); 460 return (common_fork(real_fork)); 461 } 462 463 #ifdef sparc 464 /* 465 * Function to be interposed in front of thr_stksegment 466 * _tnf_thr_stksegment() - used to hide the probestart() allocated data 467 * on the thread stack, ensuring that the caller receives a pointer to the 468 * true bottom (ie, usable) portion of the stack, and the size thereof. 469 * 470 * NOTE: On sparc systems, failure to allow for the presense of tnf data 471 * on the stack would cause TNF probes to fail across doorfs calls. The 472 * i386 version of door_return decides to "skip over some slop", so no 473 * interpose function is required for x86; if the 512 byte 'slop skip' 474 * is ever removed from the i386 door_return, then it will also need 475 * interpose function intervention. 476 */ 477 #pragma weak thr_stksegment = _tnf_thr_stksegment 478 static int 479 _tnf_thr_stksegment(stack_t *s) 480 { 481 static tnf_thr_stksegment_func_t real_thr_stksegment = NULL; 482 int err; 483 484 #ifdef VERYVERBOSE 485 fprintf(stderr, "hello from the interposed thr_stksegment\n"); 486 #endif 487 488 if (real_thr_stksegment == NULL) { 489 real_thr_stksegment = (tnf_thr_stksegment_func_t) 490 dlsym(RTLD_NEXT, "thr_stksegment"); 491 } 492 assert(real_thr_stksegment); 493 494 err = ((*real_thr_stksegment)(s)); 495 if (err == 0) { 496 s->ss_sp = (void *)((caddr_t)s->ss_sp - tnf_probe_dsize); 497 s->ss_size -= tnf_probe_dsize; 498 } 499 return (err); 500 } 501 #endif /* sparc */ 502 503 /* ---------------------------------------------------------------- */ 504 /* ----------------------- Private Functions ---------------------- */ 505 /* ---------------------------------------------------------------- */ 506 507 /* 508 * tnf_probe_getfunc() - default test function if libthread is not 509 * present 510 */ 511 static tnf_ops_t * 512 tnf_probe_getfunc(void) 513 { 514 /* test function to be used if libthread is not linked in */ 515 #ifdef DEBUGFUNCS 516 { 517 char tmp_buf[512]; 518 (void) sprintf(tmp_buf, "tnf_probe_getfunc: \n"); 519 (void) write(2, tmp_buf, strlen(tmp_buf)); 520 } 521 #endif 522 return (tpd); 523 } /* end tnf_probe_getfunc */ 524 525 526 /* 527 * probestart() - this function is called as the start_func by the 528 * interposed thr_create() and pthread_create(). It calls the real start 529 * function. 530 */ 531 532 static void * 533 probestart(void * arg) 534 { 535 args_t *args_p = (args_t *)arg; 536 start_func_t real_func; 537 void *real_arg; 538 tnf_ops_t ops; /* allocated on stack */ 539 void *real_retval; 540 541 #ifdef VERYVERBOSE 542 fprintf(stderr, "hello from the interposed thr_create child\n"); 543 #endif 544 #ifdef sparc 545 /* 546 * if the size of the probe data has not yet been calculated, 547 * initialize a jmpbuffer and calculate the amount of stack space 548 * used by probestart: %fp - %sp from jmp_buf 549 * Not expecting anything to actually longjmp here, so that is 550 * handled as an error condition. 551 */ 552 if (tnf_probe_dsize == 0) { 553 jmp_buf tnf_jmpbuf; 554 if (setjmp(tnf_jmpbuf) != 0) { 555 (void) write(2, 556 "probestart: unexpected longjmp\n", 32); 557 assert(0); 558 } 559 tnf_probe_dsize = (size_t)(tnf_jmpbuf[3] - tnf_jmpbuf[1]); 560 } 561 #endif /* sparc */ 562 563 /* initialize ops */ 564 (void) memset(&ops, 0, sizeof (ops)); /* zero ops */ 565 ops.mode = TNF_ALLOC_REUSABLE; 566 ops.alloc = tnfw_b_alloc; 567 ops.commit = tnfw_b_xcommit; 568 ops.rollback = tnfw_b_xabort; 569 570 /* copy (and free) the allocated arg block */ 571 real_func = args_p->real_func; 572 real_arg = args_p->real_arg; 573 free(args_p); 574 575 /* paranoia: thr_probe_setup should be defined */ 576 assert(thr_probe_setup != 0); 577 if (thr_probe_setup != 0) thr_probe_setup(&ops); 578 579 #ifdef VERYVERBOSE 580 fprintf(stderr, "in middle of interposed start procedure\n"); 581 #endif 582 583 real_retval = (*real_func)(real_arg); 584 585 /* 586 * we need to write a NULL into the tpd pointer to disable 587 * tracing for this thread. 588 * CAUTION: never make this function tail recursive because 589 * tpd is allocated on stack. 590 */ 591 592 /* This should be handled by the call to tnf_thread_disable() */ 593 /* if (thr_probe_setup != 0) */ 594 /* thr_probe_setup(NULL); */ 595 596 /* see the comment in thr_exit about tnf_thread_disable */ 597 tnf_thread_disable(); 598 599 return (real_retval); 600 601 } /* end probestart */ 602 603 604 static thread_key_t tpd_key = THR_ONCE_KEY; 605 static tnf_ops_t *stashed_tpd = NULL; 606 607 /* 608 * tnf_thread_disable: API to disable a thread 609 */ 610 void 611 tnf_thread_disable(void) 612 { 613 tnf_ops_t *ops; 614 615 if (thr_probe_setup != 0) { 616 /* threaded client */ 617 618 /* REMIND: destructor function ? */ 619 (void) thr_keycreate_once(&tpd_key, NULL); 620 /* get the tpd */ 621 ops = thr_probe_getfunc_addr(); 622 /* check ops to ensure function is idempotent */ 623 if (ops != NULL) { 624 /* unlock currently held blocks */ 625 tnfw_b_release_block(&ops->wcb); 626 /* disable the thread */ 627 thr_probe_setup(NULL); 628 /* stash the tpd */ 629 (void) thr_setspecific(tpd_key, ops); 630 } 631 } else { 632 /* non-threaded client */ 633 634 /* get the tpd */ 635 ops = tnf_probe_getfunc(); 636 if (ops != NULL) { 637 /* disable the process */ 638 probe_setup(NULL); 639 /* stash the tpd */ 640 stashed_tpd = ops; 641 } 642 } 643 } 644 645 /* 646 * tnf_thread_enable: API to enable a thread 647 */ 648 void 649 tnf_thread_enable(void) 650 { 651 tnf_ops_t *ops; 652 653 if (thr_probe_setup != 0) { 654 /* threaded client */ 655 656 ops = pthread_getspecific(tpd_key); 657 if (ops) 658 thr_probe_setup(ops); 659 } else { 660 /* non-threaded client */ 661 662 ops = stashed_tpd; 663 if (ops) 664 probe_setup(ops); 665 } 666 } 667 668 /* 669 * common_fork - code that is common among the interpositions of 670 * fork, fork1, and vfork 671 */ 672 static pid_t 673 common_fork(fork_t real_fork) 674 { 675 pid_t retval; 676 tnf_ops_t *ops; 677 tnf_tag_data_t *metatag_data; 678 679 #ifdef DEBUGFUNCS 680 { 681 char tmp_buf[512]; 682 (void) sprintf(tmp_buf, "in interposed fork: \n"); 683 (void) write(2, tmp_buf, strlen(tmp_buf)); 684 } 685 #endif 686 if ((_tnfw_b_control->tnf_state == TNFW_B_NOBUFFER) && 687 (tnf_trace_file_name[0] != '\0')) { 688 /* 689 * if no buffer has been allocated yet, and prex plugged in 690 * name... 691 */ 692 ops = tnf_get_ops(); 693 if (ops == NULL) { 694 /* 695 * get it from stashed location 696 * don't enable thread though 697 */ 698 if (thr_probe_setup != 0) { 699 /* threaded client */ 700 ops = pthread_getspecific(tpd_key); 701 } else { 702 /* non-threaded client */ 703 ops = stashed_tpd; 704 } 705 } 706 707 /* 708 * ops shouldn't be NULL. But, if it is, then we don't 709 * initialize tracing. In the child, tracing will be 710 * set to broken. 711 */ 712 if (ops) { 713 /* initialize tracing */ 714 ops->busy = 1; 715 metatag_data = TAG_DATA(tnf_struct_type); 716 metatag_data->tag_desc(ops, metatag_data); 717 /* commit the data */ 718 (void) ops->commit(&(ops->wcb)); 719 ops->busy = 0; 720 } 721 } 722 723 retval = real_fork(); 724 if (retval == 0) { 725 /* child process */ 726 _tnfw_b_control->tnf_pid = getpid(); 727 if ((_tnfw_b_control->tnf_state == TNFW_B_NOBUFFER) && 728 (tnf_trace_file_name[0] != '\0')) { 729 /* 730 * race condition, prex attached after condition was 731 * checked in parent, so both parent and child point at 732 * the same file name and will overwrite each other. 733 * So, we set tracing to broken in child. We could 734 * invent a new state called RACE and use prex to 735 * reset it, if needed... 736 */ 737 tnf_trace_file_name[0] = '\0'; 738 _tnfw_b_control->tnf_state = TNFW_B_BROKEN; 739 } else if (_tnfw_b_control->tnf_state == TNFW_B_RUNNING) { 740 /* normal expected condition */ 741 _tnfw_b_control->tnf_state = TNFW_B_FORKED; 742 } 743 } 744 return (retval); 745 } 746 747 /* 748 * tnf_threaded_test 749 */ 750 /*ARGSUSED0*/ 751 static void * 752 tnf_threaded_test(void *dummy, tnf_probe_control_t *probe_p, 753 tnf_probe_setup_t *set_p) 754 { 755 tnf_ops_t *tpd_p; 756 757 tpd_p = thr_probe_getfunc_addr(); 758 if (tpd_p) { 759 return (probe_p->alloc_func(tpd_p, probe_p, set_p)); 760 } 761 return (NULL); 762 } 763 764 765 /* 766 * tnf_non_threaded_test 767 */ 768 /*ARGSUSED0*/ 769 static void * 770 tnf_non_threaded_test(void *dummy, tnf_probe_control_t *probe_p, 771 tnf_probe_setup_t *set_p) 772 { 773 tnf_ops_t *tpd_p; 774 775 tpd_p = tnf_probe_getfunc(); 776 if (tpd_p) { 777 return (probe_p->alloc_func(tpd_p, probe_p, set_p)); 778 } 779 return (NULL); 780 } 781 782 /* 783 * tnf_get_ops() returns the ops pointer (thread-private data), or NULL 784 * if tracing is disabled for this thread. 785 */ 786 static tnf_ops_t * 787 tnf_get_ops() 788 { 789 tnf_context_t *test_func_p = &thr_probe_getfunc_addr; 790 tnf_context_t test_func; 791 792 /* 793 * IMPORTANT: this test to see whether thr_probe_getfunc_addr 794 * is bound is tricky. The compiler currently has a bug 795 * (1263684) that causes the test to be optimized away unless 796 * coded with an intermediate pointer (test_func_p). This 797 * causes the process to SEGV when the variable is not bound. 798 */ 799 800 test_func = test_func_p ? *test_func_p : tnf_probe_getfunc; 801 return ((*test_func)()); 802 } 803