xref: /onnv-gate/usr/src/lib/libtnfprobe/probe_cntl.c (revision 4292:d7beef35913b)
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