10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
53864Sraf * Common Development and Distribution License (the "License").
63864Sraf * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
213864Sraf
220Sstevel@tonic-gate /*
233864Sraf * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
240Sstevel@tonic-gate * Use is subject to license terms.
250Sstevel@tonic-gate */
260Sstevel@tonic-gate
270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
280Sstevel@tonic-gate
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate * Includes
310Sstevel@tonic-gate */
320Sstevel@tonic-gate
330Sstevel@tonic-gate #ifndef DEBUG
340Sstevel@tonic-gate #define NDEBUG 1
350Sstevel@tonic-gate #endif
360Sstevel@tonic-gate
370Sstevel@tonic-gate #include <thread.h>
380Sstevel@tonic-gate #include <pthread.h>
390Sstevel@tonic-gate #include <sys/lwp.h>
400Sstevel@tonic-gate #include <synch.h>
410Sstevel@tonic-gate #include <sys/types.h>
420Sstevel@tonic-gate #include <sys/stat.h>
430Sstevel@tonic-gate #include <sys/param.h>
440Sstevel@tonic-gate #include <fcntl.h>
450Sstevel@tonic-gate #include <dlfcn.h>
460Sstevel@tonic-gate #include <string.h>
470Sstevel@tonic-gate #include <unistd.h>
480Sstevel@tonic-gate #include <stdlib.h>
490Sstevel@tonic-gate #include <assert.h>
500Sstevel@tonic-gate #include <stdio.h>
510Sstevel@tonic-gate #include <errno.h>
520Sstevel@tonic-gate #ifdef sparc
530Sstevel@tonic-gate #include <setjmp.h>
540Sstevel@tonic-gate #endif /* sparc */
550Sstevel@tonic-gate
560Sstevel@tonic-gate #include "tnf_trace.h"
570Sstevel@tonic-gate
580Sstevel@tonic-gate /*
590Sstevel@tonic-gate * Typedefs
600Sstevel@tonic-gate */
610Sstevel@tonic-gate
620Sstevel@tonic-gate typedef tnf_ops_t *(*tnf_context_t)(void);
630Sstevel@tonic-gate
640Sstevel@tonic-gate typedef void * (*start_func_t)(void *arg);
650Sstevel@tonic-gate
660Sstevel@tonic-gate typedef int (*tnf_thr_create_func_t)(void *stk,
670Sstevel@tonic-gate size_t stksize,
680Sstevel@tonic-gate start_func_t startfunc,
690Sstevel@tonic-gate void *arg,
700Sstevel@tonic-gate long flags,
710Sstevel@tonic-gate thread_t *newthread);
720Sstevel@tonic-gate
730Sstevel@tonic-gate typedef int (*tnf_pthread_create_func_t)(pthread_t *thr,
740Sstevel@tonic-gate const pthread_attr_t *attr,
750Sstevel@tonic-gate start_func_t startfunc,
760Sstevel@tonic-gate void * arg);
770Sstevel@tonic-gate
780Sstevel@tonic-gate typedef void (*tnf_thr_exit_func_t)(void *) __NORETURN;
790Sstevel@tonic-gate
800Sstevel@tonic-gate typedef void (*tnf_pthread_exit_func_t)(void *) __NORETURN;
810Sstevel@tonic-gate
820Sstevel@tonic-gate typedef pid_t (*fork_t)(void);
830Sstevel@tonic-gate
840Sstevel@tonic-gate typedef int (*tnf_thr_stksegment_func_t)(stack_t *s);
850Sstevel@tonic-gate
860Sstevel@tonic-gate typedef struct args {
870Sstevel@tonic-gate start_func_t real_func;
880Sstevel@tonic-gate void *real_arg;
890Sstevel@tonic-gate } args_t;
900Sstevel@tonic-gate
910Sstevel@tonic-gate /*
920Sstevel@tonic-gate * Local Declarations
930Sstevel@tonic-gate */
940Sstevel@tonic-gate
950Sstevel@tonic-gate static void * tnf_threaded_test(void *dummy,
960Sstevel@tonic-gate tnf_probe_control_t *probe_p,
970Sstevel@tonic-gate tnf_probe_setup_t *set_p);
980Sstevel@tonic-gate static void * tnf_non_threaded_test(void *dummy,
990Sstevel@tonic-gate tnf_probe_control_t *probe_p,
1000Sstevel@tonic-gate tnf_probe_setup_t *set_p);
1010Sstevel@tonic-gate static tnf_ops_t *tnf_probe_getfunc(void);
1020Sstevel@tonic-gate static void *probestart(void *arg);
1030Sstevel@tonic-gate static pid_t common_fork(fork_t real_fork);
1040Sstevel@tonic-gate static void probe_setup(void *data);
1050Sstevel@tonic-gate static tnf_ops_t *tnf_get_ops();
1060Sstevel@tonic-gate
1070Sstevel@tonic-gate /*
1080Sstevel@tonic-gate * Static Globals
1090Sstevel@tonic-gate */
1100Sstevel@tonic-gate
1110Sstevel@tonic-gate extern tnf_ops_t tnf_trace_initial_tpd;
1120Sstevel@tonic-gate static void *tpd = &tnf_trace_initial_tpd;
1130Sstevel@tonic-gate #ifdef sparc
1140Sstevel@tonic-gate static size_t tnf_probe_dsize = 0;
1150Sstevel@tonic-gate #endif /* sparc */
1160Sstevel@tonic-gate
1170Sstevel@tonic-gate /*
1180Sstevel@tonic-gate * Project Private interfaces:
1190Sstevel@tonic-gate * These are interfaces between prex and libtnfw, or
1200Sstevel@tonic-gate * between libtnfw and libtthread.
1210Sstevel@tonic-gate */
1220Sstevel@tonic-gate
1230Sstevel@tonic-gate /* variable indicates if libtnfw has sync'ed up with libthread or not */
1240Sstevel@tonic-gate long __tnf_probe_thr_sync = 0;
1250Sstevel@tonic-gate
1260Sstevel@tonic-gate /* head of the list that is used to chain all probes */
1270Sstevel@tonic-gate tnf_probe_control_t *__tnf_probe_list_head = NULL;
1280Sstevel@tonic-gate int __tnf_probe_list_valid = 0;
1290Sstevel@tonic-gate
1300Sstevel@tonic-gate /* notify function that libthread calls after primordial thread is created */
1310Sstevel@tonic-gate void __tnf_probe_notify(void);
1320Sstevel@tonic-gate
1330Sstevel@tonic-gate tnf_probe_test_func_t tnf_threaded_test_addr = tnf_threaded_test;
1340Sstevel@tonic-gate tnf_probe_test_func_t tnf_non_threaded_test_addr = tnf_non_threaded_test;
1350Sstevel@tonic-gate
1360Sstevel@tonic-gate
1370Sstevel@tonic-gate /*
1380Sstevel@tonic-gate * Externs
1390Sstevel@tonic-gate */
1400Sstevel@tonic-gate #pragma weak thr_probe_getfunc_addr
1410Sstevel@tonic-gate extern tnf_context_t thr_probe_getfunc_addr;
1420Sstevel@tonic-gate
1430Sstevel@tonic-gate #pragma weak thr_probe_setup
1440Sstevel@tonic-gate extern void thr_probe_setup(void *);
1450Sstevel@tonic-gate
1460Sstevel@tonic-gate /* ---------------------------------------------------------------- */
1470Sstevel@tonic-gate /* ----------------------- Public Functions ----------------------- */
1480Sstevel@tonic-gate /* ---------------------------------------------------------------- */
1490Sstevel@tonic-gate
1500Sstevel@tonic-gate /*
1510Sstevel@tonic-gate * probe_setup() - the thread probe setup function for the non-threaded
1520Sstevel@tonic-gate * case.
1530Sstevel@tonic-gate */
1540Sstevel@tonic-gate static void
probe_setup(void * data)1550Sstevel@tonic-gate probe_setup(void *data)
1560Sstevel@tonic-gate {
1570Sstevel@tonic-gate #ifdef DEBUG
1580Sstevel@tonic-gate /* #### - TEMPORARY */
1590Sstevel@tonic-gate fprintf(stderr, "probe_setup: \n");
1600Sstevel@tonic-gate #endif
1610Sstevel@tonic-gate tpd = data;
1620Sstevel@tonic-gate
1630Sstevel@tonic-gate } /* end probe_setup */
1640Sstevel@tonic-gate
1650Sstevel@tonic-gate /*
1660Sstevel@tonic-gate * __tnf_probe_notify() - libthread calls this function to notify us
1670Sstevel@tonic-gate * that the primordial thread has been created.
1680Sstevel@tonic-gate */
1690Sstevel@tonic-gate
1700Sstevel@tonic-gate void
__tnf_probe_notify(void)1710Sstevel@tonic-gate __tnf_probe_notify(void)
1720Sstevel@tonic-gate {
1730Sstevel@tonic-gate tnf_probe_control_t *prbctl_p;
1740Sstevel@tonic-gate tnf_probe_test_func_t test_func;
1750Sstevel@tonic-gate
1760Sstevel@tonic-gate /* paranoia: thr_probe_setup should be defined */
1770Sstevel@tonic-gate assert(thr_probe_setup != 0);
1780Sstevel@tonic-gate if (thr_probe_setup != 0) thr_probe_setup(tpd);
1790Sstevel@tonic-gate
1800Sstevel@tonic-gate /*
1810Sstevel@tonic-gate * no race with prex if we set flag first
1820Sstevel@tonic-gate * - this is an idempotent operation
1830Sstevel@tonic-gate */
1840Sstevel@tonic-gate __tnf_probe_thr_sync = 1;
1850Sstevel@tonic-gate
1860Sstevel@tonic-gate #ifdef DEBUG
1870Sstevel@tonic-gate {
1880Sstevel@tonic-gate char tmp_buf[512];
1890Sstevel@tonic-gate (void) sprintf(tmp_buf, "__tnf_probe_notify: \n");
1900Sstevel@tonic-gate (void) write(2, tmp_buf, strlen(tmp_buf));
1910Sstevel@tonic-gate }
1920Sstevel@tonic-gate #endif
1930Sstevel@tonic-gate /*
1940Sstevel@tonic-gate * Use dlsym to test for the present of "thr_probe_getfunc_addr" .
1950Sstevel@tonic-gate */
1960Sstevel@tonic-gate
1970Sstevel@tonic-gate test_func = (((int(*)())dlsym(RTLD_DEFAULT,
1980Sstevel@tonic-gate "thr_probe_getfunc_addr")) != NULL) ? tnf_threaded_test : 0;
1990Sstevel@tonic-gate
2000Sstevel@tonic-gate assert(test_func);
2010Sstevel@tonic-gate
2020Sstevel@tonic-gate /*
2030Sstevel@tonic-gate * I think in this case that we do not need to check the
2040Sstevel@tonic-gate * __tnf_probe_list_valid flag since __tnf_probe_notify is
2050Sstevel@tonic-gate * called very early.
2060Sstevel@tonic-gate */
2070Sstevel@tonic-gate
2080Sstevel@tonic-gate /* replace all existing test functions with libthread's test func */
2090Sstevel@tonic-gate for (prbctl_p = __tnf_probe_list_head; prbctl_p;
2100Sstevel@tonic-gate prbctl_p = prbctl_p->next)
2110Sstevel@tonic-gate if (prbctl_p->test_func)
2120Sstevel@tonic-gate prbctl_p->test_func = test_func;
2130Sstevel@tonic-gate
2140Sstevel@tonic-gate return;
2150Sstevel@tonic-gate
2160Sstevel@tonic-gate } /* end __tnf_probe_notify */
2170Sstevel@tonic-gate
2180Sstevel@tonic-gate /*
2190Sstevel@tonic-gate * _tnf_fork_thread_setup - function called by buffering layer
2200Sstevel@tonic-gate * whenever it finds a thread in the newly forked process that
2210Sstevel@tonic-gate * hasn't been re-initialized in this process.
2220Sstevel@tonic-gate */
2230Sstevel@tonic-gate void
_tnf_fork_thread_setup(void)2240Sstevel@tonic-gate _tnf_fork_thread_setup(void)
2250Sstevel@tonic-gate {
2260Sstevel@tonic-gate tnf_ops_t *ops;
2270Sstevel@tonic-gate
2280Sstevel@tonic-gate #ifdef DEBUGFUNCS
2290Sstevel@tonic-gate {
2300Sstevel@tonic-gate char tmp_buf[512];
2310Sstevel@tonic-gate (void) sprintf(tmp_buf, "in _tnf_fork_thread_setup: \n");
2320Sstevel@tonic-gate (void) write(2, tmp_buf, strlen(tmp_buf));
2330Sstevel@tonic-gate }
2340Sstevel@tonic-gate #endif
2350Sstevel@tonic-gate /* get the tpd */
2360Sstevel@tonic-gate ops = tnf_get_ops();
2370Sstevel@tonic-gate if (!ops)
2380Sstevel@tonic-gate return;
2390Sstevel@tonic-gate /* null out tag_index, so that a new one is initialized and written */
2400Sstevel@tonic-gate ops->schedule.record_p = 0;
2410Sstevel@tonic-gate return;
2420Sstevel@tonic-gate
2430Sstevel@tonic-gate }
2440Sstevel@tonic-gate
2450Sstevel@tonic-gate /* ---------------------------------------------------------------- */
2460Sstevel@tonic-gate /* ---------------------- Interposed Functions -------------------- */
2470Sstevel@tonic-gate /* ---------------------------------------------------------------- */
2480Sstevel@tonic-gate
2490Sstevel@tonic-gate /*
2500Sstevel@tonic-gate * thr_create() - this function is interposed in front of the
2510Sstevel@tonic-gate * actual thread create function in libthread.
2520Sstevel@tonic-gate */
2530Sstevel@tonic-gate
2540Sstevel@tonic-gate int
thr_create(void * stk,size_t stksize,void * (* real_func)(void *),void * real_arg,long flags,thread_t * new_thread)2550Sstevel@tonic-gate thr_create(void *stk,
2560Sstevel@tonic-gate size_t stksize,
2570Sstevel@tonic-gate void * (*real_func)(void *),
2580Sstevel@tonic-gate void *real_arg,
2590Sstevel@tonic-gate long flags,
2600Sstevel@tonic-gate thread_t *new_thread)
2610Sstevel@tonic-gate {
2620Sstevel@tonic-gate static tnf_thr_create_func_t real_thr_create = NULL;
2630Sstevel@tonic-gate args_t *arg_p;
2640Sstevel@tonic-gate
2650Sstevel@tonic-gate #ifdef VERYVERBOSE
2660Sstevel@tonic-gate fprintf(stderr, "hello from the interposed thr_create parent\n");
2670Sstevel@tonic-gate #endif
2680Sstevel@tonic-gate
2690Sstevel@tonic-gate /* use dlsym to find the address of the "real" thr_create function */
2700Sstevel@tonic-gate if (real_thr_create == NULL) {
2710Sstevel@tonic-gate real_thr_create = (tnf_thr_create_func_t)
2720Sstevel@tonic-gate dlsym(RTLD_NEXT, "thr_create");
2730Sstevel@tonic-gate }
2740Sstevel@tonic-gate assert(real_thr_create);
2750Sstevel@tonic-gate
2760Sstevel@tonic-gate /* set up the interposed argument block */
2770Sstevel@tonic-gate arg_p = (args_t *)malloc(sizeof (args_t));
2780Sstevel@tonic-gate assert(arg_p);
2790Sstevel@tonic-gate arg_p->real_func = real_func;
2800Sstevel@tonic-gate arg_p->real_arg = real_arg;
2810Sstevel@tonic-gate
2820Sstevel@tonic-gate return ((*real_thr_create)(stk, stksize, probestart, (void *) arg_p,
2830Sstevel@tonic-gate flags, new_thread));
2840Sstevel@tonic-gate
2850Sstevel@tonic-gate } /* end thr_create */
2860Sstevel@tonic-gate
2870Sstevel@tonic-gate
2880Sstevel@tonic-gate int
pthread_create(pthread_t * new_thread_id,const pthread_attr_t * attr,void * (* real_func)(void *),void * real_arg)2890Sstevel@tonic-gate pthread_create(pthread_t *new_thread_id,
2900Sstevel@tonic-gate const pthread_attr_t *attr,
2910Sstevel@tonic-gate void * (*real_func)(void *),
2920Sstevel@tonic-gate void *real_arg)
2930Sstevel@tonic-gate {
2940Sstevel@tonic-gate static tnf_pthread_create_func_t real_pthread_create = NULL;
2950Sstevel@tonic-gate args_t *arg_p;
2960Sstevel@tonic-gate
2970Sstevel@tonic-gate #ifdef VERYVERBOSE
2980Sstevel@tonic-gate fprintf(stderr, "hello from the interposed pthread_create parent\n");
2990Sstevel@tonic-gate #endif
3000Sstevel@tonic-gate
3010Sstevel@tonic-gate /* use dlsym to find the address of the "real" pthread_create func */
3020Sstevel@tonic-gate if (real_pthread_create == NULL) {
3030Sstevel@tonic-gate real_pthread_create = (tnf_pthread_create_func_t)
3040Sstevel@tonic-gate dlsym(RTLD_NEXT, "pthread_create");
3050Sstevel@tonic-gate }
3060Sstevel@tonic-gate assert(real_pthread_create);
3070Sstevel@tonic-gate
3080Sstevel@tonic-gate /* set up the interposed argument block */
3090Sstevel@tonic-gate arg_p = (args_t *)malloc(sizeof (args_t));
3100Sstevel@tonic-gate assert(arg_p);
3110Sstevel@tonic-gate arg_p->real_func = real_func;
3120Sstevel@tonic-gate arg_p->real_arg = real_arg;
3130Sstevel@tonic-gate
3140Sstevel@tonic-gate return ((*real_pthread_create)(new_thread_id, attr, probestart,
3150Sstevel@tonic-gate (void *) arg_p));
3160Sstevel@tonic-gate
3170Sstevel@tonic-gate } /* end pthread_create */
3180Sstevel@tonic-gate
3190Sstevel@tonic-gate void
thr_exit(void * status)3200Sstevel@tonic-gate thr_exit(void * status)
3210Sstevel@tonic-gate {
3220Sstevel@tonic-gate static tnf_thr_exit_func_t real_thr_exit = NULL;
3230Sstevel@tonic-gate /* use dlsym to find the address of the "real" pthread_create func */
3240Sstevel@tonic-gate if (real_thr_exit == NULL) {
3250Sstevel@tonic-gate real_thr_exit = (tnf_thr_exit_func_t)
3260Sstevel@tonic-gate dlsym(RTLD_NEXT, "thr_exit");
3270Sstevel@tonic-gate }
3280Sstevel@tonic-gate assert(real_thr_exit);
3290Sstevel@tonic-gate
3300Sstevel@tonic-gate
3310Sstevel@tonic-gate /*
3320Sstevel@tonic-gate * Calling tnf_thread_disable() whenever a thread exits...
3330Sstevel@tonic-gate * This has the side-effect of unlocking our currently
3340Sstevel@tonic-gate * locked block in the trace buffer. This keeps a dying
3350Sstevel@tonic-gate * thread from taking a block with it when it dies, but
3360Sstevel@tonic-gate * it means that we won't be able to trace events from
3370Sstevel@tonic-gate * the thread-specific data destructors. We will lose
3380Sstevel@tonic-gate * out on any events a thread spits out AFTER is calls thr_exit().
3390Sstevel@tonic-gate * This code was added to fix a bug where tracing breaks when trying
3400Sstevel@tonic-gate * to trace a program with large numbers of thread-ids.
3410Sstevel@tonic-gate *
3420Sstevel@tonic-gate * Addendum:
3430Sstevel@tonic-gate * Now you can't get events for thr_exit using an interposition library.
3440Sstevel@tonic-gate * Since thr_exit is a really helpful event, this is a problem.
3450Sstevel@tonic-gate * Also, breaking this interposition will probably break
3460Sstevel@tonic-gate * BAT, the DevPro TNF perf tool.
3470Sstevel@tonic-gate *
3480Sstevel@tonic-gate * Addendum:
3490Sstevel@tonic-gate * Correction: You can get interposition events if the interposition
3500Sstevel@tonic-gate * library comes BEFORE libtnfprobe.so. But not, if the interp.
3510Sstevel@tonic-gate * library comes AFTER libtnfprobe.so. This is a more difficult
3520Sstevel@tonic-gate * constraint that it might sound like because of the following:
3530Sstevel@tonic-gate * The tnfctl functional interface and the prex command line
3540Sstevel@tonic-gate * interface provide convenience features where you can supply
3550Sstevel@tonic-gate * a character string argument which will be put into LD_PRELOAD
3560Sstevel@tonic-gate * for you. Unfortunately, this string gets appended AFTER
3570Sstevel@tonic-gate * libtnfprobe.so by the tnfctl library(and also hence by the
3580Sstevel@tonic-gate * prex -l option).
3590Sstevel@tonic-gate * Luckily, when libtnfprobe is added by the tnfctl library, it is
3600Sstevel@tonic-gate * added AFTER an existing contents of the LD_PRELOAD variable.
3610Sstevel@tonic-gate *
3620Sstevel@tonic-gate * Therefore, if you are using an interposition library to collect
3630Sstevel@tonic-gate * thr_exit and pthread_exit events, THEN you should NOT use 'prex -l'
3640Sstevel@tonic-gate * or the 'ld_preload' argument to tnfctl_exec_open(), instead, you
3650Sstevel@tonic-gate * should be sure to put the interposition library into the LD_PRELOAD
3660Sstevel@tonic-gate * variable yourself.
3670Sstevel@tonic-gate *
3680Sstevel@tonic-gate */
3690Sstevel@tonic-gate
3700Sstevel@tonic-gate tnf_thread_disable();
3710Sstevel@tonic-gate
3720Sstevel@tonic-gate ((*real_thr_exit)(status));
3730Sstevel@tonic-gate }
3740Sstevel@tonic-gate
3750Sstevel@tonic-gate void
pthread_exit(void * status)3760Sstevel@tonic-gate pthread_exit(void * status)
3770Sstevel@tonic-gate {
3780Sstevel@tonic-gate static tnf_pthread_exit_func_t real_pthread_exit = NULL;
3790Sstevel@tonic-gate /* use dlsym to find the address of the "real" pthread_create func */
3800Sstevel@tonic-gate if (real_pthread_exit == NULL) {
3810Sstevel@tonic-gate real_pthread_exit = (tnf_pthread_exit_func_t)
3820Sstevel@tonic-gate dlsym(RTLD_NEXT, "pthread_exit");
3830Sstevel@tonic-gate }
3840Sstevel@tonic-gate assert(real_pthread_exit);
3850Sstevel@tonic-gate /* see the comment in thr_exit about tnf_thread_disable() */
3860Sstevel@tonic-gate tnf_thread_disable();
3870Sstevel@tonic-gate ((*real_pthread_exit)(status));
3880Sstevel@tonic-gate }
3890Sstevel@tonic-gate
3900Sstevel@tonic-gate /*
3910Sstevel@tonic-gate * function to be interposed in front of _resume. We invalidate the
3920Sstevel@tonic-gate * schedule record in case the lwpid changes the next time this
3930Sstevel@tonic-gate * thread is scheduled.
3940Sstevel@tonic-gate */
3950Sstevel@tonic-gate
3960Sstevel@tonic-gate #pragma weak _resume_ret = _tnf_resume_ret
3970Sstevel@tonic-gate void
_tnf_resume_ret(void * arg1)3980Sstevel@tonic-gate _tnf_resume_ret(void *arg1)
3990Sstevel@tonic-gate {
4000Sstevel@tonic-gate static void (*real_resume_ret)(void *) = NULL;
4010Sstevel@tonic-gate tnf_ops_t *ops;
4020Sstevel@tonic-gate
4030Sstevel@tonic-gate if (real_resume_ret == NULL) {
4040Sstevel@tonic-gate real_resume_ret = (void (*)(void *)) dlsym(RTLD_NEXT,
4050Sstevel@tonic-gate "_resume_ret");
4060Sstevel@tonic-gate }
4070Sstevel@tonic-gate assert(real_resume_ret);
4080Sstevel@tonic-gate
4090Sstevel@tonic-gate ops = tnf_get_ops();
4100Sstevel@tonic-gate if (ops) {
4110Sstevel@tonic-gate /*
4120Sstevel@tonic-gate * invalidate the schedule record. This forces it
4130Sstevel@tonic-gate * to get re-initialized with the new lwpid the next
4140Sstevel@tonic-gate * time this thread gets scheduled
4150Sstevel@tonic-gate */
4160Sstevel@tonic-gate if (ops->schedule.lwpid != _lwp_self())
4170Sstevel@tonic-gate ops->schedule.record_p = 0;
4180Sstevel@tonic-gate }
4190Sstevel@tonic-gate
4200Sstevel@tonic-gate real_resume_ret(arg1);
4210Sstevel@tonic-gate }
4220Sstevel@tonic-gate
4230Sstevel@tonic-gate /*
4240Sstevel@tonic-gate * Functions to be interposed in front of fork and fork1.
4250Sstevel@tonic-gate *
4260Sstevel@tonic-gate * NOTE: we can't handle vfork, because the child would ruin the parent's
4270Sstevel@tonic-gate * data structures. We therefore don't interpose, letting the child's
4280Sstevel@tonic-gate * events appear as though they were the parent's. A slightly cleaner
4290Sstevel@tonic-gate * way to handle vfork would be to interpose on vfork separately to
4300Sstevel@tonic-gate * change the pid and anything else needed to show any events caused
4310Sstevel@tonic-gate * by the child as its events, and then interpose on the exec's as
4320Sstevel@tonic-gate * well to set things back to the way they should be for the parent.
4330Sstevel@tonic-gate * But this is a lot of work, and it makes almost no difference, since the
4340Sstevel@tonic-gate * child typically exec's very quickly after a vfork.
4350Sstevel@tonic-gate */
4360Sstevel@tonic-gate
4370Sstevel@tonic-gate #pragma weak fork = _tnf_fork
4380Sstevel@tonic-gate pid_t
_tnf_fork(void)4390Sstevel@tonic-gate _tnf_fork(void)
4400Sstevel@tonic-gate {
4410Sstevel@tonic-gate static fork_t real_fork = NULL;
4420Sstevel@tonic-gate
4430Sstevel@tonic-gate if (real_fork == NULL) {
4440Sstevel@tonic-gate real_fork = (fork_t)dlsym(RTLD_NEXT, "fork");
4450Sstevel@tonic-gate }
4460Sstevel@tonic-gate assert(real_fork);
4470Sstevel@tonic-gate return (common_fork(real_fork));
4480Sstevel@tonic-gate }
4490Sstevel@tonic-gate
4500Sstevel@tonic-gate #pragma weak fork1 = _tnf_fork1
4510Sstevel@tonic-gate pid_t
_tnf_fork1(void)4520Sstevel@tonic-gate _tnf_fork1(void)
4530Sstevel@tonic-gate {
4540Sstevel@tonic-gate static fork_t real_fork = NULL;
4550Sstevel@tonic-gate
4560Sstevel@tonic-gate if (real_fork == NULL) {
4570Sstevel@tonic-gate real_fork = (fork_t)dlsym(RTLD_NEXT, "fork1");
4580Sstevel@tonic-gate }
4590Sstevel@tonic-gate assert(real_fork);
4600Sstevel@tonic-gate return (common_fork(real_fork));
4610Sstevel@tonic-gate }
4620Sstevel@tonic-gate
4630Sstevel@tonic-gate #ifdef sparc
4640Sstevel@tonic-gate /*
4650Sstevel@tonic-gate * Function to be interposed in front of thr_stksegment
4660Sstevel@tonic-gate * _tnf_thr_stksegment() - used to hide the probestart() allocated data
4670Sstevel@tonic-gate * on the thread stack, ensuring that the caller receives a pointer to the
4680Sstevel@tonic-gate * true bottom (ie, usable) portion of the stack, and the size thereof.
4690Sstevel@tonic-gate *
4700Sstevel@tonic-gate * NOTE: On sparc systems, failure to allow for the presense of tnf data
4710Sstevel@tonic-gate * on the stack would cause TNF probes to fail across doorfs calls. The
4720Sstevel@tonic-gate * i386 version of door_return decides to "skip over some slop", so no
4730Sstevel@tonic-gate * interpose function is required for x86; if the 512 byte 'slop skip'
4740Sstevel@tonic-gate * is ever removed from the i386 door_return, then it will also need
4750Sstevel@tonic-gate * interpose function intervention.
476*4292Sab196087 *
477*4292Sab196087 * Note: Instead of making this function static, we reduce it to local
478*4292Sab196087 * scope in the mapfile. That allows the linker to prevent it from
479*4292Sab196087 * appearing in the .SUNW_dynsymsort section.
4800Sstevel@tonic-gate */
4810Sstevel@tonic-gate #pragma weak thr_stksegment = _tnf_thr_stksegment
482*4292Sab196087 int
_tnf_thr_stksegment(stack_t * s)4830Sstevel@tonic-gate _tnf_thr_stksegment(stack_t *s)
4840Sstevel@tonic-gate {
4850Sstevel@tonic-gate static tnf_thr_stksegment_func_t real_thr_stksegment = NULL;
4860Sstevel@tonic-gate int err;
4870Sstevel@tonic-gate
4880Sstevel@tonic-gate #ifdef VERYVERBOSE
4890Sstevel@tonic-gate fprintf(stderr, "hello from the interposed thr_stksegment\n");
4900Sstevel@tonic-gate #endif
4910Sstevel@tonic-gate
4920Sstevel@tonic-gate if (real_thr_stksegment == NULL) {
4930Sstevel@tonic-gate real_thr_stksegment = (tnf_thr_stksegment_func_t)
4940Sstevel@tonic-gate dlsym(RTLD_NEXT, "thr_stksegment");
4950Sstevel@tonic-gate }
4960Sstevel@tonic-gate assert(real_thr_stksegment);
4970Sstevel@tonic-gate
4980Sstevel@tonic-gate err = ((*real_thr_stksegment)(s));
4990Sstevel@tonic-gate if (err == 0) {
5000Sstevel@tonic-gate s->ss_sp = (void *)((caddr_t)s->ss_sp - tnf_probe_dsize);
5010Sstevel@tonic-gate s->ss_size -= tnf_probe_dsize;
5020Sstevel@tonic-gate }
5030Sstevel@tonic-gate return (err);
5040Sstevel@tonic-gate }
5050Sstevel@tonic-gate #endif /* sparc */
5060Sstevel@tonic-gate
5070Sstevel@tonic-gate /* ---------------------------------------------------------------- */
5080Sstevel@tonic-gate /* ----------------------- Private Functions ---------------------- */
5090Sstevel@tonic-gate /* ---------------------------------------------------------------- */
5100Sstevel@tonic-gate
5110Sstevel@tonic-gate /*
5120Sstevel@tonic-gate * tnf_probe_getfunc() - default test function if libthread is not
5130Sstevel@tonic-gate * present
5140Sstevel@tonic-gate */
5150Sstevel@tonic-gate static tnf_ops_t *
tnf_probe_getfunc(void)5160Sstevel@tonic-gate tnf_probe_getfunc(void)
5170Sstevel@tonic-gate {
5180Sstevel@tonic-gate /* test function to be used if libthread is not linked in */
5190Sstevel@tonic-gate #ifdef DEBUGFUNCS
5200Sstevel@tonic-gate {
5210Sstevel@tonic-gate char tmp_buf[512];
5220Sstevel@tonic-gate (void) sprintf(tmp_buf, "tnf_probe_getfunc: \n");
5230Sstevel@tonic-gate (void) write(2, tmp_buf, strlen(tmp_buf));
5240Sstevel@tonic-gate }
5250Sstevel@tonic-gate #endif
5260Sstevel@tonic-gate return (tpd);
5270Sstevel@tonic-gate } /* end tnf_probe_getfunc */
5280Sstevel@tonic-gate
5290Sstevel@tonic-gate
5300Sstevel@tonic-gate /*
5310Sstevel@tonic-gate * probestart() - this function is called as the start_func by the
5320Sstevel@tonic-gate * interposed thr_create() and pthread_create(). It calls the real start
5330Sstevel@tonic-gate * function.
5340Sstevel@tonic-gate */
5350Sstevel@tonic-gate
5360Sstevel@tonic-gate static void *
probestart(void * arg)5370Sstevel@tonic-gate probestart(void * arg)
5380Sstevel@tonic-gate {
5390Sstevel@tonic-gate args_t *args_p = (args_t *)arg;
5400Sstevel@tonic-gate start_func_t real_func;
5410Sstevel@tonic-gate void *real_arg;
5420Sstevel@tonic-gate tnf_ops_t ops; /* allocated on stack */
5430Sstevel@tonic-gate void *real_retval;
5440Sstevel@tonic-gate
5450Sstevel@tonic-gate #ifdef VERYVERBOSE
5460Sstevel@tonic-gate fprintf(stderr, "hello from the interposed thr_create child\n");
5470Sstevel@tonic-gate #endif
5480Sstevel@tonic-gate #ifdef sparc
5490Sstevel@tonic-gate /*
5500Sstevel@tonic-gate * if the size of the probe data has not yet been calculated,
5510Sstevel@tonic-gate * initialize a jmpbuffer and calculate the amount of stack space
5520Sstevel@tonic-gate * used by probestart: %fp - %sp from jmp_buf
5530Sstevel@tonic-gate * Not expecting anything to actually longjmp here, so that is
5540Sstevel@tonic-gate * handled as an error condition.
5550Sstevel@tonic-gate */
5560Sstevel@tonic-gate if (tnf_probe_dsize == 0) {
5570Sstevel@tonic-gate jmp_buf tnf_jmpbuf;
5580Sstevel@tonic-gate if (setjmp(tnf_jmpbuf) != 0) {
5590Sstevel@tonic-gate (void) write(2,
5600Sstevel@tonic-gate "probestart: unexpected longjmp\n", 32);
5610Sstevel@tonic-gate assert(0);
5620Sstevel@tonic-gate }
5630Sstevel@tonic-gate tnf_probe_dsize = (size_t)(tnf_jmpbuf[3] - tnf_jmpbuf[1]);
5640Sstevel@tonic-gate }
5650Sstevel@tonic-gate #endif /* sparc */
5660Sstevel@tonic-gate
5670Sstevel@tonic-gate /* initialize ops */
5680Sstevel@tonic-gate (void) memset(&ops, 0, sizeof (ops)); /* zero ops */
5690Sstevel@tonic-gate ops.mode = TNF_ALLOC_REUSABLE;
5700Sstevel@tonic-gate ops.alloc = tnfw_b_alloc;
5710Sstevel@tonic-gate ops.commit = tnfw_b_xcommit;
5720Sstevel@tonic-gate ops.rollback = tnfw_b_xabort;
5730Sstevel@tonic-gate
5740Sstevel@tonic-gate /* copy (and free) the allocated arg block */
5750Sstevel@tonic-gate real_func = args_p->real_func;
5760Sstevel@tonic-gate real_arg = args_p->real_arg;
5770Sstevel@tonic-gate free(args_p);
5780Sstevel@tonic-gate
5790Sstevel@tonic-gate /* paranoia: thr_probe_setup should be defined */
5800Sstevel@tonic-gate assert(thr_probe_setup != 0);
5810Sstevel@tonic-gate if (thr_probe_setup != 0) thr_probe_setup(&ops);
5820Sstevel@tonic-gate
5830Sstevel@tonic-gate #ifdef VERYVERBOSE
5840Sstevel@tonic-gate fprintf(stderr, "in middle of interposed start procedure\n");
5850Sstevel@tonic-gate #endif
5860Sstevel@tonic-gate
5870Sstevel@tonic-gate real_retval = (*real_func)(real_arg);
5880Sstevel@tonic-gate
5890Sstevel@tonic-gate /*
5900Sstevel@tonic-gate * we need to write a NULL into the tpd pointer to disable
5910Sstevel@tonic-gate * tracing for this thread.
5920Sstevel@tonic-gate * CAUTION: never make this function tail recursive because
5930Sstevel@tonic-gate * tpd is allocated on stack.
5940Sstevel@tonic-gate */
5950Sstevel@tonic-gate
5960Sstevel@tonic-gate /* This should be handled by the call to tnf_thread_disable() */
5970Sstevel@tonic-gate /* if (thr_probe_setup != 0) */
5980Sstevel@tonic-gate /* thr_probe_setup(NULL); */
5990Sstevel@tonic-gate
6000Sstevel@tonic-gate /* see the comment in thr_exit about tnf_thread_disable */
6010Sstevel@tonic-gate tnf_thread_disable();
6020Sstevel@tonic-gate
6030Sstevel@tonic-gate return (real_retval);
6040Sstevel@tonic-gate
6050Sstevel@tonic-gate } /* end probestart */
6060Sstevel@tonic-gate
6070Sstevel@tonic-gate
6083864Sraf static thread_key_t tpd_key = THR_ONCE_KEY;
6090Sstevel@tonic-gate static tnf_ops_t *stashed_tpd = NULL;
6100Sstevel@tonic-gate
6110Sstevel@tonic-gate /*
6120Sstevel@tonic-gate * tnf_thread_disable: API to disable a thread
6130Sstevel@tonic-gate */
6140Sstevel@tonic-gate void
tnf_thread_disable(void)6150Sstevel@tonic-gate tnf_thread_disable(void)
6160Sstevel@tonic-gate {
6170Sstevel@tonic-gate tnf_ops_t *ops;
6180Sstevel@tonic-gate
6190Sstevel@tonic-gate if (thr_probe_setup != 0) {
6200Sstevel@tonic-gate /* threaded client */
6210Sstevel@tonic-gate
6223864Sraf /* REMIND: destructor function ? */
6233864Sraf (void) thr_keycreate_once(&tpd_key, NULL);
6240Sstevel@tonic-gate /* get the tpd */
6250Sstevel@tonic-gate ops = thr_probe_getfunc_addr();
6260Sstevel@tonic-gate /* check ops to ensure function is idempotent */
6270Sstevel@tonic-gate if (ops != NULL) {
6280Sstevel@tonic-gate /* unlock currently held blocks */
6290Sstevel@tonic-gate tnfw_b_release_block(&ops->wcb);
6300Sstevel@tonic-gate /* disable the thread */
6310Sstevel@tonic-gate thr_probe_setup(NULL);
6320Sstevel@tonic-gate /* stash the tpd */
6330Sstevel@tonic-gate (void) thr_setspecific(tpd_key, ops);
6340Sstevel@tonic-gate }
6350Sstevel@tonic-gate } else {
6360Sstevel@tonic-gate /* non-threaded client */
6370Sstevel@tonic-gate
6380Sstevel@tonic-gate /* get the tpd */
6390Sstevel@tonic-gate ops = tnf_probe_getfunc();
6400Sstevel@tonic-gate if (ops != NULL) {
6410Sstevel@tonic-gate /* disable the process */
6420Sstevel@tonic-gate probe_setup(NULL);
6430Sstevel@tonic-gate /* stash the tpd */
6440Sstevel@tonic-gate stashed_tpd = ops;
6450Sstevel@tonic-gate }
6460Sstevel@tonic-gate }
6470Sstevel@tonic-gate }
6480Sstevel@tonic-gate
6490Sstevel@tonic-gate /*
6500Sstevel@tonic-gate * tnf_thread_enable: API to enable a thread
6510Sstevel@tonic-gate */
6520Sstevel@tonic-gate void
tnf_thread_enable(void)6530Sstevel@tonic-gate tnf_thread_enable(void)
6540Sstevel@tonic-gate {
6550Sstevel@tonic-gate tnf_ops_t *ops;
6560Sstevel@tonic-gate
6570Sstevel@tonic-gate if (thr_probe_setup != 0) {
6580Sstevel@tonic-gate /* threaded client */
6590Sstevel@tonic-gate
6603864Sraf ops = pthread_getspecific(tpd_key);
6613864Sraf if (ops)
6623864Sraf thr_probe_setup(ops);
6630Sstevel@tonic-gate } else {
6640Sstevel@tonic-gate /* non-threaded client */
6650Sstevel@tonic-gate
6660Sstevel@tonic-gate ops = stashed_tpd;
6673864Sraf if (ops)
6680Sstevel@tonic-gate probe_setup(ops);
6690Sstevel@tonic-gate }
6700Sstevel@tonic-gate }
6710Sstevel@tonic-gate
6720Sstevel@tonic-gate /*
6730Sstevel@tonic-gate * common_fork - code that is common among the interpositions of
6740Sstevel@tonic-gate * fork, fork1, and vfork
6750Sstevel@tonic-gate */
6760Sstevel@tonic-gate static pid_t
common_fork(fork_t real_fork)6770Sstevel@tonic-gate common_fork(fork_t real_fork)
6780Sstevel@tonic-gate {
6790Sstevel@tonic-gate pid_t retval;
6800Sstevel@tonic-gate tnf_ops_t *ops;
6810Sstevel@tonic-gate tnf_tag_data_t *metatag_data;
6820Sstevel@tonic-gate
6830Sstevel@tonic-gate #ifdef DEBUGFUNCS
6840Sstevel@tonic-gate {
6850Sstevel@tonic-gate char tmp_buf[512];
6860Sstevel@tonic-gate (void) sprintf(tmp_buf, "in interposed fork: \n");
6870Sstevel@tonic-gate (void) write(2, tmp_buf, strlen(tmp_buf));
6880Sstevel@tonic-gate }
6890Sstevel@tonic-gate #endif
6900Sstevel@tonic-gate if ((_tnfw_b_control->tnf_state == TNFW_B_NOBUFFER) &&
6910Sstevel@tonic-gate (tnf_trace_file_name[0] != '\0')) {
6920Sstevel@tonic-gate /*
6930Sstevel@tonic-gate * if no buffer has been allocated yet, and prex plugged in
6940Sstevel@tonic-gate * name...
6950Sstevel@tonic-gate */
6960Sstevel@tonic-gate ops = tnf_get_ops();
6970Sstevel@tonic-gate if (ops == NULL) {
6980Sstevel@tonic-gate /*
6990Sstevel@tonic-gate * get it from stashed location
7000Sstevel@tonic-gate * don't enable thread though
7010Sstevel@tonic-gate */
7020Sstevel@tonic-gate if (thr_probe_setup != 0) {
7030Sstevel@tonic-gate /* threaded client */
7043864Sraf ops = pthread_getspecific(tpd_key);
7050Sstevel@tonic-gate } else {
7060Sstevel@tonic-gate /* non-threaded client */
7070Sstevel@tonic-gate ops = stashed_tpd;
7080Sstevel@tonic-gate }
7090Sstevel@tonic-gate }
7100Sstevel@tonic-gate
7110Sstevel@tonic-gate /*
7120Sstevel@tonic-gate * ops shouldn't be NULL. But, if it is, then we don't
7130Sstevel@tonic-gate * initialize tracing. In the child, tracing will be
7140Sstevel@tonic-gate * set to broken.
7150Sstevel@tonic-gate */
7160Sstevel@tonic-gate if (ops) {
7170Sstevel@tonic-gate /* initialize tracing */
7180Sstevel@tonic-gate ops->busy = 1;
7190Sstevel@tonic-gate metatag_data = TAG_DATA(tnf_struct_type);
7200Sstevel@tonic-gate metatag_data->tag_desc(ops, metatag_data);
7210Sstevel@tonic-gate /* commit the data */
7220Sstevel@tonic-gate (void) ops->commit(&(ops->wcb));
7230Sstevel@tonic-gate ops->busy = 0;
7240Sstevel@tonic-gate }
7250Sstevel@tonic-gate }
7260Sstevel@tonic-gate
7270Sstevel@tonic-gate retval = real_fork();
7280Sstevel@tonic-gate if (retval == 0) {
7290Sstevel@tonic-gate /* child process */
7300Sstevel@tonic-gate _tnfw_b_control->tnf_pid = getpid();
7310Sstevel@tonic-gate if ((_tnfw_b_control->tnf_state == TNFW_B_NOBUFFER) &&
7320Sstevel@tonic-gate (tnf_trace_file_name[0] != '\0')) {
7330Sstevel@tonic-gate /*
7340Sstevel@tonic-gate * race condition, prex attached after condition was
7350Sstevel@tonic-gate * checked in parent, so both parent and child point at
7360Sstevel@tonic-gate * the same file name and will overwrite each other.
7370Sstevel@tonic-gate * So, we set tracing to broken in child. We could
7380Sstevel@tonic-gate * invent a new state called RACE and use prex to
7390Sstevel@tonic-gate * reset it, if needed...
7400Sstevel@tonic-gate */
7410Sstevel@tonic-gate tnf_trace_file_name[0] = '\0';
7420Sstevel@tonic-gate _tnfw_b_control->tnf_state = TNFW_B_BROKEN;
7430Sstevel@tonic-gate } else if (_tnfw_b_control->tnf_state == TNFW_B_RUNNING) {
7440Sstevel@tonic-gate /* normal expected condition */
7450Sstevel@tonic-gate _tnfw_b_control->tnf_state = TNFW_B_FORKED;
7460Sstevel@tonic-gate }
7470Sstevel@tonic-gate }
7480Sstevel@tonic-gate return (retval);
7490Sstevel@tonic-gate }
7500Sstevel@tonic-gate
7510Sstevel@tonic-gate /*
7520Sstevel@tonic-gate * tnf_threaded_test
7530Sstevel@tonic-gate */
7540Sstevel@tonic-gate /*ARGSUSED0*/
7550Sstevel@tonic-gate static void *
tnf_threaded_test(void * dummy,tnf_probe_control_t * probe_p,tnf_probe_setup_t * set_p)7560Sstevel@tonic-gate tnf_threaded_test(void *dummy, tnf_probe_control_t *probe_p,
7570Sstevel@tonic-gate tnf_probe_setup_t *set_p)
7580Sstevel@tonic-gate {
7590Sstevel@tonic-gate tnf_ops_t *tpd_p;
7600Sstevel@tonic-gate
7610Sstevel@tonic-gate tpd_p = thr_probe_getfunc_addr();
7620Sstevel@tonic-gate if (tpd_p) {
7630Sstevel@tonic-gate return (probe_p->alloc_func(tpd_p, probe_p, set_p));
7640Sstevel@tonic-gate }
7650Sstevel@tonic-gate return (NULL);
7660Sstevel@tonic-gate }
7670Sstevel@tonic-gate
7680Sstevel@tonic-gate
7690Sstevel@tonic-gate /*
7700Sstevel@tonic-gate * tnf_non_threaded_test
7710Sstevel@tonic-gate */
7720Sstevel@tonic-gate /*ARGSUSED0*/
7730Sstevel@tonic-gate static void *
tnf_non_threaded_test(void * dummy,tnf_probe_control_t * probe_p,tnf_probe_setup_t * set_p)7740Sstevel@tonic-gate tnf_non_threaded_test(void *dummy, tnf_probe_control_t *probe_p,
7750Sstevel@tonic-gate tnf_probe_setup_t *set_p)
7760Sstevel@tonic-gate {
7770Sstevel@tonic-gate tnf_ops_t *tpd_p;
7780Sstevel@tonic-gate
7790Sstevel@tonic-gate tpd_p = tnf_probe_getfunc();
7800Sstevel@tonic-gate if (tpd_p) {
7810Sstevel@tonic-gate return (probe_p->alloc_func(tpd_p, probe_p, set_p));
7820Sstevel@tonic-gate }
7830Sstevel@tonic-gate return (NULL);
7840Sstevel@tonic-gate }
7850Sstevel@tonic-gate
7860Sstevel@tonic-gate /*
7870Sstevel@tonic-gate * tnf_get_ops() returns the ops pointer (thread-private data), or NULL
7880Sstevel@tonic-gate * if tracing is disabled for this thread.
7890Sstevel@tonic-gate */
7900Sstevel@tonic-gate static tnf_ops_t *
tnf_get_ops()7910Sstevel@tonic-gate tnf_get_ops()
7920Sstevel@tonic-gate {
7930Sstevel@tonic-gate tnf_context_t *test_func_p = &thr_probe_getfunc_addr;
7940Sstevel@tonic-gate tnf_context_t test_func;
7950Sstevel@tonic-gate
7960Sstevel@tonic-gate /*
7970Sstevel@tonic-gate * IMPORTANT: this test to see whether thr_probe_getfunc_addr
7980Sstevel@tonic-gate * is bound is tricky. The compiler currently has a bug
7990Sstevel@tonic-gate * (1263684) that causes the test to be optimized away unless
8000Sstevel@tonic-gate * coded with an intermediate pointer (test_func_p). This
8010Sstevel@tonic-gate * causes the process to SEGV when the variable is not bound.
8020Sstevel@tonic-gate */
8030Sstevel@tonic-gate
8040Sstevel@tonic-gate test_func = test_func_p ? *test_func_p : tnf_probe_getfunc;
8050Sstevel@tonic-gate return ((*test_func)());
8060Sstevel@tonic-gate }
807