// $Id: Profile_Timer.cpp,v 4.20 1999/04/16 13:15:16 brunsch Exp $

#define ACE_BUILD_DLL
#include "ace/Profile_Timer.h"

#if !defined (__ACE_INLINE__)
# include "ace/Profile_Timer.i"
#endif /* __ACE_INLINE__ */

ACE_RCSID(ace, Profile_Timer, "$Id: Profile_Timer.cpp,v 4.20 1999/04/16 13:15:16 brunsch Exp $")

ACE_ALLOC_HOOK_DEFINE(ACE_Profile_Timer)

#if (defined (ACE_HAS_PRUSAGE_T) || defined (ACE_HAS_GETRUSAGE)) && !defined (ACE_WIN32)

void
ACE_Profile_Timer::dump (void) const
{
  ACE_TRACE ("ACE_Profile_Timer::dump");
}

// Initialize interval timer.

ACE_Profile_Timer::ACE_Profile_Timer (void)
{
  ACE_TRACE ("ACE_Profile_Timer::ACE_Profile_Timer");
  ACE_OS::memset (&this->end_usage_, 0, sizeof this->end_usage_);
  ACE_OS::memset (&this->begin_usage_, 0, sizeof this->begin_usage_);
  ACE_OS::memset (&this->last_usage_, 0, sizeof this->last_usage_);

#  if defined (ACE_HAS_PRUSAGE_T)
  ACE_OS::memset (&this->last_usage_, 0, sizeof this->last_usage_);
  char buf[20];
  ACE_OS::sprintf (buf, "/proc/%d", ACE_OS::getpid ());

  if ((this->proc_handle_ = ACE_OS::open (buf, O_RDONLY, 0)) == -1)
    ACE_OS::perror (buf);
#  elif defined (ACE_HAS_GETRUSAGE)
  ACE_OS::memset (&this->begin_time_, 0, sizeof this->begin_time_);
  ACE_OS::memset (&this->end_time_, 0, sizeof this->end_time_);
  ACE_OS::memset (&this->last_time_, 0, sizeof this->last_time_);
#  endif /* ACE_HAS_PRUSAGE_T */
}

// Terminate the interval timer.
ACE_Profile_Timer::~ACE_Profile_Timer (void)
{
  ACE_TRACE ("ACE_Profile_Timer::~ACE_Profile_Timer");
#  if defined (ACE_HAS_PRUSAGE_T)
  if (ACE_OS::close (this->proc_handle_) == -1)
    ACE_OS::perror ("ACE_Profile_Timer::~ACE_Profile_Timer");
#  endif /* ACE_HAS_PRUSAGE_T */
}

// Return the resource utilization.

void
ACE_Profile_Timer::get_rusage (ACE_Profile_Timer::Rusage &usage)
{
  ACE_TRACE ("ACE_Profile_Timer::get_rusage");
  usage = this->end_usage_;
}

#  if defined (ACE_HAS_PRUSAGE_T)

// Compute the amount of resource utilization since the start time.

void
ACE_Profile_Timer::elapsed_rusage (ACE_Profile_Timer::Rusage &rusage)
{
  ACE_TRACE ("ACE_Profile_Timer::elapsed_rusage");
  rusage.pr_lwpid = this->end_usage_.pr_lwpid - this->last_usage_.pr_lwpid;
  rusage.pr_count = this->end_usage_.pr_count - this->last_usage_.pr_count;
  rusage.pr_minf  = this->end_usage_.pr_minf - this->last_usage_.pr_minf;
  rusage.pr_majf  = this->end_usage_.pr_majf - this->last_usage_.pr_majf;
  rusage.pr_inblk = this->end_usage_.pr_inblk - this->last_usage_.pr_inblk;
  rusage.pr_oublk = this->end_usage_.pr_oublk - this->last_usage_.pr_oublk;
  rusage.pr_msnd  = this->end_usage_.pr_msnd - this->last_usage_.pr_msnd;
  rusage.pr_mrcv  = this->end_usage_.pr_mrcv - this->last_usage_.pr_mrcv;
  rusage.pr_sigs  = this->end_usage_.pr_sigs - this->last_usage_.pr_sigs;
  this->subtract (rusage.pr_wtime, this->end_usage_.pr_wtime, this->last_usage_.pr_wtime);
  this->subtract (rusage.pr_ltime, this->end_usage_.pr_ltime, this->last_usage_.pr_ltime);
  this->subtract (rusage.pr_slptime, this->end_usage_.pr_slptime, this->last_usage_.pr_slptime);
  rusage.pr_vctx  = this->end_usage_.pr_vctx - this->last_usage_.pr_vctx;
  rusage.pr_ictx  = this->end_usage_.pr_ictx - this->last_usage_.pr_ictx;
  rusage.pr_sysc  = this->end_usage_.pr_sysc - this->last_usage_.pr_sysc;
  rusage.pr_ioch  = this->end_usage_.pr_ioch - this->last_usage_.pr_ioch;
}

