/* Copyright (C) 2021-2024 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.  */

/*
 *	Synchronization events
 */
#include "config.h"
#include <alloca.h>
#include <dlfcn.h>
#include <unistd.h>
#include <semaphore.h>		/* sem_wait() */
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <pthread.h>

#include "gp-defs.h"
#include "collector.h"
#include "gp-experiment.h"
#include "data_pckts.h"
#include "tsd.h"
#include "cc_libcollector.h"

/* define the packet that will be written out */
typedef struct Sync_packet
{ /* Synchronization delay tracing packet */
  Common_packet comm;
  hrtime_t requested;       /* time of synchronization request */
  Vaddr_type objp;          /* vaddr of synchronization object */
} Sync_packet;

static int open_experiment (const char *);
static int start_data_collection (void);
static int stop_data_collection (void);
static int close_experiment (void);
static int detach_experiment (void);
static int init_thread_intf ();
static int sync_calibrate ();

static ModuleInterface module_interface ={
  SP_SYNCTRACE_FILE,        /* description */
  NULL,                     /* initInterface */
  open_experiment,          /* openExperiment */
  start_data_collection,    /* startDataCollection */
  stop_data_collection,     /* stopDataCollection */
  close_experiment,         /* closeExperiment */
  detach_experiment         /* detachExperiment (fork child) */
};

static CollectorInterface *collector_interface = NULL;
static int sync_mode = 0;
static long sync_scope = 0;
static int sync_native = 0;
static int sync_java = 0;
static CollectorModule sync_hndl = COLLECTOR_MODULE_ERR;
static unsigned sync_key = COLLECTOR_TSD_INVALID_KEY;
static long sync_threshold = -1; /* calibrate the value */
static int init_thread_intf_started = 0;
static int init_thread_intf_finished = 0;
static Sync_packet spacket_0 = { .comm.tsize = sizeof ( Sync_packet) };

#define CHCK_NREENTRANCE(x)     (!sync_native || !sync_mode || ((x) = collector_interface->getKey( sync_key )) == NULL || (*(x) != 0))
#define RECHCK_NREENTRANCE(x)   (!sync_native || !sync_mode || ((x) = collector_interface->getKey( sync_key )) == NULL || (*(x) == 0))
#define CHCK_JREENTRANCE(x)     (!sync_java || !sync_mode || ((x) = collector_interface->getKey( sync_key )) == NULL || (*(x) != 0))
#define RECHCK_JREENTRANCE(x)   (!sync_java || !sync_mode || ((x) = collector_interface->getKey( sync_key )) == NULL || (*(x) == 0))
#define PUSH_REENTRANCE(x)      ((*(x))++)
#define POP_REENTRANCE(x)       ((*(x))--)
#define gethrtime	collector_interface->getHiResTime

/*
 * In most cases, the functions which require interposition are implemented as
 * weak symbols corresponding to an associated internal function named with a
 * leading underscore: e.g., mutex_lock() is simply an alias for _mutex_lock().
 * For the wait functions, however, the published version (used by applications)
 * is distinct from the internal version (used by system libraries), i.e.,
 * cond_wait() is an alias for _cond_wait_cancel() rather than _cond_wait().
 */
static long int (*__real_strtol)(const char *nptr, char **endptr, int base) = NULL;
static int (*__real_fprintf) (FILE *stream, const char *format, ...) = NULL;
static void (*__real___collector_jprofile_enable_synctrace) (void) = NULL;
static int (*__real_pthread_mutex_lock) (pthread_mutex_t *mutex) = NULL;
static int (*__real_pthread_mutex_lock_2_17) (pthread_mutex_t *mutex) = NULL;
static int (*__real_pthread_mutex_lock_2_2_5) (pthread_mutex_t *mutex) = NULL;
static int (*__real_pthread_mutex_lock_2_0) (pthread_mutex_t *mutex) = NULL;
static int (*__real_pthread_mutex_unlock) (pthread_mutex_t *mutex) = NULL;
static int (*__real_pthread_cond_wait) (pthread_cond_t *restrict cond,
				pthread_mutex_t *restrict mutex) = NULL;
static int (*__real_pthread_cond_timedwait) (pthread_cond_t *restrict cond,
			pthread_mutex_t *restrict mutex,
			const struct timespec *restrict abstime) = NULL;
