binutils-gdb/gprofng/libcollector/dispatcher.c
Alan Modra 76bdc7266a Update year range in gprofng copyright notices
This adds 'Innovative Computing Labs' as an external author to
update-copyright.py, to cover the copyright notice in
gprofng/common/opteron_pcbe.c, and uses that plus another external
author 'Oracle and' to update gprofng copyright dates.  I'm not going
to commit 'Oracle and' as an accepted author, but that covers the
string "Copyright (c) 2006, 2012, Oracle and/or its affiliates. All
rights reserved." found in gprofng/testsuite/gprofng.display/jsynprog
files.
2023-01-01 23:26:30 +10:30

1262 lines
43 KiB
C

/* Copyright (C) 2021-2023 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")));