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