static int (*__real_pthread_join) (pthread_t thread, void **retval) = NULL;
static int (*__real_pthread_join_2_34) (pthread_t thread, void **retval) = NULL;
static int (*__real_pthread_join_2_17) (pthread_t thread, void **retval) = NULL;
static int (*__real_pthread_join_2_2_5) (pthread_t thread, void **retval) = NULL;
static int (*__real_pthread_join_2_0) (pthread_t thread, void **retval) = NULL;
static int (*__real_sem_wait) (sem_t *sem) = NULL;
static int (*__real_sem_wait_2_34) (sem_t *sem) = NULL;
static int (*__real_sem_wait_2_17) (sem_t *sem) = NULL;
static int (*__real_sem_wait_2_2_5) (sem_t *sem) = NULL;
static int (*__real_sem_wait_2_1) (sem_t *sem) = NULL;
static int (*__real_sem_wait_2_0) (sem_t *sem) = NULL;
static int (*__real_pthread_cond_wait_2_17) (pthread_cond_t *restrict cond,
				pthread_mutex_t *restrict mutex) = NULL;
static int (*__real_pthread_cond_wait_2_3_2) (pthread_cond_t *restrict cond,
				pthread_mutex_t *restrict mutex) = NULL;
static int (*__real_pthread_cond_wait_2_2_5) (pthread_cond_t *restrict cond,
				pthread_mutex_t *restrict mutex) = NULL;
static int (*__real_pthread_cond_wait_2_0) (pthread_cond_t *restrict cond,
				pthread_mutex_t *restrict mutex) = NULL;
static int (*__real_pthread_cond_timedwait_2_17) (pthread_cond_t *restrict cond,
			pthread_mutex_t *restrict mutex,
			const struct timespec *restrict abstime) = NULL;
static int (*__real_pthread_cond_timedwait_2_3_2) (pthread_cond_t *restrict cond,
			pthread_mutex_t *restrict mutex,
			const struct timespec *restrict abstime) = NULL;
static int (*__real_pthread_cond_timedwait_2_2_5) (pthread_cond_t *restrict cond,
			pthread_mutex_t *restrict mutex,
			const struct timespec *restrict abstime) = NULL;
static int (*__real_pthread_cond_timedwait_2_0) (pthread_cond_t *restrict cond,
			pthread_mutex_t *restrict mutex,
			const struct timespec *restrict abstime) = NULL;

void
__collector_module_init (CollectorInterface *_collector_interface)
{
  if (_collector_interface == NULL)
    return;
  collector_interface = _collector_interface;
  TprintfT (0, "synctrace: __collector_module_init\n");
  sync_hndl = collector_interface->registerModule (&module_interface);

  /* Initialize next module */
  ModuleInitFunc next_init = (ModuleInitFunc) dlsym (RTLD_NEXT, "__collector_module_init");
  if (next_init != NULL)
    next_init (_collector_interface);
}

