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 * Note: Instead of making this function static, we reduce it to local 478 * scope in the mapfile. That allows the linker to prevent it from 479 * appearing in the .SUNW_dynsymsort section. 480 */ 481 #pragma weak thr_stksegment = _tnf_thr_stksegment 482 int 483 _tnf_thr_stksegment(stack_t *s) 484 { 485 static tnf_thr_stksegment_func_t real_thr_stksegment = NULL; 486 int err; 487 488 #ifdef VERYVERBOSE 489 fprintf(stderr, "hello from the interposed thr_stksegment\n"); 490 #endif 491 492 if (real_thr_stksegment == NULL) { 493 real_thr_stksegment = (tnf_thr_stksegment_func_t) 494 dlsym(RTLD_NEXT, "thr_stksegment"); 495 } 496 assert(real_thr_stksegment); 497 498 err = ((*real_thr_stksegment)(s)); 499 if (err == 0) { 500 s->ss_sp = (void *)((caddr_t)s->ss_sp - tnf_probe_dsize); 501 s->ss_size -= tnf_probe_dsize; 502 } 503 return (err); 504 } 505 #endif /* sparc */ 506 507 /* ---------------------------------------------------------------- */ 508 /* ----------------------- Private Functions ---------------------- */ 509 /* ---------------------------------------------------------------- */ 510 511 /* 512 * tnf_probe_getfunc() - default test function if libthread is not 513 * present 514 */ 515 static tnf_ops_t * 516 tnf_probe_getfunc(void) 517 { 518 /* test function to be used if libthread is not linked in */ 519 #ifdef DEBUGFUNCS 520 { 521 char tmp_buf[512]; 522 (void) sprintf(tmp_buf, "tnf_probe_getfunc: \n"); 523 (void) write(2, tmp_buf, strlen(tmp_buf)); 524 } 525 #endif 526 return (tpd); 527 } /* end tnf_probe_getfunc */ 528 529 530 /* 531 * probestart() - this function is called as the start_func by the 532 * interposed thr_create() and pthread_create(). It calls the real start 533 * function. 534 */ 535 536 static void * 537 probestart(void * arg) 538 { 539 args_t *args_p = (args_t *)arg; 540 start_func_t real_func; 541 void *real_arg; 542 tnf_ops_t ops; /* allocated on stack */ 543 void *real_retval; 544 545 #ifdef VERYVERBOSE 546 fprintf(stderr, "hello from the interposed thr_create child\n"); 547 #endif 548 #ifdef sparc 549 /* 550 * if the size of the probe data has not yet been calculated, 551 * initialize a jmpbuffer and calculate the amount of stack space 552 * used by probestart: %fp - %sp from jmp_buf 553 * Not expecting anything to actually longjmp here, so that is 554 * handled as an error condition. 555 */ 556 if (tnf_probe_dsize == 0) { 557 jmp_buf tnf_jmpbuf; 558 if (setjmp(tnf_jmpbuf) != 0) { 559 (void) write(2, 560 "probestart: unexpected longjmp\n", 32); 561 assert(0); 562 } 563 tnf_probe_dsize = (size_t)(tnf_jmpbuf[3] - tnf_jmpbuf[1]); 564 } 565 #endif /* sparc */ 566 567 /* initialize ops */ 568 (void) memset(&ops, 0, sizeof (ops)); /* zero ops */ 569 ops.mode = TNF_ALLOC_REUSABLE; 570 ops.alloc = tnfw_b_alloc; 571 ops.commit = tnfw_b_xcommit; 572 ops.rollback = tnfw_b_xabort; 573 574 /* copy (and free) the allocated arg block */ 575 real_func = args_p->real_func; 576 real_arg = args_p->real_arg; 577 free(args_p); 578 579 /* paranoia: thr_probe_setup should be defined */ 580 assert(thr_probe_setup != 0); 581 if (thr_probe_setup != 0) thr_probe_setup(&ops); 582 583 #ifdef VERYVERBOSE 584 fprintf(stderr, "in middle of interposed start procedure\n"); 585 #endif 586 587 real_retval = (*real_func)(real_arg); 588 589 /* 590 * we need to write a NULL into the tpd pointer to disable 591 * tracing for this thread. 592 * CAUTION: never make this function tail recursive because 593 * tpd is allocated on stack. 594 */ 595 596 /* This should be handled by the call to tnf_thread_disable() */ 597 /* if (thr_probe_setup != 0) */ 598 /* thr_probe_setup(NULL); */ 599 600 /* see the comment in thr_exit about tnf_thread_disable */ 601 tnf_thread_disable(); 602 603 return (real_retval); 604 605 } /* end probestart */ 606 607 608 static thread_key_t tpd_key = THR_ONCE_KEY; 609 static tnf_ops_t *stashed_tpd = NULL; 610 611 /* 612 * tnf_thread_disable: API to disable a thread 613 */ 614 void 615 tnf_thread_disable(void) 616 { 617 tnf_ops_t *ops; 618 619 if (thr_probe_setup != 0) { 620 /* threaded client */ 621 622 /* REMIND: destructor function ? */ 623 (void) thr_keycreate_once(&tpd_key, NULL); 624 /* get the tpd */ 625 ops = thr_probe_getfunc_addr(); 626 /* check ops to ensure function is idempotent */ 627 if (ops != NULL) { 628 /* unlock currently held blocks */ 629 tnfw_b_release_block(&ops->wcb); 630 /* disable the thread */ 631 thr_probe_setup(NULL); 632 /* stash the tpd */ 633 (void) thr_setspecific(tpd_key, ops); 634 } 635 } else { 636 /* non-threaded client */ 637 638 /* get the tpd */ 639 ops = tnf_probe_getfunc(); 640 if (ops != NULL) { 641 /* disable the process */ 642 probe_setup(NULL); 643 /* stash the tpd */ 644 stashed_tpd = ops; 645 } 646 } 647 } 648 649 /* 650 * tnf_thread_enable: API to enable a thread 651 */ 652 void 653 tnf_thread_enable(void) 654 { 655 tnf_ops_t *ops; 656 657 if (thr_probe_setup != 0) { 658 /* threaded client */ 659 660 ops = pthread_getspecific(tpd_key); 661 if (ops) 662 thr_probe_setup(ops); 663 } else { 664 /* non-threaded client */ 665 666 ops = stashed_tpd; 667 if (ops) 668 probe_setup(ops); 669 } 670 } 671 672 /* 673 * common_fork - code that is common among the interpositions of 674 * fork, fork1, and vfork 675 */ 676 static pid_t 677 common_fork(fork_t real_fork) 678 { 679 pid_t retval; 680 tnf_ops_t *ops; 681 tnf_tag_data_t *metatag_data; 682 683 #ifdef DEBUGFUNCS 684 { 685 char tmp_buf[512]; 686 (void) sprintf(tmp_buf, "in interposed fork: \n"); 687 (void) write(2, tmp_buf, strlen(tmp_buf)); 688 } 689 #endif 690 if ((_tnfw_b_control->tnf_state == TNFW_B_NOBUFFER) && 691 (tnf_trace_file_name[0] != '\0')) { 692 /* 693 * if no buffer has been allocated yet, and prex plugged in 694 * name... 695 */ 696 ops = tnf_get_ops(); 697 if (ops == NULL) { 698 /* 699 * get it from stashed location 700 * don't enable thread though 701 */ 702 if (thr_probe_setup != 0) { 703 /* threaded client */ 704 ops = pthread_getspecific(tpd_key); 705 } else { 706 /* non-threaded client */ 707 ops = stashed_tpd; 708 } 709 } 710 711 /* 712 * ops shouldn't be NULL. But, if it is, then we don't 713 * initialize tracing. In the child, tracing will be 714 * set to broken. 715 */ 716 if (ops) { 717 /* initialize tracing */ 718 ops->busy = 1; 719 metatag_data = TAG_DATA(tnf_struct_type); 720 metatag_data->tag_desc(ops, metatag_data); 721 /* commit the data */ 722 (void) ops->commit(&(ops->wcb)); 723 ops->busy = 0; 724 } 725 } 726 727 retval = real_fork(); 728 if (retval == 0) { 729 /* child process */ 730 _tnfw_b_control->tnf_pid = getpid(); 731 if ((_tnfw_b_control->tnf_state == TNFW_B_NOBUFFER) && 732 (tnf_trace_file_name[0] != '\0')) { 733 /* 734 * race condition, prex attached after condition was 735 * checked in parent, so both parent and child point at 736 * the same file name and will overwrite each other. 737 * So, we set tracing to broken in child. We could 738 * invent a new state called RACE and use prex to 739 * reset it, if needed... 740 */ 741 tnf_trace_file_name[0] = '\0'; 742 _tnfw_b_control->tnf_state = TNFW_B_BROKEN; 743 } else if (_tnfw_b_control->tnf_state == TNFW_B_RUNNING) { 744 /* normal expected condition */ 745 _tnfw_b_control->tnf_state = TNFW_B_FORKED; 746 } 747 } 748 return (retval); 749 } 750 751 /* 752 * tnf_threaded_test 753 */ 754 /*ARGSUSED0*/ 755 static void * 756 tnf_threaded_test(void *dummy, tnf_probe_control_t *probe_p, 757 tnf_probe_setup_t *set_p) 758 { 759 tnf_ops_t *tpd_p; 760 761 tpd_p = thr_probe_getfunc_addr(); 762 if (tpd_p) { 763 return (probe_p->alloc_func(tpd_p, probe_p, set_p)); 764 } 765 return (NULL); 766 } 767 768 769 /* 770 * tnf_non_threaded_test 771 */ 772 /*ARGSUSED0*/ 773 static void * 774 tnf_non_threaded_test(void *dummy, tnf_probe_control_t *probe_p, 775 tnf_probe_setup_t *set_p) 776 { 777 tnf_ops_t *tpd_p; 778 779 tpd_p = tnf_probe_getfunc(); 780 if (tpd_p) { 781 return (probe_p->alloc_func(tpd_p, probe_p, set_p)); 782 } 783 return (NULL); 784 } 785 786 /* 787 * tnf_get_ops() returns the ops pointer (thread-private data), or NULL 788 * if tracing is disabled for this thread. 789 */ 790 static tnf_ops_t * 791 tnf_get_ops() 792 { 793 tnf_context_t *test_func_p = &thr_probe_getfunc_addr; 794 tnf_context_t test_func; 795 796 /* 797 * IMPORTANT: this test to see whether thr_probe_getfunc_addr 798 * is bound is tricky. The compiler currently has a bug 799 * (1263684) that causes the test to be optimized away unless 800 * coded with an intermediate pointer (test_func_p). This 801 * causes the process to SEGV when the variable is not bound. 802 */ 803 804 test_func = test_func_p ? *test_func_p : tnf_probe_getfunc; 805 return ((*test_func)()); 806 } 807