| /* Copyright (C) 2021 Free Software Foundation, Inc. |
| Contributed by Oracle. |
| |
| This file is part of GNU Binutils. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| /* |
| * Central SIGPROF dispatcher to various module event handlers |
| * (REALPROF profile, HWC check, overview sample, manual sample) |
| */ |
| |
| #include "config.h" |
| #include <dlfcn.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/param.h> |
| #include <sys/syscall.h> |
| #include <time.h> |
| #include <signal.h> |
| |
| #include "gp-defs.h" |
| #include "gp-experiment.h" |
| #include "collector.h" |
| #include "collector_module.h" |
| #include "tsd.h" |
| #include "hwcdrv.h" |
| |
| |
| /* TprintfT(<level>,...) definitions. Adjust per module as needed */ |
| #define DBG_LT0 0 // for high-level configuration, unexpected errors/warnings |
| #define DBG_LTT 0 // for interposition on GLIBC functions |
| #define DBG_LT1 1 // for configuration details, warnings |
| #define DBG_LT2 2 |
| #define DBG_LT3 3 |
| |
| static void collector_sigprof_dispatcher (int, siginfo_t*, void*); |
| static int init_interposition_intf (); |
| #include "memmgr.h" |
| static int collector_timer_create (timer_t * ptimerid); |
| static int collector_timer_settime (int period, timer_t timerid); |
| static int collector_timer_gettime (timer_t timerid); |
| static volatile int collector_sigprof_entries = 0; /* counter for SIGPROF signals in DISPATCH_TST mode */ |
| static timer_t collector_master_thread_timerid = NULL; |
| static collector_mutex_t collector_clone_libc_lock = COLLECTOR_MUTEX_INITIALIZER; |
| static unsigned dispatcher_key = COLLECTOR_TSD_INVALID_KEY; |
| |
| static void *__real_clone = NULL; |
| static void *__real_timer_create = NULL; |
| static void *__real_timer_settime = NULL; |
| static void *__real_timer_delete = NULL; |
| static void *__real_timer_gettime = NULL; |
| #if ARCH(Intel) && WSIZE(32) |
| static void *__real_pthread_create_2_1 = NULL; |
| static void *__real_pthread_create_2_0 = NULL; |
| #elif ARCH(Intel) && WSIZE(64) |
| static void *__real_timer_create_2_3_3 = NULL; |
| static void *__real_timer_create_2_2_5 = NULL; |
| #elif ARCH(SPARC) && WSIZE(64) |
| static void *__real_timer_create_2_3_3 = NULL; |
| static void *__real_timer_create_2_2 = NULL; |
| #endif |
| |
| /* Original SIGPROF handler which will be replaced with the dispatcher. Used |
| * to properly interact with libaio, which uses SIGPROF as its SIGAIOCANCEL. */ |
| static struct sigaction original_sigprof_handler; |
| |
| enum |
| { |
| DISPATCH_NYI = -1, /* dispatcher not yet installed */ |
| DISPATCH_OFF = 0, /* dispatcher installed, but disabled */ |
| DISPATCH_ON = 1, /* dispatcher installed, and enabled */ |
| DISPATCH_TST = 2 /* dispatcher installed, and enabled in testing mode */ |
| }; |
| |
| static int dispatch_mode = DISPATCH_NYI; /* controls SIGPROF dispatching */ |
| static int itimer_period_requested = 0; /* dispatcher itimer period */ |
| static int itimer_period_actual = 0; /* actual dispatcher itimer period */ |
| |
| #define CALL_REAL(x) (*(int(*)())__real_##x) |
| #define NULL_PTR(x) ( __real_##x == NULL ) |
| |
| static void *__real_sigaction = NULL; |
| static void *__real_setitimer = NULL; |
| static void *__real_libc_setitimer = NULL; |
| static void *__real_sigprocmask = NULL; |
| static void *__real_thr_sigsetmask = NULL; |
| static void *__real_pthread_sigmask = NULL; |
| static void *__real_pthread_create = NULL; |
| |
| /* |
| * void collector_sigprof_dispatcher() |
| * |
| * Common SIGPROF event handler which dispatches events to appropriate |
| * module handlers, if they are active for this collection and due. |
| * Dispatch sequence, logic and handlers currently hardcoded in dispatcher. |
| */ |
| static void |
| collector_sigprof_dispatcher (int sig, siginfo_t *info, void *context) |
| { |
| if (info == NULL || (info->si_code <= 0 && info->si_code != SI_TIMER)) |
| { |
| TprintfT (DBG_LT2, "collector_sigprof_dispatcher signal for %p\n", |
| original_sigprof_handler.sa_handler); |
| /* pass signal to previous handler */ |
| /* watch for recursion, SIG_IGN, and SIG_DFL */ |
| if (original_sigprof_handler.sa_handler == SIG_DFL) |
| __collector_SIGDFL_handler (SIGPROF); |
| else if (original_sigprof_handler.sa_handler != SIG_IGN && |
| original_sigprof_handler.sa_sigaction != &collector_sigprof_dispatcher) |
| { |
| (original_sigprof_handler.sa_sigaction)(sig, info, context); |
| TprintfT (DBG_LT2, "collector_sigprof_dispatcher handled\n"); |
| } |
| } |
| else if (dispatch_mode == DISPATCH_ON) |
| { |
| #if ARCH(SPARC) |
| ucontext_t uctxmem; |
| ucontext_t *uctx = &uctxmem; |
| uctx->uc_link = NULL; |
| /* 23340823 signal handler third argument should point to a ucontext_t */ |
| /* Convert sigcontext to ucontext_t on sparc-Linux */ |
| struct sigcontext *sctx = (struct sigcontext*) context; |
| #if WSIZE(32) |
| uctx->uc_mcontext.gregs[REG_PC] = sctx->si_regs.pc; |
| __collector_memcpy (&uctx->uc_mcontext.gregs[3], |
| sctx->si_regs.u_regs, |
| sizeof (sctx->si_regs.u_regs)); |
| #else |
| uctx->uc_mcontext.mc_gregs[MC_PC] = sctx->sigc_regs.tpc; |
| __collector_memcpy (&uctx->uc_mcontext.mc_gregs[3], |
| sctx->sigc_regs.u_regs, |
| sizeof (sctx->sigc_regs.u_regs)); |
| #endif /* WSIZE() */ |
| |
| #else /* not sparc-Linux */ |
| ucontext_t *uctx = (ucontext_t*) context; |
| #endif /* ARCH() */ |
| TprintfT (DBG_LT3, "collector_sigprof_dispatcher dispatching signal\n"); |
| |
| /* XXXX the order of these checks/activities may need adjustment */ |
| /* XXXX should also check (first) for a "cached" manual sample */ |
| /* HWC check for each LWP: required even if collection is paused */ |
| /* This should be first, otherwise it's likely to find the counters |
| * stopped due to an event/overflow during some of the other activities. |
| */ |
| /* XXXX HWC check performed every time (skipping if HWC profiling inactive) |
| * to avoid complexity of maintaining separate check times for each LWP |
| */ |
| __collector_ext_hwc_check (info, uctx); |
| |
| /* XXXX if sigemtpending, should perhaps skip __collector_ext_usage_sample |
| * (and get it next time through) |
| */ |
| |
| /* check for experiment past delay start */ |
| if (__collector_delay_start != 0) |
| { |
| hrtime_t now = __collector_gethrtime (); |
| if (__collector_delay_start < now) |
| { |
| TprintfT (0, "__collector_ext_usage_sample: now (%lld) > delay_start (%lld)\n", |
| (now - __collector_start_time), (__collector_delay_start - __collector_start_time)); |
| |
| /* resume the data collection */ |
| __collector_delay_start = 0; |
| __collector_resume (); |
| |
| /* don't take a periodic sample, just let the resume sample cover it */ |
| if (__collector_sample_period != 0) |
| { |
| /* this update should only be done for periodic samples */ |
| while (__collector_next_sample < now) |
| __collector_next_sample += ((hrtime_t) NANOSEC) * __collector_sample_period; |
| } |
| /* return; */ |
| } |
| } |
| /* check for periodic sampling */ |
| if (__collector_gethrtime () > __collector_next_sample) |
| __collector_ext_usage_sample (PERIOD_SMPL, "periodic"); |
| |
| /* check for experiment past termination time */ |
| if (__collector_exp_active && __collector_terminate_time != 0) |
| { |
| hrtime_t now = __collector_gethrtime (); |
| if (__collector_terminate_time < now) |
| { |
| TprintfT (0, "__collector_ext_usage_sample: now (%lld) > terminate_time (%lld); closing experiment\n", |
| (now - __collector_start_time), (__collector_terminate_time - __collector_start_time)); |
| /* close the experiment */ |
| __collector_close_experiment (); |
| } |
| } |
| |
| /* call the code to process the profile data, and generate the packet */ |
| /* (must always be called, otherwise profile data must be aggregated, |
| * but can be left till last, as already have the required data) |
| */ |
| __collector_ext_profile_handler (info, uctx); |
| } |
| else if (dispatch_mode == DISPATCH_TST) |
| { |
| collector_sigprof_entries++; |
| return; |
| } |
| } |
| |
| /* |
| * __collector_sigprof_install |
| */ |
| int |
| __collector_sigprof_install () |
| { |
| TprintfT (DBG_LT2, "__collector_sigprof_install\n"); |
| struct sigaction oact; |
| if (__collector_sigaction (SIGPROF, NULL, &oact) != 0) |
| return COL_ERROR_DISPINIT; |
| if (oact.sa_sigaction == collector_sigprof_dispatcher) |
| /* signal handler is already in place; we are probably in a fork-child */ |
| TprintfT (DBG_LT1, "dispatcher: __collector_ext_dispatcher_install() collector_sigprof_dispatcher already installed\n"); |
| else |
| { |
| struct sigaction c_act; |
| CALL_UTIL (memset)(&c_act, 0, sizeof c_act); |
| sigemptyset (&c_act.sa_mask); |
| sigaddset (&c_act.sa_mask, HWCFUNCS_SIGNAL); /* block SIGEMT delivery in handler */ |
| c_act.sa_sigaction = collector_sigprof_dispatcher; |
| c_act.sa_flags = SA_RESTART | SA_SIGINFO; |
| if (__collector_sigaction (SIGPROF, &c_act, &original_sigprof_handler)) |
| return COL_ERROR_DISPINIT; |
| } |
| dispatch_mode = DISPATCH_OFF; /* don't dispatch yet */ |
| TprintfT (DBG_LT2, "__collector_sigprof_install done\n"); |
| return COL_ERROR_NONE; |
| } |
| |
| /* |
| * void __collector_ext_dispatcher_tsd_create_key() |
| * |
| * create tsd key for dispatcher |
| */ |
| void |
| __collector_ext_dispatcher_tsd_create_key () |
| { |
| dispatcher_key = __collector_tsd_create_key (sizeof (timer_t), NULL, NULL); |
| } |
| /* |
| * int __collector_ext_dispatcher_install() |
| * |
| * installs a common handler/dispatcher (and itimer) for SIGPROF events |
| */ |
| int |
| __collector_ext_dispatcher_install () |
| { |
| int timer_period; |
| TprintfT (DBG_LT2, "__collector_ext_dispatcher_install\n"); |
| |
| /* check period set for interval timer, which will be used as the basis |
| * for all timed activities: if not set, no role for SIGPROF dispatcher |
| */ |
| if (itimer_period_requested <= 0) |
| { |
| TprintfT (DBG_LT1, "No interval timer set: skipping dispatcher install!\n"); |
| return COL_ERROR_NONE; /* no itimer/dispatcher required */ |
| } |
| |
| /* check for an existing interval timer */ |
| if (collector_master_thread_timerid == NULL) |
| if (collector_timer_create (&collector_master_thread_timerid) < 0) |
| return COL_ERROR_ITMRINIT; |
| timer_t *timeridptr = __collector_tsd_get_by_key (dispatcher_key); |
| if (timeridptr != NULL) |
| *timeridptr = collector_master_thread_timerid; // store for per thread timer stop/start |
| TprintfT (DBG_LT3, "__collector_ext_dispatcher_install: collector_master_thread_timerid=%p\n", |
| collector_master_thread_timerid); |
| timer_period = collector_timer_gettime (collector_master_thread_timerid); |
| if (timer_period > 0) |
| { |
| TprintfT (DBG_LT1, "Overriding app-set interval timer with period %d\n", timer_period); |
| (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%d->%d</event>\n", |
| SP_JCMD_CWARN, COL_WARN_ITMRPOVR, timer_period, itimer_period_requested); |
| } |
| /* install the interval timer used for all timed activities */ |
| if (collector_timer_settime (itimer_period_requested, collector_master_thread_timerid) < 0) |
| return COL_ERROR_ITMRINIT; |
| TprintfT (DBG_LT2, "__collector_ext_dispatcher_install done\n"); |
| dispatch_mode = DISPATCH_ON; /* activate SIGPROF dispatch to event handlers */ |
| return COL_ERROR_NONE; |
| } |
| |
| int |
| __collector_sigaction (int sig, const struct sigaction *nact, struct sigaction *oact) |
| { |
| TprintfT (DBG_LT1, "__collector_sigaction: %d, %p\n", sig, nact ? nact->sa_sigaction : NULL); |
| if (NULL_PTR (sigaction)) |
| init_interposition_intf (); |
| |
| /* Whether we change the signal handler in the kernel |
| * or not make sure the real sigaction is aware about |
| * our new handler (6227565) |
| */ |
| return CALL_REAL (sigaction)(sig, nact, oact); |
| } |
| |
| /* |
| * We have special dispatchers for SIGPROF and HWCFUNCS_SIGNAL to |
| * decide whether the signal was intended for us or for the user. |
| * One special case is SIGDFL, in which case we don't have a |
| * user-function address to call. If the user did indeed set |
| * default disposition for one of these signals and sent that |
| * signal, we honor that action, even though it will lead to |
| * termination. |
| */ |
| void |
| __collector_SIGDFL_handler (int sig) |
| { |
| /* remove our dispatcher, replacing it with the default disposition */ |
| struct sigaction act; |
| CALL_UTIL (memset)(&act, 0, sizeof (act)); |
| act.sa_handler = SIG_DFL; |
| if (__collector_sigaction (sig, &act, NULL)) |
| { |
| /* XXXXXX what are we supposed to do here? we're committing suicide anyhow */ |
| } |
| /* resend the signal we intercepted earlier */ |
| // XXXX Bug 18177509 - additional sigprof signal kills target program |
| kill (getpid (), sig); |
| } |
| |
| /* |
| * suspend/resume timer per thread |
| */ |
| void |
| __collector_ext_dispatcher_thread_timer_suspend () |
| { |
| timer_t * timeridptr = __collector_tsd_get_by_key (dispatcher_key); |
| if (timeridptr != NULL && *timeridptr != NULL) |
| (void) collector_timer_settime (0, *timeridptr); |
| return; |
| } |
| |
| int |
| __collector_ext_dispatcher_thread_timer_resume () |
| { |
| timer_t * timeridptr = __collector_tsd_get_by_key (dispatcher_key); |
| if (timeridptr == NULL) |
| return -1; |
| if (*timeridptr == NULL) |
| { // timer id not initialized yet |
| TprintfT (DBG_LT2, "__collector_ext_dispatcher_thread_timer_resume: timer not initialized yet, create it\n"); |
| if (collector_timer_create (timeridptr) == -1) |
| { |
| TprintfT (0, "__collector_ext_dispatcher_thread_timer_resume(): WARNING: No timer created\n"); |
| return -1; |
| } |
| } |
| return collector_timer_settime (itimer_period_requested, *timeridptr); |
| } |
| |
| void |
| __collector_ext_dispatcher_suspend () |
| { |
| TprintfT (DBG_LT2, "__collector_ext_dispatcher_suspend\n"); |
| if (dispatch_mode == DISPATCH_NYI) |
| { |
| TprintfT (0, "__collector_ext_dispatcher_suspend(): WARNING: No dispatcher installed\n"); |
| return; |
| } |
| |
| /* disable SIGPROF dispatching */ |
| dispatch_mode = DISPATCH_OFF; |
| |
| /* disable the interval timer; ignore any failures */ |
| __collector_ext_dispatcher_thread_timer_suspend (); |
| return; |
| } |
| |
| void |
| __collector_ext_dispatcher_restart () |
| { |
| TprintfT (DBG_LT2, "__collector_ext_dispatcher_restart(ip=%d)\n", itimer_period_requested); |
| if (dispatch_mode == DISPATCH_NYI) |
| { |
| TprintfT (0, "__collector_ext_dispatcher_restart(): WARNING: No dispatcher installed\n"); |
| return; |
| } |
| |
| /* restart the interval timer used for all timed activities */ |
| if (__collector_ext_dispatcher_thread_timer_resume () == 0) |
| dispatch_mode = DISPATCH_ON; /* re-activate SIGPROF dispatch to handlers */ |
| return; |
| } |
| /* |
| * void __collector_ext_dispatcher_deinstall() |
| * |
| * If installed, disables SIGPROF dispatch and interval timer. |
| * Includes checks for last SIGPROF dispatch time, interval timer period, |
| * and currently installed SIGPROF handler, with appropriate warnings logged. |
| * The dispatcher remains installed to handle pending collector SIGPROFs and |
| * forward non-collector SIGPROFs to the application's handler(s). |
| * If the decision is ever made actually to deinstall the dispatcher, |
| * consider bug 4183714 and what to do about any possible pending |
| * SIGPROFs. |
| */ |
| |
| void |
| __collector_ext_dispatcher_deinstall () |
| { |
| TprintfT (DBG_LT1, "__collector_ext_dispatcher_deinstall()\n"); |
| if (dispatch_mode == DISPATCH_NYI) |
| { |
| TprintfT (0, "__collector_ext_dispatcher_deinstall(): WARNING: No dispatcher installed\n"); |
| return; |
| } |
| dispatch_mode = DISPATCH_OFF; /* disable SIGPROF dispatching */ |
| |
| /* verify that interval timer is still installed with expected period */ |
| int timer_period = collector_timer_gettime (collector_master_thread_timerid); |
| if (timer_period != itimer_period_actual) |
| { |
| TprintfT (DBG_LT2, "dispatcher: Collector interval timer period changed %d -> %d\n", |
| itimer_period_actual, timer_period); |
| if ((itimer_period_actual >= (timer_period + timer_period / 10)) || |
| (itimer_period_actual <= (timer_period - timer_period / 10))) |
| __collector_log_write ("<event kind=\"%s\" id=\"%d\">%d -> %d</event>\n", |
| SP_JCMD_CWARN, COL_WARN_ITMRREP, |
| itimer_period_actual, timer_period); |
| else |
| __collector_log_write ("<event kind=\"%s\" id=\"%d\">%d -> %d</event>\n", |
| SP_JCMD_COMMENT, COL_WARN_PROFRND, |
| itimer_period_actual, timer_period); |
| } |
| |
| /* Verify that SIGPROF dispatcher is still installed. |
| * (still required with sigaction interposition and management, |
| * since interposition is not done for attach experiments) |
| */ |
| struct sigaction curr; |
| if (__collector_sigaction (SIGPROF, NULL, &curr) == -1) |
| TprintfT (0, "ERROR: dispatcher sigaction check failed: errno=%d\n", errno); |
| else if (curr.sa_sigaction != collector_sigprof_dispatcher) |
| { |
| TprintfT (0, "ERROR: collector dispatcher replaced by %p!\n", curr.sa_handler); |
| (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%p</event>\n", |
| SP_JCMD_CWARN, COL_WARN_SIGPROF, curr.sa_handler); |
| } |
| else |
| TprintfT (DBG_LT2, "collector dispatcher integrity verified!\n"); |
| |
| /* disable the interval timer; ignore any failures */ |
| if (collector_master_thread_timerid != NULL) |
| { |
| (void) CALL_REAL (timer_delete)(collector_master_thread_timerid); |
| collector_master_thread_timerid = NULL; |
| } |
| dispatcher_key = COLLECTOR_TSD_INVALID_KEY; |
| itimer_period_requested = 0; |
| itimer_period_actual = 0; |
| } |
| |
| /* |
| * void __collector_ext_dispatcher_fork_child_cleanup() |
| * |
| * delete timer, clear timer interval |
| */ |
| void |
| __collector_ext_dispatcher_fork_child_cleanup () |
| { |
| if (collector_master_thread_timerid != NULL) |
| { |
| (void) CALL_REAL (timer_delete)(collector_master_thread_timerid); |
| collector_master_thread_timerid = NULL; |
| } |
| __collector_mutex_init (&collector_clone_libc_lock); |
| dispatcher_key = COLLECTOR_TSD_INVALID_KEY; |
| itimer_period_requested = 0; |
| itimer_period_actual = 0; |
| } |
| /* |
| * int __collector_ext_itimer_set (int rperiod) |
| * |
| * set itimer period, if not yet set to a positive number of microseconds, |
| * (after rounding to sys_resolution if necessary) and return its value |
| */ |
| int |
| __collector_ext_itimer_set (int rperiod) |
| { |
| int period; |
| /* if rperiod is negative, force setting */ |
| if (rperiod < 0) |
| { |
| itimer_period_actual = 0; |
| period = -rperiod; |
| } |
| else |
| period = rperiod; |
| |
| // ignore SIGPROF while testing itimer interval setting |
| int saved = dispatch_mode; |
| dispatch_mode = DISPATCH_OFF; |
| if (collector_timer_create (&collector_master_thread_timerid) == -1) |
| { |
| TprintfT (0, "__collector_ext_itimer_set(): WARNING: No timer created\n"); |
| return itimer_period_actual; |
| } |
| if (collector_timer_settime (period, collector_master_thread_timerid) == 0) |
| { |
| itimer_period_actual = collector_timer_gettime (collector_master_thread_timerid); |
| (void) collector_timer_settime (0, collector_master_thread_timerid); /* XXXX unset for now */ |
| itimer_period_requested = period; |
| if (itimer_period_requested != itimer_period_actual) |
| { |
| TprintfT (DBG_LT2, " itimer period %d adjusted to %d\n", |
| itimer_period_requested, itimer_period_actual); |
| // (void) __collector_log_write("<event kind=\"%s\" id=\"%d\">%d -> %d</event>\n", |
| // SP_JCMD_CWARN, COL_WARN_PROFRND, itimer_period_requested, itimer_period_actual); |
| } |
| else |
| TprintfT (DBG_LT2, " itimer period %d accepted\n", period); |
| } |
| |
| // restore dispatching SIGPROF handler |
| dispatch_mode = saved; |
| TprintfT (0, "__collector_ext_itimer_set(%d), requested=%d, actual=%d)\n", |
| rperiod, itimer_period_requested, itimer_period_actual); |
| return (itimer_period_actual); |
| } |
| |
| static int |
| collector_timer_gettime (timer_t timerid) |
| { |
| int timer_period; |
| struct itimerspec itimer; |
| if (timerid == NULL) |
| return (0); // timer was not initialized |
| if (CALL_REAL (timer_gettime)(timerid, &itimer) == -1) |
| { |
| /* this should never reasonably fail, so not worth logging */ |
| TprintfT (DBG_LT1, "WARNING: timer_gettime failed: errno=%d\n", errno); |
| return (-1); |
| } |
| timer_period = ((itimer.it_interval.tv_sec * NANOSEC) + |
| itimer.it_interval.tv_nsec) / 1000; |
| TprintfT (DBG_LT2, "collector_timer_gettime (period=%d)\n", timer_period); |
| return (timer_period); |
| } |
| |
| static int |
| collector_timer_create (timer_t * ptimerid) |
| { |
| struct sigevent sigev; |
| if (NULL_PTR (timer_create)) |
| init_interposition_intf (); |
| TprintfT (DBG_LT2, "collector_timer_settime(): timer_create is %p\n", __real_timer_create); |
| sigev.sigev_notify = SIGEV_THREAD_ID | SIGEV_SIGNAL; |
| sigev.sigev_signo = SIGPROF; |
| sigev.sigev_value.sival_ptr = ptimerid; |
| #if !defined(__MUSL_LIBC) |
| sigev._sigev_un._tid = __collector_gettid (); |
| #endif |
| if (CALL_REAL (timer_create)(CLOCK_THREAD_CPUTIME_ID, &sigev, ptimerid) == -1) |
| { |
| TprintfT (DBG_LT2, "collector_timer_settime() failed! errno=%d\n", errno); |
| return -1; |
| } |
| return 0; |
| } |
| |
| static int |
| collector_timer_settime (int period, timer_t timerid) |
| { |
| struct itimerspec itimer; |
| if (NULL_PTR (timer_settime)) |
| init_interposition_intf (); |
| TprintfT (DBG_LT2, "collector_timer_settime(period=%d)\n", period); |
| time_t NPM = 1000; |
| itimer.it_interval.tv_sec = NPM * period / NANOSEC; |
| itimer.it_interval.tv_nsec = (NPM * period) % NANOSEC; |
| itimer.it_value = itimer.it_interval; |
| if (CALL_REAL (timer_settime)(timerid, 0, &itimer, NULL) == -1) |
| { |
| TprintfT (DBG_LT2, "collector_timer_settime(%d) failed! errno=%d\n", period, errno); |
| return -1; |
| } |
| return 0; |
| } |
| |
| static void |
| protect_profiling_signals (sigset_t* lset) |
| { |
| static unsigned int protected_sigprof = 0; |
| static unsigned int protected_sigemt = 0; |
| // T1 relies on thread signal masking, so best not to mess with it: |
| // T1 users have already been warned about the dangers of its use |
| if (__collector_libthread_T1) |
| return; |
| if (sigismember (lset, SIGPROF) && (dispatch_mode == DISPATCH_ON)) |
| { |
| TprintfT (0, "WARNING: ignoring %s block while profiling\n", "SIGPROF"); |
| if (protected_sigprof == 0) |
| __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", |
| SP_JCMD_CWARN, COL_WARN_SIGMASK, "SIGPROF"); |
| sigdelset (lset, SIGPROF); |
| protected_sigprof++; |
| } |
| if (sigismember (lset, HWCFUNCS_SIGNAL) && __collector_ext_hwc_active ()) |
| { |
| TprintfT (0, "WARNING: ignoring %s block while profiling\n", "SIGEMT"); |
| if (protected_sigemt == 0) |
| __collector_log_write ("<event kind=\"%s\" id=\"%d\">%s</event>\n", |
| SP_JCMD_CWARN, COL_WARN_SIGMASK, HWCFUNCS_SIGNAL_STRING); |
| sigdelset (lset, HWCFUNCS_SIGNAL); |
| protected_sigemt++; |
| } |
| } |
| |
| #define SYS_SETITIMER_NAME "setitimer" |
| #define SYS_SIGACTION_NAME "sigaction" |
| #define SYS_SIGPROCMASK_NAME "sigprocmask" |
| #define SYS_PTHREAD_SIGMASK "pthread_sigmask" |
| #define SYS_THR_SIGSETMASK "thr_sigsetmask" |
| |
| static int |
| init_interposition_intf () |
| { |
| if (__collector_dlsym_guard) |
| return 1; |
| void *dlflag; |
| /* Linux requires RTLD_LAZY, Solaris can do just RTLD_NOLOAD */ |
| void *handle = dlopen (SYS_LIBC_NAME, RTLD_LAZY | RTLD_NOLOAD); |
| |
| #if ARCH(SPARC) && WSIZE(64) |
| /* dlopen a bogus path to avoid CR 23608692 */ |
| dlopen ("/bogus_path_for_23608692_workaround/", RTLD_LAZY | RTLD_NOLOAD); |
| #endif |
| __real_setitimer = dlsym (RTLD_NEXT, SYS_SETITIMER_NAME); |
| |
| if (__real_setitimer == NULL) |
| { |
| __real_setitimer = dlsym (RTLD_DEFAULT, SYS_SETITIMER_NAME); |
| if (__real_setitimer == NULL) |
| { |
| TprintfT (DBG_LT2, "init_interposition_intf() setitimer not found\n"); |
| return 1; |
| } |
| dlflag = RTLD_DEFAULT; |
| } |
| else |
| dlflag = RTLD_NEXT; |
| |
| TprintfT (DBG_LT2, "init_interposition_intf() using RTLD_%s\n", |
| (dlflag == RTLD_DEFAULT) ? "DEFAULT" : "NEXT"); |
| TprintfT (DBG_LT2, "@%p __real_setitimer\n", __real_setitimer); |
| |
| __real_sigaction = dlsym (dlflag, SYS_SIGACTION_NAME); |
| TprintfT (DBG_LT2, "@%p __real_sigaction\n", __real_sigaction); |
| |
| /* also explicitly get libc.so/setitimer (as a backup) */ |
| __real_libc_setitimer = dlsym (handle, SYS_SETITIMER_NAME); |
| TprintfT (DBG_LT2, "@%p __real_libc_setitimer\n", __real_libc_setitimer); |
| |
| __real_sigprocmask = dlsym (dlflag, SYS_SIGPROCMASK_NAME); |
| TprintfT (DBG_LT2, "@%p __real_sigprocmask\n", __real_sigprocmask); |
| |
| __real_thr_sigsetmask = dlsym (dlflag, SYS_THR_SIGSETMASK); |
| TprintfT (DBG_LT2, "@%p __real_thr_sigsetmask\n", __real_thr_sigsetmask); |
| |
| __real_pthread_sigmask = dlsym (dlflag, SYS_PTHREAD_SIGMASK); |
| TprintfT (DBG_LT2, "@%p __real_pthread_sigmask\n", __real_pthread_sigmask); |
| |
| #if ARCH(Aarch64) |
| __real_pthread_create = dlvsym (dlflag, "pthread_create", SYS_PTHREAD_CREATE_VERSION); |
| __real_timer_create = dlsym (dlflag, "timer_create"); |
| __real_timer_settime = dlsym (dlflag, "timer_settime"); |
| __real_timer_delete = dlsym (dlflag, "timer_delete"); |
| __real_timer_gettime = dlsym (dlflag, "timer_gettime"); |
| #else |
| __real_pthread_create = dlvsym (dlflag, "pthread_create", SYS_PTHREAD_CREATE_VERSION); |
| TprintfT (DBG_LT2, "[%s] @%p __real_pthread_create\n", SYS_PTHREAD_CREATE_VERSION, __real_pthread_create); |
| __real_timer_create = dlvsym (dlflag, "timer_create", SYS_TIMER_X_VERSION); |
| TprintfT (DBG_LT2, "init_lineage_intf() [%s] @0x%p __real_timer_create\n", SYS_TIMER_X_VERSION, __real_timer_create); |
| __real_timer_settime = dlvsym (dlflag, "timer_settime", SYS_TIMER_X_VERSION); |
| TprintfT (DBG_LT2, "init_lineage_intf() [%s] @0x%p __real_timer_settime\n", SYS_TIMER_X_VERSION, __real_timer_settime); |
| __real_timer_delete = dlvsym (dlflag, "timer_delete", SYS_TIMER_X_VERSION); |
| TprintfT (DBG_LT2, "init_lineage_intf() [%s] @0x%p __real_timer_delete\n", SYS_TIMER_X_VERSION, __real_timer_delete); |
| __real_timer_gettime = dlvsym (dlflag, "timer_gettime", SYS_TIMER_X_VERSION); |
| TprintfT (DBG_LT2, "init_lineage_intf() [%s] @0x%p __real_timer_gettime\n", SYS_TIMER_X_VERSION, __real_timer_gettime); |
| __real_clone = dlsym (dlflag, "clone"); |
| TprintfT (DBG_LT2, "init_lineage_intf() @0x%p __real_clone\n", __real_clone); |
| #if ARCH(Intel) && WSIZE(32) |
| __real_pthread_create_2_1 = __real_pthread_create; |
| __real_pthread_create_2_0 = dlvsym (dlflag, "pthread_create", "GLIBC_2.0"); |
| #elif ARCH(Intel) && WSIZE(64) |
| __real_timer_create_2_3_3 = __real_timer_create; |
| __real_timer_create_2_2_5 = dlvsym (dlflag, "timer_create", "GLIBC_2.2.5"); |
| #elif ARCH(SPARC) && WSIZE(64) |
| __real_timer_create_2_3_3 = __real_timer_create; |
| __real_timer_create_2_2 = dlvsym (dlflag, "timer_create", "GLIBC_2.2"); |
| #endif /* ARCH() && SIZE() */ |
| #endif |
| return 0; |
| } |
| |
| |
| /*------------------------------------------------------------- sigaction */ |
| |
| /* NB: need a global interposing function called "sigaction" */ |
| int |
| sigaction (int sig, const struct sigaction *nact, struct sigaction *oact) |
| { |
| int ret = 0; |
| int err = 0; |
| if (NULL_PTR (sigaction)) |
| err = init_interposition_intf (); |
| if (err) |
| return -1; |
| TprintfT (DBG_LT3, "sigaction(sig=%02d, nact=%p) interposing\n", sig, nact); |
| if (sig == SIGPROF && dispatch_mode != DISPATCH_NYI) |
| { |
| if (oact != NULL) |
| { |
| oact->sa_handler = original_sigprof_handler.sa_handler; |
| oact->sa_mask = original_sigprof_handler.sa_mask; |
| oact->sa_flags = original_sigprof_handler.sa_flags; |
| } |
| if (nact != NULL) |
| { |
| original_sigprof_handler.sa_handler = nact->sa_handler; |
| original_sigprof_handler.sa_mask = nact->sa_mask; |
| original_sigprof_handler.sa_flags = nact->sa_flags; |
| TprintfT (DBG_LT1, "dispatcher: new sigaction(sig=%02d) set\n", sig); |
| } |
| } |
| else if (sig == HWCFUNCS_SIGNAL) |
| ret = collector_sigemt_sigaction (nact, oact); |
| else |
| { |
| if (sig != SIGCHLD || collector_sigchld_sigaction (nact, oact)) |
| ret = CALL_REAL (sigaction)(sig, nact, oact); |
| TprintfT (DBG_LT3, "Real sigaction(sig=%02d) returned %d (oact=%p)\n", |
| sig, ret, oact); |
| /* but check for other important signals */ |
| /* check for sample and pause/resume signals; give warning once, if need be */ |
| if ((sig == __collector_sample_sig) && (__collector_sample_sig_warn == 0)) |
| { |
| /* give user a warning */ |
| (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%d</event>\n", |
| SP_JCMD_CWARN, COL_WARN_SAMPSIGUSED, __collector_sample_sig); |
| __collector_sample_sig_warn = 1; |
| } |
| if ((sig == __collector_pause_sig) && (__collector_pause_sig_warn == 0)) |
| { |
| /* give user a warning */ |
| (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%d</event>\n", |
| SP_JCMD_CWARN, COL_WARN_PAUSESIGUSED, __collector_pause_sig); |
| __collector_pause_sig_warn = 1; |
| } |
| } |
| TprintfT (DBG_LT3, "sigaction() returning %d (oact=%p)\n", ret, oact); |
| return ret; |
| } |
| |
| /* |
| * In addition to interposing on sigaction(), should we also interpose |
| * on other important signal functions like signal() or sigset()? |
| * - On Solaris, those other functions apparently call sigaction(). |
| * So, we only have to interpose on it. |
| * - On Linux, we should perhaps interpose on these other functions, |
| * but they are less portable than sigaction() and deprecated or even obsolete. |
| * So, we interpose, but don't overly worry about doing a good job. |
| */ |
| sighandler_t |
| signal (int sig, sighandler_t handler) |
| { |
| struct sigaction nact; |
| struct sigaction oact; |
| TprintfT (DBG_LT3, "signal(sig=%02d, handler=%p) interposing\n", sig, handler); |
| sigemptyset (&nact.sa_mask); |
| nact.sa_handler = handler; |
| nact.sa_flags = SA_RESTART; |
| if (sigaction (sig, &nact, &oact)) |
| return SIG_ERR; |
| TprintfT (DBG_LT3, "signal() returning %p\n", oact.sa_handler); |
| return oact.sa_handler; |
| } |
| |
| sighandler_t |
| sigset (int sig, sighandler_t handler) |
| { |
| TprintfT (DBG_LT3, "sigset(sig=%02d, handler=%p) interposing\n", sig, handler); |
| return signal (sig, handler); |
| } |
| |
| /*------------------------------------------------------------- timer_create */ |
| |
| // map interposed symbol versions |
| #if WSIZE(64) |
| #if ARCH(SPARC) || ARCH(Intel) |
| static int |
| __collector_timer_create_symver (int(real_timer_create) (), clockid_t clockid, struct sigevent *sevp, |
| timer_t *timerid); |
| |
| SYMVER_ATTRIBUTE (__collector_timer_create_2_3_3, timer_create@GLIBC_2.3.3) |
| int |
| __collector_timer_create_2_3_3 (clockid_t clockid, struct sigevent *sevp, |
| timer_t *timerid) |
| { |
| if (NULL_PTR (timer_create)) |
| init_interposition_intf (); |
| TprintfT (DBG_LTT, "dispatcher: GLIBC: __collector_timer_create_2_3_3@%p\n", CALL_REAL (timer_create_2_3_3)); |
| return __collector_timer_create_symver (CALL_REAL (timer_create_2_3_3), clockid, sevp, timerid); |
| } |
| #endif /* ARCH(SPARC) || ARCH(Intel)*/ |
| |
| #if ARCH(SPARC) |
| |
| SYMVER_ATTRIBUTE (__collector_timer_create_2_2, timer_create@GLIBC_2.2) |
| int |
| __collector_timer_create_2_2 (clockid_t clockid, struct sigevent *sevp, |
| timer_t *timerid) |
| { |
| if (NULL_PTR (timer_create)) |
| init_interposition_intf (); |
| TprintfT (DBG_LTT, "dispatcher: GLIBC: __collector_timer_create_2_2@%p\n", CALL_REAL (timer_create_2_2)); |
| return __collector_timer_create_symver (CALL_REAL (timer_create_2_2), clockid, sevp, timerid); |
| } |
| |
| #elif ARCH(Intel) |
| |
| SYMVER_ATTRIBUTE (__collector_timer_create_2_2_5, timer_create@GLIBC_2.2.5) |
| int |
| __collector_timer_create_2_2_5 (clockid_t clockid, struct sigevent *sevp, |
| timer_t *timerid) |
| { |
| if (NULL_PTR (timer_create)) |
| init_interposition_intf (); |
| TprintfT (DBG_LTT, "dispatcher: GLIBC: __collector_timer_create_2_2_5@%p\n", CALL_REAL (timer_create_2_2_5)); |
| return __collector_timer_create_symver (CALL_REAL (timer_create_2_2_5), clockid, sevp, timerid); |
| } |
| #endif /* ARCH() */ |
| #endif /* WSIZE(64) */ |
| |
| #if ARCH(Aarch64) || (ARCH(Intel) && WSIZE(32)) |
| int timer_create (clockid_t clockid, struct sigevent *sevp, timer_t *timerid) |
| #else |
| static int |
| __collector_timer_create_symver (int(real_timer_create) (), clockid_t clockid, |
| struct sigevent *sevp, timer_t *timerid) |
| #endif |
| { |
| int ret; |
| |
| if (NULL_PTR (timer_create)) |
| init_interposition_intf (); |
| |
| /* collector reserves SIGPROF |
| */ |
| if (sevp == NULL || sevp->sigev_notify != SIGEV_SIGNAL |
| || sevp->sigev_signo != SIGPROF) |
| { |
| #if ARCH(Aarch64) || (ARCH(Intel) && WSIZE(32)) |
| ret = CALL_REAL (timer_create)(clockid, sevp, timerid); |
| #else |
| ret = (real_timer_create) (clockid, sevp, timerid); |
| #endif |
| TprintfT (DBG_LT2, "Real timer_create(%d) returned %d\n", |
| clockid, ret); |
| return ret; |
| } |
| |
| /* log that application's timer_create request is overridden */ |
| (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%d</event>\n", |
| SP_JCMD_CWARN, COL_WARN_ITMROVR, -1); |
| ret = -1; |
| errno = EBUSY; |
| TprintfT (DBG_LT2, "timer_create() returning %d\n", ret); |
| return ret; |
| } |
| /*------------------------------------------------------------- setitimer */ |
| int |
| _setitimer (int which, const struct itimerval *nval, |
| struct itimerval *oval) |
| { |
| int ret; |
| int period; |
| |
| if (NULL_PTR (setitimer)) |
| init_interposition_intf (); |
| |
| if (nval == NULL) |
| period = -1; |
| else |
| period = (nval->it_interval.tv_sec * MICROSEC) + |
| nval->it_interval.tv_usec; |
| TprintfT (DBG_LT1, "setitimer(which=%d,nval=%dus) interposing\n", which, period); |
| |
| /* collector reserves ITIMER_REALPROF for its own use, and ITIMER_PROF |
| * uses the same signal (SIGPROF) so it must also be reserved |
| */ |
| if (((which != ITIMER_REALPROF) && (which != ITIMER_PROF)) || (nval == NULL)) |
| { |
| ret = CALL_REAL (setitimer)(which, nval, oval); |
| if (oval == NULL) |
| period = -1; |
| else |
| period = (oval->it_interval.tv_sec * MICROSEC) + |
| oval->it_interval.tv_usec; |
| TprintfT (DBG_LT2, "Real setitimer(%d) returned %d (oval=%dus)\n", |
| which, ret, period); |
| return ret; |
| } |
| /* log that application's setitimer request is overridden */ |
| (void) __collector_log_write ("<event kind=\"%s\" id=\"%d\">%d</event>\n", |
| SP_JCMD_CWARN, COL_WARN_ITMROVR, period); |
| if (oval == NULL) |
| period = -1; |
| else |
| { |
| getitimer (which, oval); /* return current itimer setting */ |
| period = (oval->it_interval.tv_sec * MICROSEC) + |
| oval->it_interval.tv_usec; |
| } |
| ret = -1; |
| errno = EBUSY; |
| TprintfT (DBG_LT2, "setitimer() returning %d (oval=%dus)\n", ret, period); |
| return ret; |
| } |
| |
| /*--------------------------------------------------------------- sigprocmask */ |
| int |
| __collector_sigprocmask (int how, const sigset_t* iset, sigset_t* oset) |
| { |
| int err = 0; |
| if (NULL_PTR (sigprocmask)) |
| err = init_interposition_intf (); |
| if (err) |
| return -1; |
| TprintfT (DBG_LT2, "__collector_sigprocmask(%d) interposing\n", how); |
| sigset_t lsigset; |
| sigset_t* lset = NULL; |
| if (iset) |
| { |
| lsigset = *iset; |
| lset = &lsigset; |
| if ((how == SIG_BLOCK) || (how == SIG_SETMASK)) |
| protect_profiling_signals (lset); |
| } |
| int ret = CALL_REAL (sigprocmask)(how, lset, oset); |
| TprintfT (DBG_LT2, "__collector_sigprocmask(%d) returning %d\n", how, ret); |
| return ret; |
| } |
| |
| /*------------------------------------------------------------ thr_sigsetmask */ |
| int |
| __collector_thr_sigsetmask (int how, const sigset_t* iset, sigset_t* oset) |
| { |
| if (NULL_PTR (thr_sigsetmask)) |
| init_interposition_intf (); |
| TprintfT (DBG_LT1, "__collector_thr_sigsetmask(%d) interposing\n", how); |
| sigset_t lsigset; |
| sigset_t* lset = NULL; |
| if (iset) |
| { |
| lsigset = *iset; |
| lset = &lsigset; |
| if ((how == SIG_BLOCK) || (how == SIG_SETMASK)) |
| protect_profiling_signals (lset); |
| } |
| int ret = CALL_REAL (thr_sigsetmask)(how, lset, oset); |
| TprintfT (DBG_LT1, "__collector_thr_sigsetmask(%d) returning %d\n", how, ret); |
| return ret; |
| } |
| |
| /*----------------------------------------------------------- pthread_sigmask */ |
| |
| int |
| pthread_sigmask (int how, const sigset_t* iset, sigset_t* oset) |
| { |
| if (NULL_PTR (pthread_sigmask)) |
| init_interposition_intf (); |
| TprintfT (DBG_LT1, "__collector_pthread_sigmask(%d) interposing\n", how); |
| sigset_t lsigset; |
| sigset_t* lset = NULL; |
| if (iset) |
| { |
| lsigset = *iset; |
| lset = &lsigset; |
| if ((how == SIG_BLOCK) || (how == SIG_SETMASK)) |
| protect_profiling_signals (lset); |
| } |
| int ret = CALL_REAL (pthread_sigmask)(how, lset, oset); |
| TprintfT (DBG_LT1, "__collector_pthread_sigmask(%d) returning %d\n", how, ret); |
| return ret; |
| } |
| /*----------------------------------------------------------- pthread_create */ |
| typedef struct _CollectorArgs |
| { |
| void *(*func)(void*); |
| void *arg; |
| void *stack; |
| int isPthread; |
| } CollectorArgs; |
| |
| static void * |
| collector_root (void *cargs) |
| { |
| /* save the real arguments and free cargs */ |
| void *(*func)(void*) = ((CollectorArgs*) cargs)->func; |
| void *arg = ((CollectorArgs*) cargs)->arg; |
| void *stack = ((CollectorArgs*) cargs)->stack; |
| int isPthread = ((CollectorArgs*) cargs)->isPthread; |
| __collector_freeCSize (__collector_heap, cargs, sizeof (CollectorArgs)); |
| |
| /* initialize tsd for this thread */ |
| if (__collector_tsd_allocate () == 0) |
| /* init tsd for unwind, called right after __collector_tsd_allocate()*/ |
| __collector_ext_unwind_key_init (isPthread, stack); |
| |
| if (!isPthread) |
| __collector_mutex_lock (&collector_clone_libc_lock); |
| |
| /* set the profile timer */ |
| timer_t *timeridptr = __collector_tsd_get_by_key (dispatcher_key); |
| timer_t timerid = NULL; |
| if (timeridptr != NULL) |
| { |
| collector_timer_create (timeridptr); |
| if (*timeridptr != NULL) |
| collector_timer_settime (itimer_period_requested, *timeridptr); |
| timerid = *timeridptr; |
| } |
| int hwc_rc = __collector_ext_hwc_lwp_init (); |
| |
| if (!isPthread) |
| __collector_mutex_unlock (&collector_clone_libc_lock); |
| /* call the real function */ |
| void *ret = func (arg); |
| if (!isPthread) |
| __collector_mutex_lock (&collector_clone_libc_lock); |
| if (timerid != NULL) |
| CALL_REAL (timer_delete)(timerid); |
| if (!hwc_rc) |
| /* pthread_kill not handled here */ |
| __collector_ext_hwc_lwp_fini (); |
| |
| if (!isPthread) |
| __collector_mutex_unlock (&collector_clone_libc_lock); |
| /* if we have this chance, release tsd */ |
| __collector_tsd_release (); |
| |
| return ret; |
| } |
| |
| // map interposed symbol versions |
| #if ARCH(Intel) && WSIZE(32) |
| static int |
| __collector_pthread_create_symver (int(real_pthread_create) (), |
| pthread_t *thread, |
| const pthread_attr_t *attr, |
| void *(*func)(void*), |
| void *arg); |
| |
| SYMVER_ATTRIBUTE (__collector_pthread_create_2_1, pthread_create@GLIBC_2.1) |
| int |
| __collector_pthread_create_2_1 (pthread_t *thread, |
| const pthread_attr_t *attr, |
| void *(*func)(void*), |
| void *arg) |
| { |
| if (NULL_PTR (pthread_create)) |
| init_interposition_intf (); |
| TprintfT (DBG_LTT, "dispatcher: GLIBC: __collector_pthread_create_2_1@%p\n", CALL_REAL (pthread_create_2_1)); |
| return __collector_pthread_create_symver (CALL_REAL (pthread_create_2_1), thread, attr, func, arg); |
| } |
| |
| SYMVER_ATTRIBUTE (__collector_pthread_create_2_0, pthread_create@GLIBC_2.0) |
| int |
| __collector_pthread_create_2_0 (pthread_t *thread, |
| const pthread_attr_t *attr, |
| void *(*func)(void*), |
| void *arg) |
| { |
| if (NULL_PTR (pthread_create)) |
| init_interposition_intf (); |
| TprintfT (DBG_LTT, "dispatcher: GLIBC: __collector_pthread_create_2_0@%p\n", CALL_REAL (pthread_create_2_0)); |
| return __collector_pthread_create_symver (CALL_REAL (pthread_create_2_0), thread, attr, func, arg); |
| } |
| |
| #endif |
| |
| #if ARCH(Intel) && WSIZE(32) |
| static int |
| __collector_pthread_create_symver (int(real_pthread_create) (), |
| pthread_t *thread, |
| const pthread_attr_t *attr, |
| void *(*func)(void*), |
| void *arg) |
| #else |
| int |
| pthread_create (pthread_t *thread, const pthread_attr_t *attr, |
| void *(*func)(void*), void *arg) |
| #endif |
| { |
| if (NULL_PTR (pthread_create)) |
| init_interposition_intf (); |
| |
| TprintfT (DBG_LT1, "pthread_create interposition called\n"); |
| |
| if (dispatch_mode != DISPATCH_ON) |
| { |
| #if ARCH(Intel) && WSIZE(32) |
| return (real_pthread_create) (thread, attr, func, arg); |
| #else |
| return CALL_REAL (pthread_create)(thread, attr, func, arg); |
| #endif |
| } |
| CollectorArgs *cargs = __collector_allocCSize (__collector_heap, sizeof (CollectorArgs), 1); |
| |
| if (cargs == NULL) |
| { |
| #if ARCH(Intel) && WSIZE(32) |
| return (real_pthread_create) (thread, attr, func, arg); |
| #else |
| return CALL_REAL (pthread_create)(thread, attr, func, arg); |
| #endif |
| } |
| cargs->func = func; |
| cargs->arg = arg; |
| cargs->stack = NULL; |
| cargs->isPthread = 1; |
| int ret = -1; |
| #if ARCH(Intel) && WSIZE(32) |
| ret = (real_pthread_create) (thread, attr, &collector_root, cargs); |
| #else |
| ret = CALL_REAL (pthread_create)(thread, attr, &collector_root, cargs); |
| #endif |
| if (ret) |
| __collector_freeCSize (__collector_heap, cargs, sizeof (CollectorArgs)); |
| TprintfT (DBG_LT1, "pthread_create returning %d\n", ret); |
| return ret; |
| } |
| |
| int |
| __collector_ext_clone_pthread (int (*fn)(void *), void *child_stack, int flags, void *arg, |
| va_list va /* pid_t *ptid, struct user_desc *tls, pid_t *" ctid" */) |
| { |
| if (NULL_PTR (clone)) |
| init_interposition_intf (); |
| TprintfT (0, "clone thread interposing\n"); |
| pid_t * ptid = NULL; |
| struct user_desc * tls = NULL; |
| pid_t * ctid = NULL; |
| int num_args = 0; |
| if (flags & (CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID)) |
| { |
| ptid = va_arg (va, pid_t *); |
| tls = va_arg (va, struct user_desc*); |
| ctid = va_arg (va, pid_t *); |
| num_args = 3; |
| } |
| else if (flags & CLONE_SETTLS) |
| { |
| ptid = va_arg (va, pid_t *); |
| tls = va_arg (va, struct user_desc*); |
| num_args = 2; |
| } |
| else if (flags & CLONE_PARENT_SETTID) |
| { |
| ptid = va_arg (va, pid_t *); |
| num_args = 1; |
| } |
| int ret = 0; |
| if (dispatch_mode != DISPATCH_ON) |
| { |
| switch (num_args) |
| { |
| case 3: |
| ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls, ctid); |
| break; |
| case 2: |
| ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls); |
| break; |
| case 1: |
| ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid); |
| break; |
| default: |
| ret = CALL_REAL (clone)(fn, child_stack, flags, arg); |
| break; |
| } |
| return ret; |
| } |
| CollectorArgs *cargs = __collector_allocCSize (__collector_heap, sizeof (CollectorArgs), 1); |
| if (cargs == NULL) |
| { |
| switch (num_args) |
| { |
| case 3: |
| ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls, ctid); |
| break; |
| case 2: |
| ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid, tls); |
| break; |
| case 1: |
| ret = CALL_REAL (clone)(fn, child_stack, flags, arg, ptid); |
| break; |
| default: |
| ret = CALL_REAL (clone)(fn, child_stack, flags, arg); |
| break; |
| } |
| return ret; |
| } |
| |
| cargs->func = (void *(*)(void*))fn; |
| cargs->arg = arg; |
| cargs->stack = child_stack; |
| cargs->isPthread = 0; |
| |
| switch (num_args) |
| { |
| case 3: |
| ret = CALL_REAL (clone)((int(*)(void*))collector_root, child_stack, flags, cargs, ptid, tls, ctid); |
| break; |
| case 2: |
| ret = CALL_REAL (clone)((int(*)(void*))collector_root, child_stack, flags, cargs, ptid, tls); |
| break; |
| case 1: |
| ret = CALL_REAL (clone)((int(*)(void*))collector_root, child_stack, flags, cargs, ptid); |
| break; |
| default: |
| ret = CALL_REAL (clone)((int(*)(void*))collector_root, child_stack, flags, cargs); |
| break; |
| } |
| |
| if (ret < 0) |
| __collector_freeCSize (__collector_heap, cargs, sizeof (CollectorArgs)); |
| TprintfT (DBG_LT1, "clone thread returning %d\n", ret); |
| return ret; |
| } |
| |
| // weak symbols: |
| int sigprocmask () __attribute__ ((weak, alias ("__collector_sigprocmask"))); |
| int thr_sigsetmask () __attribute__ ((weak, alias ("__collector_thr_sigsetmask"))); |
| int setitimer () __attribute__ ((weak, alias ("_setitimer"))); |