static int
open_experiment (const char *exp)
{
  long thresh = 0;
  if (init_thread_intf_finished == 0)
    init_thread_intf ();
  if (collector_interface == NULL)
    {
      Tprintf (0, "synctrace: collector_interface is null.\n");
      return COL_ERROR_SYNCINIT;
    }
  if (sync_hndl == COLLECTOR_MODULE_ERR)
    {
      Tprintf (0, "synctrace: handle create failed.\n");
      collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">data handle not created</event>\n",
				     SP_JCMD_CERROR, COL_ERROR_SYNCINIT);
      return COL_ERROR_SYNCINIT;
    }
  TprintfT (0, "synctrace: open_experiment %s\n", exp);

  char *params = (char *) collector_interface->getParams ();
  while (params)
    {
      if ((params[0] == 's') && (params[1] == ':'))
	{
	  char *ptr = params + 2;
	  Tprintf (DBG_LT1, "synctrace: open_experiment s: parameter = %s\n", ptr);
	  while (*ptr != ',' && *ptr != ';')
	    ptr++;
	  sync_scope = 0;
	  if (*ptr == ',')
	    {
	      sync_scope = CALL_REAL (strtol) (ptr + 1, NULL, 0);
	      switch (sync_scope)
		{
		case 1:
		  sync_java = 0;
		  sync_native = 1;
		  break;
		case 2:
		  sync_java = 1;
		  sync_native = 0;
		  break;
		default:
		case 3:
		  sync_native = 1;
		  sync_java = 1;
		  break;
		}
	      Tprintf (0, "\tsynctrace: sync_scope found as %ld\n", sync_scope);
	    }
	  else
	    {
	      /* the old-style descriptor, without scope */
	      /* if there was no comma, use the old default */
	      sync_scope = 3;
	      sync_java = 1;
	      sync_native = 1;
	      Tprintf (0, "\tsynctrace: sync_scope not found set to %ld\n", sync_scope);
	    }
	  if (__real___collector_jprofile_enable_synctrace == NULL)
	    sync_java = 0;
	  thresh = CALL_REAL (strtol)(params + 2, NULL, 0);
	  break; /* from the loop to find the "s:thresh,scope" entry */
	}
      else
	params++;
    }
  if (params == NULL)  /* Sync data collection not specified */
    return COL_ERROR_SYNCINIT;
  if (thresh < 0)  /* calibrate the threshold, keep it as a negative number */
    thresh = -sync_calibrate ();

  sync_key = collector_interface->createKey (sizeof ( int), NULL, NULL);
  if (sync_key == (unsigned) - 1)
    {
      Tprintf (0, "synctrace: TSD key create failed.\n");
      collector_interface->writeLog ("<event kind=\"%s\" id=\"%d\">TSD key not created</event>\n",
				     SP_JCMD_CERROR, COL_ERROR_SYNCINIT);
      return COL_ERROR_SYNCINIT;
    }
  /* if Java synctrace was requested, tell the jprofile module */
  if (sync_java)
    {
      TprintfT (0, "synctrace: enabling Java synctrace\n");
      CALL_REAL (__collector_jprofile_enable_synctrace)();
    }
  collector_interface->writeLog ("<profile name=\"%s\" threshold=\"%ld\" scope=\"%ld\">\n",
				 SP_JCMD_SYNCTRACE, thresh, sync_scope);
  collector_interface->writeLog ("  <profdata fname=\"%s\"/>\n",
				 module_interface.description);
  /* Record Sync_packet description */
  collector_interface->writeLog ("  <profpckt kind=\"%d\" uname=\"Synchronization tracing data\">\n", SYNC_PCKT);
  collector_interface->writeLog ("    <field name=\"LWPID\" uname=\"Lightweight process id\" offset=\"%d\" type=\"%s\"/>\n",
		(int) offsetof (Sync_packet, comm.lwp_id),
		fld_sizeof (Sync_packet, comm.lwp_id) == 4 ? "INT32" : "INT64");
  collector_interface->writeLog ("    <field name=\"THRID\" uname=\"Thread number\" offset=\"%d\" type=\"%s\"/>\n",
		(int) offsetof (Sync_packet, comm.thr_id),
		fld_sizeof (Sync_packet, comm.thr_id) == 4 ? "INT32" : "INT64");
  collector_interface->writeLog ("    <field name=\"CPUID\" uname=\"CPU id\" offset=\"%d\" type=\"%s\"/>\n",
		(int) offsetof (Sync_packet, comm.cpu_id),
		fld_sizeof (Sync_packet, comm.cpu_id) == 4 ? "INT32" : "INT64");
  collector_interface->writeLog ("    <field name=\"TSTAMP\" uname=\"High resolution timestamp\" offset=\"%d\" type=\"%s\"/>\n",
		(int) offsetof (Sync_packet, comm.tstamp),
		fld_sizeof (Sync_packet, comm.tstamp) == 4 ? "INT32" : "INT64");
  collector_interface->writeLog ("    <field name=\"FRINFO\" offset=\"%d\" type=\"%s\"/>\n",
		(int) offsetof (Sync_packet, comm.frinfo),
		fld_sizeof (Sync_packet, comm.frinfo) == 4 ? "INT32" : "INT64");
  collector_interface->writeLog ("    <field name=\"SRQST\" uname=\"Synchronization start time\" offset=\"%d\" type=\"%s\"/>\n",
		(int) offsetof (Sync_packet, requested),
		fld_sizeof (Sync_packet, requested) == 4 ? "INT32" : "INT64");
  collector_interface->writeLog ("    <field name=\"SOBJ\" uname=\"Synchronization object address\" offset=\"%d\" type=\"%s\"/>\n",
		(int) offsetof (Sync_packet, objp),
		fld_sizeof (Sync_packet, objp) == 4 ? "INT32" : "INT64");
  collector_interface->writeLog ("  </profpckt>\n");
  collector_interface->writeLog ("</profile>\n");

  /* Convert threshold from microsec to nanosec */
  sync_threshold = (thresh > 0 ? thresh : -thresh) * 1000;
  TprintfT (0, "synctrace: open_experiment complete %ld\n", sync_threshold);
  return COL_ERROR_NONE;
}

static int
start_data_collection (void)
{
  sync_mode = 1;
  TprintfT (0, "synctrace: start_data_collection\n");
  return 0;
}

static int
stop_data_collection (void)
{
  sync_mode = 0;
  TprintfT (0, "synctrace: stop_data_collection\n");
  return 0;
}

static int
close_experiment (void)
{
  sync_mode = 0;
  sync_threshold = -1;
  sync_key = COLLECTOR_TSD_INVALID_KEY;
  TprintfT (0, "synctrace: close_experiment\n");
  return 0;
}

