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.
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.
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]
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
41 #include <sys/types.h>
43 #include <sys/param.h>
56 #include "tnf_trace.h"
62 typedef tnf_ops_t
*(*tnf_context_t
)(void);
64 typedef void * (*start_func_t
)(void *arg
);
66 typedef int (*tnf_thr_create_func_t
)(void *stk
,
68 start_func_t startfunc
,
73 typedef int (*tnf_pthread_create_func_t
)(pthread_t
*thr
,
74 const pthread_attr_t
*attr
,
75 start_func_t startfunc
,
78 typedef void (*tnf_thr_exit_func_t
)(void *) __NORETURN
;
80 typedef void (*tnf_pthread_exit_func_t
)(void *) __NORETURN
;
82 typedef pid_t (*fork_t
)(void);
84 typedef int (*tnf_thr_stksegment_func_t
)(stack_t
*s
);
87 start_func_t real_func
;
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();
111 extern tnf_ops_t tnf_trace_initial_tpd
;
112 static void *tpd
= &tnf_trace_initial_tpd
;
114 static size_t tnf_probe_dsize
= 0;
118 * Project Private interfaces:
119 * These are interfaces between prex and libtnfw, or
120 * between libtnfw and libtthread.
123 /* variable indicates if libtnfw has sync'ed up with libthread or not */
124 long __tnf_probe_thr_sync
= 0;
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;
130 /* notify function that libthread calls after primordial thread is created */
131 void __tnf_probe_notify(void);
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
;
140 #pragma weak thr_probe_getfunc_addr
141 extern tnf_context_t thr_probe_getfunc_addr
;
143 #pragma weak thr_probe_setup
144 extern void thr_probe_setup(void *);
146 /* ---------------------------------------------------------------- */
147 /* ----------------------- Public Functions ----------------------- */
148 /* ---------------------------------------------------------------- */
151 * probe_setup() - the thread probe setup function for the non-threaded
155 probe_setup(void *data
)
158 /* #### - TEMPORARY */
159 fprintf(stderr
, "probe_setup: \n");
163 } /* end probe_setup */
166 * __tnf_probe_notify() - libthread calls this function to notify us
167 * that the primordial thread has been created.
171 __tnf_probe_notify(void)
173 tnf_probe_control_t
*prbctl_p
;
174 tnf_probe_test_func_t test_func
;
176 /* paranoia: thr_probe_setup should be defined */
177 assert(thr_probe_setup
!= 0);
178 if (thr_probe_setup
!= 0) thr_probe_setup(tpd
);
181 * no race with prex if we set flag first
182 * - this is an idempotent operation
184 __tnf_probe_thr_sync
= 1;
189 (void) sprintf(tmp_buf
, "__tnf_probe_notify: \n");
190 (void) write(2, tmp_buf
, strlen(tmp_buf
));
194 * Use dlsym to test for the present of "thr_probe_getfunc_addr" .
197 test_func
= (((int(*)())dlsym(RTLD_DEFAULT
,
198 "thr_probe_getfunc_addr")) != NULL
) ? tnf_threaded_test
: 0;
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
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
;
216 } /* end __tnf_probe_notify */
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.
224 _tnf_fork_thread_setup(void)
231 (void) sprintf(tmp_buf
, "in _tnf_fork_thread_setup: \n");
232 (void) write(2, tmp_buf
, strlen(tmp_buf
));
239 /* null out tag_index, so that a new one is initialized and written */
240 ops
->schedule
.record_p
= 0;
245 /* ---------------------------------------------------------------- */
246 /* ---------------------- Interposed Functions -------------------- */
247 /* ---------------------------------------------------------------- */
250 * thr_create() - this function is interposed in front of the
251 * actual thread create function in libthread.
255 thr_create(void *stk
,
257 void * (*real_func
)(void *),
260 thread_t
*new_thread
)
262 static tnf_thr_create_func_t real_thr_create
= NULL
;
266 fprintf(stderr
, "hello from the interposed thr_create parent\n");
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");
274 assert(real_thr_create
);
276 /* set up the interposed argument block */
277 arg_p
= (args_t
*)malloc(sizeof (args_t
));
279 arg_p
->real_func
= real_func
;
280 arg_p
->real_arg
= real_arg
;
282 return ((*real_thr_create
)(stk
, stksize
, probestart
, (void *) arg_p
,
285 } /* end thr_create */
289 pthread_create(pthread_t
*new_thread_id
,
290 const pthread_attr_t
*attr
,
291 void * (*real_func
)(void *),
294 static tnf_pthread_create_func_t real_pthread_create
= NULL
;
298 fprintf(stderr
, "hello from the interposed pthread_create parent\n");
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");
306 assert(real_pthread_create
);
308 /* set up the interposed argument block */
309 arg_p
= (args_t
*)malloc(sizeof (args_t
));
311 arg_p
->real_func
= real_func
;
312 arg_p
->real_arg
= real_arg
;
314 return ((*real_pthread_create
)(new_thread_id
, attr
, probestart
,
317 } /* end pthread_create */
320 thr_exit(void * status
)
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");
328 assert(real_thr_exit
);
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.
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.
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
359 * Luckily, when libtnfprobe is added by the tnfctl library, it is
360 * added AFTER an existing contents of the LD_PRELOAD variable.
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
370 tnf_thread_disable();
372 ((*real_thr_exit
)(status
));
376 pthread_exit(void * status
)
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");
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
));
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.
396 #pragma weak _resume_ret = _tnf_resume_ret
398 _tnf_resume_ret(void *arg1
)
400 static void (*real_resume_ret
)(void *) = NULL
;
403 if (real_resume_ret
== NULL
) {
404 real_resume_ret
= (void (*)(void *)) dlsym(RTLD_NEXT
,
407 assert(real_resume_ret
);
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
416 if (ops
->schedule
.lwpid
!= _lwp_self())
417 ops
->schedule
.record_p
= 0;
420 real_resume_ret(arg1
);
424 * Functions to be interposed in front of fork and fork1.
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.
437 #pragma weak fork = _tnf_fork
441 static fork_t real_fork
= NULL
;
443 if (real_fork
== NULL
) {
444 real_fork
= (fork_t
)dlsym(RTLD_NEXT
, "fork");
447 return (common_fork(real_fork
));
450 #pragma weak fork1 = _tnf_fork1
454 static fork_t real_fork
= NULL
;
456 if (real_fork
== NULL
) {
457 real_fork
= (fork_t
)dlsym(RTLD_NEXT
, "fork1");
460 return (common_fork(real_fork
));
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.
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.
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.
481 #pragma weak thr_stksegment = _tnf_thr_stksegment
483 _tnf_thr_stksegment(stack_t
*s
)
485 static tnf_thr_stksegment_func_t real_thr_stksegment
= NULL
;
489 fprintf(stderr
, "hello from the interposed thr_stksegment\n");
492 if (real_thr_stksegment
== NULL
) {
493 real_thr_stksegment
= (tnf_thr_stksegment_func_t
)
494 dlsym(RTLD_NEXT
, "thr_stksegment");
496 assert(real_thr_stksegment
);
498 err
= ((*real_thr_stksegment
)(s
));
500 s
->ss_sp
= (void *)((caddr_t
)s
->ss_sp
- tnf_probe_dsize
);
501 s
->ss_size
-= tnf_probe_dsize
;
507 /* ---------------------------------------------------------------- */
508 /* ----------------------- Private Functions ---------------------- */
509 /* ---------------------------------------------------------------- */
512 * tnf_probe_getfunc() - default test function if libthread is not
516 tnf_probe_getfunc(void)
518 /* test function to be used if libthread is not linked in */
522 (void) sprintf(tmp_buf
, "tnf_probe_getfunc: \n");
523 (void) write(2, tmp_buf
, strlen(tmp_buf
));
527 } /* end tnf_probe_getfunc */
531 * probestart() - this function is called as the start_func by the
532 * interposed thr_create() and pthread_create(). It calls the real start
537 probestart(void * arg
)
539 args_t
*args_p
= (args_t
*)arg
;
540 start_func_t real_func
;
542 tnf_ops_t ops
; /* allocated on stack */
546 fprintf(stderr
, "hello from the interposed thr_create child\n");
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.
556 if (tnf_probe_dsize
== 0) {
558 if (setjmp(tnf_jmpbuf
) != 0) {
560 "probestart: unexpected longjmp\n", 32);
563 tnf_probe_dsize
= (size_t)(tnf_jmpbuf
[3] - tnf_jmpbuf
[1]);
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
;
574 /* copy (and free) the allocated arg block */
575 real_func
= args_p
->real_func
;
576 real_arg
= args_p
->real_arg
;
579 /* paranoia: thr_probe_setup should be defined */
580 assert(thr_probe_setup
!= 0);
581 if (thr_probe_setup
!= 0) thr_probe_setup(&ops
);
584 fprintf(stderr
, "in middle of interposed start procedure\n");
587 real_retval
= (*real_func
)(real_arg
);
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.
596 /* This should be handled by the call to tnf_thread_disable() */
597 /* if (thr_probe_setup != 0) */
598 /* thr_probe_setup(NULL); */
600 /* see the comment in thr_exit about tnf_thread_disable */
601 tnf_thread_disable();
603 return (real_retval
);
605 } /* end probestart */
608 static thread_key_t tpd_key
= THR_ONCE_KEY
;
609 static tnf_ops_t
*stashed_tpd
= NULL
;
612 * tnf_thread_disable: API to disable a thread
615 tnf_thread_disable(void)
619 if (thr_probe_setup
!= 0) {
620 /* threaded client */
622 /* REMIND: destructor function ? */
623 (void) thr_keycreate_once(&tpd_key
, NULL
);
625 ops
= thr_probe_getfunc_addr();
626 /* check ops to ensure function is idempotent */
628 /* unlock currently held blocks */
629 tnfw_b_release_block(&ops
->wcb
);
630 /* disable the thread */
631 thr_probe_setup(NULL
);
633 (void) thr_setspecific(tpd_key
, ops
);
636 /* non-threaded client */
639 ops
= tnf_probe_getfunc();
641 /* disable the process */
650 * tnf_thread_enable: API to enable a thread
653 tnf_thread_enable(void)
657 if (thr_probe_setup
!= 0) {
658 /* threaded client */
660 ops
= pthread_getspecific(tpd_key
);
662 thr_probe_setup(ops
);
664 /* non-threaded client */
673 * common_fork - code that is common among the interpositions of
674 * fork, fork1, and vfork
677 common_fork(fork_t real_fork
)
681 tnf_tag_data_t
*metatag_data
;
686 (void) sprintf(tmp_buf
, "in interposed fork: \n");
687 (void) write(2, tmp_buf
, strlen(tmp_buf
));
690 if ((_tnfw_b_control
->tnf_state
== TNFW_B_NOBUFFER
) &&
691 (tnf_trace_file_name
[0] != '\0')) {
693 * if no buffer has been allocated yet, and prex plugged in
699 * get it from stashed location
700 * don't enable thread though
702 if (thr_probe_setup
!= 0) {
703 /* threaded client */
704 ops
= pthread_getspecific(tpd_key
);
706 /* non-threaded client */
712 * ops shouldn't be NULL. But, if it is, then we don't
713 * initialize tracing. In the child, tracing will be
717 /* initialize tracing */
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
));
727 retval
= real_fork();
730 _tnfw_b_control
->tnf_pid
= getpid();
731 if ((_tnfw_b_control
->tnf_state
== TNFW_B_NOBUFFER
) &&
732 (tnf_trace_file_name
[0] != '\0')) {
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...
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
;
756 tnf_threaded_test(void *dummy
, tnf_probe_control_t
*probe_p
,
757 tnf_probe_setup_t
*set_p
)
761 tpd_p
= thr_probe_getfunc_addr();
763 return (probe_p
->alloc_func(tpd_p
, probe_p
, set_p
));
770 * tnf_non_threaded_test
774 tnf_non_threaded_test(void *dummy
, tnf_probe_control_t
*probe_p
,
775 tnf_probe_setup_t
*set_p
)
779 tpd_p
= tnf_probe_getfunc();
781 return (probe_p
->alloc_func(tpd_p
, probe_p
, set_p
));
787 * tnf_get_ops() returns the ops pointer (thread-private data), or NULL
788 * if tracing is disabled for this thread.
793 tnf_context_t
*test_func_p
= &thr_probe_getfunc_addr
;
794 tnf_context_t test_func
;
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.
804 test_func
= test_func_p
? *test_func_p
: tnf_probe_getfunc
;
805 return ((*test_func
)());