// Compute the elapsed time.

void
ACE_Profile_Timer::compute_times (ACE_Elapsed_Time &et)
{
  ACE_TRACE ("ACE_Profile_Timer::compute_times");
  timespec_t td;

  ACE_Profile_Timer::Rusage &end = this->end_usage_;
  ACE_Profile_Timer::Rusage &begin = this->begin_usage_;

  this->subtract (td, end.pr_tstamp, begin.pr_tstamp);
  // Convert nanoseconds into seconds.
  et.real_time = td.tv_sec + ((double) td.tv_nsec) / ACE_ONE_SECOND_IN_NSECS;
  this->subtract (td, end.pr_utime, begin.pr_utime);
  // Convert nanoseconds into seconds.
  et.user_time = td.tv_sec + ((double) td.tv_nsec) / ACE_ONE_SECOND_IN_NSECS;
  this->subtract (td, end.pr_stime,  begin.pr_stime);
  // Convert nanoseconds into seconds.
  et.system_time = td.tv_sec + ((double) td.tv_nsec) / ACE_ONE_SECOND_IN_NSECS;
}

// Determine the difference between T1 and T2.

void
ACE_Profile_Timer::subtract (timespec_t &tdiff, timespec_t &t1, timespec_t &t0)
{
  ACE_TRACE ("ACE_Profile_Timer::subtract");
  tdiff.tv_sec  = t1.tv_sec - t0.tv_sec;
  tdiff.tv_nsec = t1.tv_nsec - t0.tv_nsec;

  // Normalize the time.

  while (tdiff.tv_nsec < 0)
    {
      tdiff.tv_sec--;
      tdiff.tv_nsec += ACE_ONE_SECOND_IN_NSECS;
    }
}

#  elif defined (ACE_HAS_GETRUSAGE)
// Compute the amount of resource utilization since the start time.

void
ACE_Profile_Timer::elapsed_rusage (ACE_Profile_Timer::Rusage &usage)
{
  ACE_TRACE ("ACE_Profile_Timer::elapsed_rusage");
#    if !defined (ACE_WIN32) && !defined (ACE_HAS_LIMITED_RUSAGE_T)
  // integral shared memory size
  usage.ru_ixrss = this->end_usage_.ru_ixrss - this->last_usage_.ru_ixrss;
  // integral unshared data "
  usage.ru_idrss = this->end_usage_.ru_idrss - this->last_usage_.ru_idrss;
  // integral unshared stack "
  usage.ru_isrss = this->end_usage_.ru_isrss - this->last_usage_.ru_isrss;
  // page reclaims - total vmfaults
  usage.ru_minflt = this->end_usage_.ru_minflt - this->last_usage_.ru_minflt;
  // page faults
  usage.ru_majflt = this->end_usage_.ru_majflt - this->last_usage_.ru_majflt;
  // swaps
  usage.ru_nswap = this->end_usage_.ru_nswap - this->last_usage_.ru_nswap;
  // block input operations
  usage.ru_inblock = this->end_usage_.ru_inblock -
                       this->last_usage_.ru_inblock;
  // block output operations
  usage.ru_oublock = this->end_usage_.ru_oublock -
                       this->last_usage_.ru_oublock;
  // messages sent
  usage.ru_msgsnd = this->end_usage_.ru_msgsnd - this->last_usage_.ru_msgsnd;
  // messages received
  usage.ru_msgrcv = this->end_usage_.ru_msgrcv - this->last_usage_.ru_msgrcv;
  // signals received
  usage.ru_nsignals = this->end_usage_.ru_nsignals -
                        this->last_usage_.ru_nsignals;
  // voluntary context switches
  usage.ru_nvcsw = this->end_usage_.ru_nvcsw - this->last_usage_.ru_nvcsw;
  // involuntary context switches
  usage.ru_nivcsw = this->end_usage_.ru_nivcsw - this->last_usage_.ru_nivcsw;
  this->subtract (usage.ru_utime, this->end_usage_.ru_utime, this->last_usage_.ru_utime);
  this->subtract (usage.ru_stime, this->end_usage_.ru_stime, this->last_usage_.ru_stime);
#    else
  ACE_UNUSED_ARG(usage);
#    endif /* ACE_WIN32 */
}