/* fork child.  Clean up state but don't write to experiment */
static int
detach_experiment (void)
{
  sync_mode = 0;
  sync_threshold = -1;
  sync_key = COLLECTOR_TSD_INVALID_KEY;
  TprintfT (0, "synctrace: detach_experiment\n");
  return 0;
}

#define NUM_ITER    100     /* number of iterations in calibration */
#define NUM_WARMUP    3     /* number of warm up iterations */

static int
sync_calibrate ()
{
  pthread_mutex_t mt = PTHREAD_MUTEX_INITIALIZER;
  hrtime_t bt, at, delta;
  hrtime_t avg, max, min;
  int i;
  int ret;
  avg = (hrtime_t) 0;
  min = max = (hrtime_t) 0;
  for (i = 0; i < NUM_ITER + NUM_WARMUP; i++)
    {
      /* Here we simulate a real call */
      bt = gethrtime ();
      ret = CALL_REAL (pthread_mutex_lock)(&mt);
      at = gethrtime ();
      CALL_REAL (pthread_mutex_unlock)(&mt);
      if (i < NUM_WARMUP)   /* skip these iterations */
	continue;
      /* add the time of this one */
      delta = at - bt;
      avg += delta;
      if (min == 0)
	min = delta;
      if (delta < min)
	min = delta;
      if (delta > max)
	max = delta;
    }
  /* compute average time */
  avg = avg / NUM_ITER;

  /* pretty simple, let's see how it works */
  if (max < 6 * avg)
    max = 6 * avg;
  /* round up to the nearest microsecond */
  ret = (int) ((max + 999) / 1000);
  return ret;
}

static int
init_pthread_mutex_lock (void *dlflag)
{
  __real_pthread_mutex_lock_2_17 = dlvsym (dlflag, "pthread_mutex_lock", "GLIBC_2.17");
  __real_pthread_mutex_lock_2_2_5 = dlvsym (dlflag, "pthread_mutex_lock", "GLIBC_2.2.5");
  __real_pthread_mutex_lock_2_0 = dlvsym (dlflag, "pthread_mutex_lock", "GLIBC_2.0");
  if (__real_pthread_mutex_lock_2_17)
    __real_pthread_mutex_lock = __real_pthread_mutex_lock_2_17;
  else if (__real_pthread_mutex_lock_2_2_5)
    __real_pthread_mutex_lock = __real_pthread_mutex_lock_2_2_5;
  else if (__real_pthread_mutex_lock_2_0)
    __real_pthread_mutex_lock = __real_pthread_mutex_lock_2_0;
  else
    __real_pthread_mutex_lock = dlsym (dlflag, "pthread_mutex_lock");
  return __real_pthread_mutex_lock ? 1 : 0;
}

