8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libtnfprobe / probe_cntl.c
blobb7339f6a363eba3f6aba02a8f83f0e5bc6624a84
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * Includes
33 #ifndef DEBUG
34 #define NDEBUG 1
35 #endif
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 */
56 #include "tnf_trace.h"
59 * Typedefs
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,
67 size_t stksize,
68 start_func_t startfunc,
69 void *arg,
70 long flags,
71 thread_t *newthread);
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);
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);
86 typedef struct args {
87 start_func_t real_func;
88 void *real_arg;
89 } args_t;
92 * Local Declarations
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();
108 * Static Globals
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 */
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;
138 * Externs
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
152 * case.
154 static void
155 probe_setup(void *data)
157 #ifdef DEBUG
158 /* #### - TEMPORARY */
159 fprintf(stderr, "probe_setup: \n");
160 #endif
161 tpd = data;
163 } /* end probe_setup */
166 * __tnf_probe_notify() - libthread calls this function to notify us
167 * that the primordial thread has been created.
170 void
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;
186 #ifdef DEBUG
188 char tmp_buf[512];
189 (void) sprintf(tmp_buf, "__tnf_probe_notify: \n");
190 (void) write(2, tmp_buf, strlen(tmp_buf));
192 #endif
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;
200 assert(test_func);
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.
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;
214 return;
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.
223 void
224 _tnf_fork_thread_setup(void)
226 tnf_ops_t *ops;
228 #ifdef DEBUGFUNCS
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));
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;
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,
256 size_t stksize,
257 void * (*real_func)(void *),
258 void *real_arg,
259 long flags,
260 thread_t *new_thread)
262 static tnf_thr_create_func_t real_thr_create = NULL;
263 args_t *arg_p;
265 #ifdef VERYVERBOSE
266 fprintf(stderr, "hello from the interposed thr_create parent\n");
267 #endif
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));
278 assert(arg_p);
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,
283 flags, new_thread));
285 } /* end thr_create */
289 pthread_create(pthread_t *new_thread_id,
290 const pthread_attr_t *attr,
291 void * (*real_func)(void *),
292 void *real_arg)
294 static tnf_pthread_create_func_t real_pthread_create = NULL;
295 args_t *arg_p;
297 #ifdef VERYVERBOSE
298 fprintf(stderr, "hello from the interposed pthread_create parent\n");
299 #endif
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));
310 assert(arg_p);
311 arg_p->real_func = real_func;
312 arg_p->real_arg = real_arg;
314 return ((*real_pthread_create)(new_thread_id, attr, probestart,
315 (void *) arg_p));
317 } /* end pthread_create */
319 void
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.
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.
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.
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.
370 tnf_thread_disable();
372 ((*real_thr_exit)(status));
375 void
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
397 void
398 _tnf_resume_ret(void *arg1)
400 static void (*real_resume_ret)(void *) = NULL;
401 tnf_ops_t *ops;
403 if (real_resume_ret == NULL) {
404 real_resume_ret = (void (*)(void *)) dlsym(RTLD_NEXT,
405 "_resume_ret");
407 assert(real_resume_ret);
409 ops = tnf_get_ops();
410 if (ops) {
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
438 pid_t
439 _tnf_fork(void)
441 static fork_t real_fork = NULL;
443 if (real_fork == NULL) {
444 real_fork = (fork_t)dlsym(RTLD_NEXT, "fork");
446 assert(real_fork);
447 return (common_fork(real_fork));
450 #pragma weak fork1 = _tnf_fork1
451 pid_t
452 _tnf_fork1(void)
454 static fork_t real_fork = NULL;
456 if (real_fork == NULL) {
457 real_fork = (fork_t)dlsym(RTLD_NEXT, "fork1");
459 assert(real_fork);
460 return (common_fork(real_fork));
463 #ifdef sparc
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;
486 int err;
488 #ifdef VERYVERBOSE
489 fprintf(stderr, "hello from the interposed thr_stksegment\n");
490 #endif
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));
499 if (err == 0) {
500 s->ss_sp = (void *)((caddr_t)s->ss_sp - tnf_probe_dsize);
501 s->ss_size -= tnf_probe_dsize;
503 return (err);
505 #endif /* sparc */
507 /* ---------------------------------------------------------------- */
508 /* ----------------------- Private Functions ---------------------- */
509 /* ---------------------------------------------------------------- */
512 * tnf_probe_getfunc() - default test function if libthread is not
513 * present
515 static tnf_ops_t *
516 tnf_probe_getfunc(void)
518 /* test function to be used if libthread is not linked in */
519 #ifdef DEBUGFUNCS
521 char tmp_buf[512];
522 (void) sprintf(tmp_buf, "tnf_probe_getfunc: \n");
523 (void) write(2, tmp_buf, strlen(tmp_buf));
525 #endif
526 return (tpd);
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
533 * function.
536 static void *
537 probestart(void * arg)
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;
545 #ifdef VERYVERBOSE
546 fprintf(stderr, "hello from the interposed thr_create child\n");
547 #endif
548 #ifdef sparc
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) {
557 jmp_buf tnf_jmpbuf;
558 if (setjmp(tnf_jmpbuf) != 0) {
559 (void) write(2,
560 "probestart: unexpected longjmp\n", 32);
561 assert(0);
563 tnf_probe_dsize = (size_t)(tnf_jmpbuf[3] - tnf_jmpbuf[1]);
565 #endif /* sparc */
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;
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);
579 /* paranoia: thr_probe_setup should be defined */
580 assert(thr_probe_setup != 0);
581 if (thr_probe_setup != 0) thr_probe_setup(&ops);
583 #ifdef VERYVERBOSE
584 fprintf(stderr, "in middle of interposed start procedure\n");
585 #endif
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
614 void
615 tnf_thread_disable(void)
617 tnf_ops_t *ops;
619 if (thr_probe_setup != 0) {
620 /* threaded client */
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);
635 } else {
636 /* non-threaded client */
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;
650 * tnf_thread_enable: API to enable a thread
652 void
653 tnf_thread_enable(void)
655 tnf_ops_t *ops;
657 if (thr_probe_setup != 0) {
658 /* threaded client */
660 ops = pthread_getspecific(tpd_key);
661 if (ops)
662 thr_probe_setup(ops);
663 } else {
664 /* non-threaded client */
666 ops = stashed_tpd;
667 if (ops)
668 probe_setup(ops);
673 * common_fork - code that is common among the interpositions of
674 * fork, fork1, and vfork
676 static pid_t
677 common_fork(fork_t real_fork)
679 pid_t retval;
680 tnf_ops_t *ops;
681 tnf_tag_data_t *metatag_data;
683 #ifdef DEBUGFUNCS
685 char tmp_buf[512];
686 (void) sprintf(tmp_buf, "in interposed fork: \n");
687 (void) write(2, tmp_buf, strlen(tmp_buf));
689 #endif
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
694 * name...
696 ops = tnf_get_ops();
697 if (ops == NULL) {
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);
705 } else {
706 /* non-threaded client */
707 ops = stashed_tpd;
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.
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;
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')) {
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;
748 return (retval);
752 * tnf_threaded_test
754 /*ARGSUSED0*/
755 static void *
756 tnf_threaded_test(void *dummy, tnf_probe_control_t *probe_p,
757 tnf_probe_setup_t *set_p)
759 tnf_ops_t *tpd_p;
761 tpd_p = thr_probe_getfunc_addr();
762 if (tpd_p) {
763 return (probe_p->alloc_func(tpd_p, probe_p, set_p));
765 return (NULL);
770 * tnf_non_threaded_test
772 /*ARGSUSED0*/
773 static void *
774 tnf_non_threaded_test(void *dummy, tnf_probe_control_t *probe_p,
775 tnf_probe_setup_t *set_p)
777 tnf_ops_t *tpd_p;
779 tpd_p = tnf_probe_getfunc();
780 if (tpd_p) {
781 return (probe_p->alloc_func(tpd_p, probe_p, set_p));
783 return (NULL);
787 * tnf_get_ops() returns the ops pointer (thread-private data), or NULL
788 * if tracing is disabled for this thread.
790 static tnf_ops_t *
791 tnf_get_ops()
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)());