void
ACE_Profile_Timer::compute_times (ACE_Elapsed_Time &et)
{
  ACE_TRACE ("ACE_Profile_Timer::compute_times");

#    if defined (ACE_WIN32)
  ACE_Time_Value atv = this->end_time_ - this->begin_time_;
  et.real_time = atv.sec () + ((double) atv.usec ()) / ACE_ONE_SECOND_IN_USECS;

  atv = ACE_Time_Value (this->end_usage_.ru_utime)
        - ACE_Time_Value (this->begin_usage_.ru_utime);

  et.user_time = atv.sec () + ((double) atv.usec ()) / ACE_ONE_SECOND_IN_USECS;

  atv = ACE_Time_Value (this->end_usage_.ru_stime)
        - ACE_Time_Value (this->begin_usage_.ru_stime);
  et.system_time = atv.sec () + ((double) atv.usec ()) / ACE_ONE_SECOND_IN_USECS;
#    else
  timeval td;

  this->subtract (td, this->end_time_, this->begin_time_);
  et.real_time = td.tv_sec + ((double) td.tv_usec) / ACE_ONE_SECOND_IN_USECS;

  this->subtract (td, this->end_usage_.ru_utime, this->begin_usage_.ru_utime);
  et.user_time = td.tv_sec + ((double) td.tv_usec) / ACE_ONE_SECOND_IN_USECS;

  this->subtract (td, this->end_usage_.ru_stime,  this->begin_usage_.ru_stime);
  et.system_time = td.tv_sec + ((double) td.tv_usec) / ACE_ONE_SECOND_IN_USECS;
#    endif /* ACE_WIN32 */
}

// Determine the difference between T1 and T2.

void
ACE_Profile_Timer::subtract (timeval &tdiff, timeval &t1, timeval &t0)
{
  ACE_TRACE ("ACE_Profile_Timer::subtract");
  tdiff.tv_sec  = t1.tv_sec - t0.tv_sec;
  tdiff.tv_usec = t1.tv_usec - t0.tv_usec;

  // Normalize the time.

  while (tdiff.tv_usec < 0)
    {
      tdiff.tv_sec--;
      tdiff.tv_usec += ACE_ONE_SECOND_IN_USECS;
    }
}

#  endif /* ACE_HAS_PRUSAGE_T */

// Compute the amount of time that has elapsed between start and stop.

int
ACE_Profile_Timer::elapsed_time (ACE_Elapsed_Time &et)
{
  ACE_TRACE ("ACE_Profile_Timer::elapsed_time");
  this->compute_times (et);
  return 0;
}

#elif defined (ACE_WIN32) /* defined (ACE_HAS_PRUSAGE_T) || defined (ACE_HAS_GETRUSAGE) */

void
ACE_Profile_Timer::dump (void) const
{
  ACE_TRACE ("ACE_Profile_Timer::dump");
  timer_.dump ();
}

// Initialize interval timer.
ACE_Profile_Timer::ACE_Profile_Timer (void)
  : timer_ ()
{
  ACE_TRACE ("ACE_Profile_Timer::ACE_Profile_Timer");
#  if defined (ACE_HAS_GETRUSAGE)

  ACE_OS::memset (&this->end_usage_, 0, sizeof this->end_usage_);
  ACE_OS::memset (&this->begin_usage_, 0, sizeof this->begin_usage_);
  ACE_OS::memset (&this->last_usage_, 0, sizeof this->last_usage_);

  ACE_OS::memset (&this->begin_time_, 0, sizeof this->begin_time_);
  ACE_OS::memset (&this->end_time_, 0, sizeof this->end_time_);
  ACE_OS::memset (&this->last_time_, 0, sizeof this->last_time_);
#  endif /* ACE_HAS_GETRUSAGE */
}