static int
init_thread_intf ()
{
  void *dlflag = RTLD_NEXT;
  int err = 0;
  /* if we detect recursion/reentrance, SEGV so we can get a stack */
  init_thread_intf_started++;
  if (!init_thread_intf_finished && init_thread_intf_started >= 3)
    {
      /* pull the plug if recursion occurs... */
      abort ();
    }
  /* lookup fprint to print fatal error message */
  void *ptr = dlsym (RTLD_DEFAULT, "fprintf");
  if (ptr)
    {
      __real_fprintf = (void *) ptr;
    }
  else
    {
      abort ();
    }

  /* find the __collector_jprofile_enable_synctrace routine in jprofile module */
  ptr = dlsym (RTLD_DEFAULT, "__collector_jprofile_enable_synctrace");
  if (ptr)
    __real___collector_jprofile_enable_synctrace = (void *) ptr;
  else
    {
#if defined(GPROFNG_JAVA_PROFILING)
      CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT __collector_jprofile_enable_synctrace\n");
      err = COL_ERROR_SYNCINIT;
#endif
      sync_java = 0;
    }

  dlflag = RTLD_NEXT;
  if (init_pthread_mutex_lock (dlflag) == 0)
    {
      /* We are probably dlopened after libthread/libc,
       * try to search in the previously loaded objects
       */
      dlflag = RTLD_DEFAULT;
      if (init_pthread_mutex_lock (dlflag) == 0)
	{
	  CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_mutex_lock\n");
	  err = COL_ERROR_SYNCINIT;
	}
    }

  if ((ptr = dlvsym (dlflag, "pthread_mutex_unlock", "GLIBC_2.17")) != NULL)
    __real_pthread_mutex_unlock = ptr;
  else if ((ptr = dlvsym (dlflag, "pthread_mutex_unlock", "GLIBC_2.2.5")) != NULL)
    __real_pthread_mutex_unlock = ptr;
  else if ((ptr = dlvsym (dlflag, "pthread_mutex_unlock", "GLIBC_2.0")) != NULL)
    __real_pthread_mutex_unlock = ptr;
  else
    __real_pthread_mutex_unlock = dlsym (dlflag, "pthread_mutex_unlock");
  if (__real_pthread_mutex_unlock == NULL)
    {
      CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_mutex_unlock\n");
      err = COL_ERROR_SYNCINIT;
    }

  __real_pthread_cond_wait_2_17 = dlvsym (dlflag, "pthread_cond_wait", "GLIBC_2.17");
  __real_pthread_cond_wait_2_3_2 = dlvsym (dlflag, "pthread_cond_wait", "GLIBC_2.3.2");
  __real_pthread_cond_wait_2_2_5 = dlvsym (dlflag, "pthread_cond_wait", "GLIBC_2.2.5");
  __real_pthread_cond_wait_2_0 = dlvsym (dlflag, "pthread_cond_wait", "GLIBC_2.0");
  if (__real_pthread_cond_wait_2_17)
    __real_pthread_cond_wait = __real_pthread_cond_wait_2_17;
  else if (__real_pthread_cond_wait_2_3_2)
    __real_pthread_cond_wait = __real_pthread_cond_wait_2_3_2;
  else if (__real_pthread_cond_wait_2_2_5)
    __real_pthread_cond_wait = __real_pthread_cond_wait_2_2_5;
  else if (__real_pthread_cond_wait_2_0)
    __real_pthread_cond_wait = __real_pthread_cond_wait_2_0;
  else
    __real_pthread_cond_wait = dlsym (dlflag, "pthread_cond_wait");
  if (__real_pthread_cond_wait == NULL)
    {
      CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_cond_wait\n");
      err = COL_ERROR_SYNCINIT;
    }

  __real_pthread_cond_timedwait_2_17 = dlvsym (dlflag, "pthread_cond_timedwait", "GLIBC_2.17");
  __real_pthread_cond_timedwait_2_3_2 = dlvsym (dlflag, "pthread_cond_timedwait", "GLIBC_2.3.2");
  __real_pthread_cond_timedwait_2_2_5 = dlvsym (dlflag, "pthread_cond_timedwait", "GLIBC_2.2.5");
  __real_pthread_cond_timedwait_2_0 = dlvsym (dlflag, "pthread_cond_timedwait", "GLIBC_2.0");
  if (__real_pthread_cond_timedwait_2_17)
    __real_pthread_cond_timedwait = __real_pthread_cond_timedwait_2_17;
  else if (__real_pthread_cond_timedwait_2_3_2)
    __real_pthread_cond_timedwait = __real_pthread_cond_timedwait_2_3_2;
  else if (__real_pthread_cond_timedwait_2_2_5)
    __real_pthread_cond_timedwait = __real_pthread_cond_timedwait_2_2_5;
  else if (__real_pthread_cond_timedwait_2_0)
    __real_pthread_cond_timedwait = __real_pthread_cond_timedwait_2_0;
  else
    __real_pthread_cond_timedwait = dlsym (dlflag, "pthread_cond_timedwait");
  if (__real_pthread_cond_timedwait == NULL)
    {
      CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_cond_timedwait\n");
      err = COL_ERROR_SYNCINIT;
    }

  __real_pthread_join_2_34 = dlvsym (dlflag, "pthread_join", "GLIBC_2.34");
  __real_pthread_join_2_17 = dlvsym (dlflag, "pthread_join", "GLIBC_2.17");
  __real_pthread_join_2_2_5 = dlvsym (dlflag, "pthread_join", "GLIBC_2.2.5");
  __real_pthread_join_2_0 = dlvsym (dlflag, "pthread_join", "GLIBC_2.0");
  if (__real_pthread_join_2_34)
    __real_pthread_join = __real_pthread_join_2_34;
  else if (__real_pthread_join_2_17)
    __real_pthread_join = __real_pthread_join_2_17;
  else if (__real_pthread_join_2_2_5)
    __real_pthread_join = __real_pthread_join_2_2_5;
  else if (__real_pthread_join_2_0)
    __real_pthread_join = __real_pthread_join_2_0;
  else
    __real_pthread_join = dlsym (dlflag, "pthread_join");
  if (__real_pthread_join == NULL)
    {
      CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT pthread_join\n");
      err = COL_ERROR_SYNCINIT;
    }

  __real_sem_wait_2_34 = dlvsym (dlflag, "sem_wait", "GLIBC_2.34");
  __real_sem_wait_2_17 = dlvsym (dlflag, "sem_wait", "GLIBC_2.17");
  __real_sem_wait_2_2_5 = dlvsym (dlflag, "sem_wait", "GLIBC_2.2.5");
  __real_sem_wait_2_1 = dlvsym (dlflag, "sem_wait", "GLIBC_2.1");
  __real_sem_wait_2_0 = dlvsym (dlflag, "sem_wait", "GLIBC_2.0");
  if (__real_sem_wait_2_34)
    __real_sem_wait = __real_sem_wait_2_34;
  else if (__real_sem_wait_2_17)
    __real_sem_wait = __real_sem_wait_2_17;
  else if (__real_sem_wait_2_2_5)
    __real_sem_wait = __real_sem_wait_2_2_5;
  else if (__real_sem_wait_2_1)
    __real_sem_wait = __real_sem_wait_2_1;
  else if (__real_sem_wait_2_0)
    __real_sem_wait = __real_sem_wait_2_0;
  else
    __real_sem_wait = dlsym (dlflag, "sem_wait");
  if (__real_sem_wait == NULL)
    {
      CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT sem_wait\n");
      err = COL_ERROR_SYNCINIT;
    }


  ptr = dlsym (dlflag, "strtol");
  if (ptr)
    __real_strtol = (void *) ptr;
  else
    {
      CALL_REAL (fprintf)(stderr, "synctrace_init COL_ERROR_SYNCINIT strtol\n");
      err = COL_ERROR_SYNCINIT;
    }
  init_thread_intf_finished++;
  TprintfT (0, "synctrace init_thread_intf complete\n");
  return err;
}

/* These next two routines are used from jprofile to record Java synctrace data */
void
__collector_jsync_begin ()
{
  int *guard;
  if (CHCK_JREENTRANCE (guard))
    {
      Tprintf (DBG_LT1, "__collector_jsync_begin: skipped\n");
      return;
    }
  Tprintf (DBG_LT1, "__collector_jsync_begin: start event\n");
  PUSH_REENTRANCE (guard);
}

void
__collector_jsync_end (hrtime_t reqt, void *object)
{
  int *guard;
  if (RECHCK_JREENTRANCE (guard))
    {
      Tprintf (DBG_LT1, "__collector_jsync_end: skipped\n");
      return;
    }
  hrtime_t grnt = gethrtime ();
  if (grnt - reqt >= sync_threshold)
    {
      Sync_packet spacket = spacket_0;
      spacket.comm.tstamp = grnt;
      spacket.requested = reqt;
      spacket.objp = (intptr_t) object;
      spacket.comm.frinfo = collector_interface->getFrameInfo (sync_hndl,
			spacket.comm.tstamp, FRINFO_FROM_STACK_ARG, &spacket);
      collector_interface->writeDataRecord (sync_hndl, (Common_packet*) &spacket);
    }
  Tprintf (DBG_LT1, "__collector_jsync_begin: end event\n");
  POP_REENTRANCE (guard);
}
/*-------------------------------------------------------- pthread_mutex_lock */
static int
gprofng_pthread_mutex_lock (int (real_func) (pthread_mutex_t *),
			    pthread_mutex_t *mp)
{
  int *guard;
  if (CHCK_NREENTRANCE (guard))
    return (real_func) (mp);
  PUSH_REENTRANCE (guard);
  hrtime_t reqt = gethrtime ();
  int ret = (real_func) (mp);
  if (RECHCK_NREENTRANCE (guard))
    {
      POP_REENTRANCE (guard);
      return ret;
    }
  hrtime_t grnt = gethrtime ();
  if (grnt - reqt >= sync_threshold)
    {
      Sync_packet spacket = spacket_0;
      spacket.comm.tstamp = grnt;
      spacket.requested = reqt;
      spacket.objp = (intptr_t) mp;
      spacket.comm.frinfo = collector_interface->getFrameInfo (sync_hndl,
			spacket.comm.tstamp, FRINFO_FROM_STACK_ARG, &spacket);
      collector_interface->writeDataRecord (sync_hndl, (Common_packet*) &spacket);
    }
  POP_REENTRANCE (guard);
  return ret;
}

#define DCL_PTHREAD_MUTEX_LOCK(dcl_f) \
  int dcl_f (pthread_mutex_t *mp) \
  { \
    if (__real_pthread_mutex_lock == NULL) \
      init_thread_intf (); \
    return gprofng_pthread_mutex_lock (__real_pthread_mutex_lock, mp); \
  }

DCL_FUNC_VER (DCL_PTHREAD_MUTEX_LOCK, pthread_mutex_lock_2_17, pthread_mutex_lock@GLIBC_2.17)
DCL_FUNC_VER (DCL_PTHREAD_MUTEX_LOCK, pthread_mutex_lock_2_2_5, pthread_mutex_lock@GLIBC_2.2.5)
DCL_FUNC_VER (DCL_PTHREAD_MUTEX_LOCK, pthread_mutex_lock_2_0, pthread_mutex_lock@GLIBC_2.0)
DCL_PTHREAD_MUTEX_LOCK (pthread_mutex_lock)