int
ACE_Profile_Timer::elapsed_time (ACE_Elapsed_Time &et)
{
  ACE_TRACE ("ACE_Profile_Timer::elapsed_time");

  ACE_hrtime_t delta_t; // nanoseconds
  timer_.elapsed_time (delta_t);

  et.real_time = (__int64) delta_t / (double) ACE_ONE_SECOND_IN_NSECS;

#  if defined (ACE_HAS_GETRUSAGE)
  ACE_Time_Value atv = ACE_Time_Value (this->end_usage_.ru_utime)
                       - ACE_Time_Value (this->begin_usage_.ru_utime);
  et.user_time = atv.sec () + ((double) atv.usec ()) / ACE_ONE_SECOND_IN_USECS;

  atv = ACE_Time_Value (this->end_usage_.ru_stime)
        - ACE_Time_Value (this->begin_usage_.ru_stime);
  et.system_time = atv.sec () + ((double) atv.usec ()) / ACE_ONE_SECOND_IN_USECS;
#  else /* ACE_HAS_GETRUSAGE */
  et.user_time = 0;
  et.system_time = 0;
#  endif /* ACE_HAS_GETRUSAGE */

  return 0;
}

// Return the resource utilization.

void
ACE_Profile_Timer::get_rusage (ACE_Profile_Timer::Rusage &usage)
{
  ACE_TRACE ("ACE_Profile_Timer::get_rusage");
#  if defined (ACE_HAS_GETRUSAGE)
  usage = this->end_usage_;
#  else /* ACE_HAS_GETRUSAGE */
  usage = 0;
#  endif /* ACE_HAS_GETRUSAGE */
}

// Compute the amount of resource utilization since the start time.

void
ACE_Profile_Timer::elapsed_rusage (ACE_Profile_Timer::Rusage &usage)
{
  ACE_TRACE ("ACE_Profile_Timer::elapsed_rusage");

#  if defined (ACE_HAS_GETRUSAGE)
  usage.ru_utime = this->end_usage_.ru_utime - this->begin_usage_.ru_utime;
  usage.ru_stime = this->end_usage_.ru_stime - this->begin_usage_.ru_stime;
#  else /* ACE_HAS_GETRUSAGE */
  usage = 0;
#  endif /* ACE_HAS_GETRUSAGE */
}

#  if defined (ACE_HAS_GETRUSAGE)
// Determine the difference between T1 and T2.

void
ACE_Profile_Timer::subtract (timeval &tdiff, timeval &t1, timeval &t0)
{
  ACE_TRACE ("ACE_Profile_Timer::subtract");
  tdiff.tv_sec  = t1.tv_sec - t0.tv_sec;
  tdiff.tv_usec = t1.tv_usec - t0.tv_usec;

  // Normalize the time.

  while (tdiff.tv_usec < 0)
    {
      tdiff.tv_sec--;
      tdiff.tv_usec += ACE_ONE_SECOND_IN_USECS;
    }
}
#  endif /* ACE_HAS_GETRUSAGE */

#else /* defined (ACE_HAS_PRUSAGE_T) || defined (ACE_HAS_GETRUSAGE) */

void
ACE_Profile_Timer::dump (void) const
{
  ACE_TRACE ("ACE_Profile_Timer::dump");
  timer_.dump ();
}

ACE_Profile_Timer::ACE_Profile_Timer (void)
  : timer_ ()
{
  ACE_TRACE ("ACE_Profile_Timer::ACE_Profile_Timer");
}

int
ACE_Profile_Timer::elapsed_time (ACE_Elapsed_Time &et)
{
  ACE_TRACE ("ACE_Profile_Timer::elapsed_time");

#  if defined (ACE_LACKS_FLOATING_POINT)
  ACE_Time_Value delta_t; /* elapsed time will be in microseconds */
  timer_.elapsed_time (delta_t);

  // Store the time in usecs.
  et.real_time = delta_t.sec () * ACE_ONE_SECOND_IN_USECS  +
                 delta_t.usec ();
#  else  /* ! ACE_LACKS_FLOATING_POINT */
  ACE_hrtime_t delta_t; /* nanoseconds */
  timer_.elapsed_time (delta_t);

  et.real_time = delta_t / (double) ACE_ONE_SECOND_IN_NSECS;
#  endif /* ! ACE_LACKS_FLOATING_POINT */

  et.user_time = 0;
  et.system_time = 0;

  return 0;
}


#endif /* defined (ACE_HAS_PRUSAGE_T) || defined (ACE_HAS_GETRUSAGE) */