/*------------------------------------------------------------- pthread_cond_wait */
static int
gprofng_pthread_cond_wait (int(real_func) (pthread_cond_t *, pthread_mutex_t *),
			   pthread_cond_t *cond, pthread_mutex_t *mutex)
{
  int *guard;
  if (CHCK_NREENTRANCE (guard))
    return (real_func) (cond, mutex);
  PUSH_REENTRANCE (guard);
  hrtime_t reqt = gethrtime ();
  int ret = -1;
  ret = (real_func) (cond, mutex);
  if (RECHCK_NREENTRANCE (guard))
    {
      POP_REENTRANCE (guard);
      return ret;
    }
  hrtime_t grnt = gethrtime ();
  if (grnt - reqt >= sync_threshold)
    {
      Sync_packet spacket = spacket_0;
      spacket.comm.tstamp = grnt;
      spacket.requested = reqt;
      spacket.objp = (intptr_t) mutex;
      spacket.comm.frinfo = collector_interface->getFrameInfo (sync_hndl,
			spacket.comm.tstamp, FRINFO_FROM_STACK_ARG, &spacket);
      collector_interface->writeDataRecord (sync_hndl, (Common_packet*) &spacket);
    }
  POP_REENTRANCE (guard);
  return ret;
}

#define DCL_PTHREAD_COND_WAIT(dcl_f) \
  int dcl_f (pthread_cond_t *cond, pthread_mutex_t *mutex) \
  { \
    if (__real_pthread_cond_wait == NULL) \
      init_thread_intf (); \
    return gprofng_pthread_cond_wait (__real_pthread_cond_wait, cond, mutex); \
  }

DCL_FUNC_VER (DCL_PTHREAD_COND_WAIT, pthread_cond_wait_2_17, pthread_cond_wait@GLIBC_2.17)
DCL_FUNC_VER (DCL_PTHREAD_COND_WAIT, pthread_cond_wait_2_3_2, pthread_cond_wait@GLIBC_2.3.2)
DCL_FUNC_VER (DCL_PTHREAD_COND_WAIT, pthread_cond_wait_2_2_5, pthread_cond_wait@GLIBC_2.2.5)
DCL_FUNC_VER (DCL_PTHREAD_COND_WAIT, pthread_cond_wait_2_0, pthread_cond_wait@GLIBC_2.0)
DCL_PTHREAD_COND_WAIT (pthread_cond_wait)

/*---------------------------------------------------- pthread_cond_timedwait */
static int
gprofng_pthread_cond_timedwait (int(real_func) (pthread_cond_t *,
				    pthread_mutex_t*, const struct timespec *),
				pthread_cond_t *cond, pthread_mutex_t *mutex,
				const struct timespec *abstime)
{
  int *guard;
  if (CHCK_NREENTRANCE (guard))
    return (real_func) (cond, mutex, abstime);
  PUSH_REENTRANCE (guard);
  hrtime_t reqt = gethrtime ();
  int ret = -1;
  ret = (real_func) (cond, mutex, abstime);
  if (RECHCK_NREENTRANCE (guard))
    {
      POP_REENTRANCE (guard);
      return ret;
    }
  hrtime_t grnt = gethrtime ();
  if (grnt - reqt >= sync_threshold)
    {
      Sync_packet spacket = spacket_0;
      spacket.comm.tstamp = grnt;
      spacket.requested = reqt;
      spacket.objp = (intptr_t) mutex;
      spacket.comm.frinfo = collector_interface->getFrameInfo (sync_hndl,
			spacket.comm.tstamp, FRINFO_FROM_STACK_ARG, &spacket);
      collector_interface->writeDataRecord (sync_hndl, (Common_packet*) &spacket);
    }
  POP_REENTRANCE (guard);
  return ret;
}

#define DCL_PTHREAD_COND_TIMEDWAIT(dcl_f) \
  int dcl_f (pthread_cond_t *cond, pthread_mutex_t *mutex, \
	     const struct timespec *abstime) \
  { \
    if (__real_pthread_cond_timedwait == NULL) \
      init_thread_intf (); \
    return gprofng_pthread_cond_timedwait (__real_pthread_cond_timedwait, cond, mutex, abstime); \
  }

DCL_FUNC_VER (DCL_PTHREAD_COND_TIMEDWAIT, pthread_cond_timedwait_2_17, pthread_cond_timedwait@GLIBC_2.17)
DCL_FUNC_VER (DCL_PTHREAD_COND_TIMEDWAIT, pthread_cond_timedwait_2_3_2, pthread_cond_timedwait@GLIBC_2.3.2)
DCL_FUNC_VER (DCL_PTHREAD_COND_TIMEDWAIT, pthread_cond_timedwait_2_2_5, pthread_cond_timedwait@GLIBC_2.2.5)
DCL_FUNC_VER (DCL_PTHREAD_COND_TIMEDWAIT, pthread_cond_timedwait_2_0, pthread_cond_timedwait@GLIBC_2.0)
DCL_PTHREAD_COND_TIMEDWAIT (pthread_cond_timedwait)


/*------------------------------------------------------------- pthread_join */
static int
gprofng_pthread_join (int(real_func) (pthread_t, void **),
		      pthread_t target_thread, void **status)
{
  int *guard;
  if (CHCK_NREENTRANCE (guard))
    return real_func (target_thread, status);
  PUSH_REENTRANCE (guard);
  hrtime_t reqt = gethrtime ();
  int ret = real_func(target_thread, status);
  if (RECHCK_NREENTRANCE (guard))
    {
      POP_REENTRANCE (guard);
      return ret;
    }
  hrtime_t grnt = gethrtime ();
  if (grnt - reqt >= sync_threshold)
    {
      Sync_packet spacket = spacket_0;
      spacket.comm.tstamp = grnt;
      spacket.requested = reqt;
      spacket.objp = (Vaddr_type) target_thread;
      spacket.comm.frinfo = collector_interface->getFrameInfo (sync_hndl,
			spacket.comm.tstamp, FRINFO_FROM_STACK_ARG, &spacket);
      collector_interface->writeDataRecord (sync_hndl, (Common_packet*) &spacket);
    }
  POP_REENTRANCE (guard);
  return ret;
}

#define DCL_PTHREAD_JOIN(dcl_f) \
  int dcl_f (pthread_t target_thread, void **status) \
  { \
    if (__real_pthread_join == NULL) \
      init_thread_intf (); \
    return gprofng_pthread_join (__real_pthread_join, target_thread, status); \
  }

DCL_FUNC_VER (DCL_PTHREAD_JOIN, pthread_join_2_34, pthread_join@GLIBC_2.34)
DCL_FUNC_VER (DCL_PTHREAD_JOIN, pthread_join_2_17, pthread_join@GLIBC_2.17)
DCL_FUNC_VER (DCL_PTHREAD_JOIN, pthread_join_2_2_5, pthread_join@GLIBC_2.2.5)
DCL_FUNC_VER (DCL_PTHREAD_JOIN, pthread_join_2_0, pthread_join@GLIBC_2.0)
DCL_PTHREAD_JOIN (pthread_join)

/*------------------------------------------------------------- sem_wait */
static int
gprofng_sem_wait (int (real_func) (sem_t *), sem_t *sp)
{
  int *guard;
  if (CHCK_NREENTRANCE (guard))
    return real_func (sp);
  PUSH_REENTRANCE (guard);
  hrtime_t reqt = gethrtime ();
  int ret = -1;
  ret = real_func (sp);
  if (RECHCK_NREENTRANCE (guard))
    {
      POP_REENTRANCE (guard);
      return ret;
    }
  hrtime_t grnt = gethrtime ();
  if (grnt - reqt >= sync_threshold)
    {
      Sync_packet spacket = spacket_0;
      spacket.comm.tstamp = grnt;
      spacket.requested = reqt;
      spacket.objp = (intptr_t) sp;
      spacket.comm.frinfo = collector_interface->getFrameInfo (sync_hndl,
			spacket.comm.tstamp, FRINFO_FROM_STACK_ARG, &spacket);
      collector_interface->writeDataRecord (sync_hndl, (Common_packet*) &spacket);
    }
  POP_REENTRANCE (guard);
  return ret;
}

#define DCL_SEM_WAIT(dcl_f) \
  int dcl_f (sem_t *sp) \
  { \
    if (__real_sem_wait == NULL) \
      init_thread_intf (); \
    return gprofng_sem_wait (__real_sem_wait, sp); \
  }

DCL_FUNC_VER (DCL_SEM_WAIT, sem_wait_2_34, sem_wait@GLIBC_2.34)
DCL_FUNC_VER (DCL_SEM_WAIT, sem_wait_2_17, sem_wait@GLIBC_2.17)
DCL_FUNC_VER (DCL_SEM_WAIT, sem_wait_2_2_5, sem_wait@GLIBC_2.2.5)
DCL_FUNC_VER (DCL_SEM_WAIT, sem_wait_2_0, sem_wait@GLIBC_2.0)
DCL_FUNC_VER (DCL_SEM_WAIT, sem_wait_2_1, sem_wait@GLIBC_2.1)
DCL_SEM_WAIT (sem_wait)
