// Profiler.cc -- runtime profiler
// 
// Author: Ian.Piumarta@INRIA.Fr
// 
// Last edited: 2000-10-19 09:30:13 by piumarta on emilia.rd.wdi.disney.com

// Shamelessly un-POSIX implementation based on SVr4-ish profil() found
// on GNU systems.  If we were real programmers we'd implement the thing for
// ourselves based on the ITIMER_PROF plus a pc-snarfing handler generated
// dynamically using the optimiser's insn output macros (since struct
// siginfo doesn't really help us on the majority of machines).
// Unfortunately we actually rather like quiche.


#undef __STRICT_ANSI__	// ISO Standard C doesn't have profil()

#include "NativeMethod.h"
#include "Profiler.h"
#include "generate.h"

#ifdef PROFILER

#include <stdio.h>
#include <stdlib.h>

#include <unistd.h>

#include <string.h>
#include <time.h>
#include <sys/types.h>

#include <sys/times.h>

insn	          *Profiler::origin=  	 0;
size_t		   Profiler::range=	 0;
Profiler::tally_t *Profiler::tallies= 	 0;
insn	          *Profiler::base=  	 0;

static unsigned onTime=     0;
static unsigned offTime=    0;
static unsigned profilTime= 0;


// ITIMER_PROF uses VIRTUAL user+system time, NOT wall-clock time!

static unsigned vMSecs(void)
{
  struct tms buf;
  times(&buf);
  return (unsigned)(( (float)buf.tms_stime + (float)buf.tms_utime)
		    / (float)CLK_TCK * 1000.0);
}


class Profile
{
public:
  static unsigned relinkTotal;
  static unsigned pvTotal;
  static unsigned siTotal;
  static unsigned ccTotal;
  static unsigned ncTotal;
  static unsigned ixTotal;
  static unsigned primTotal;
  static unsigned controlTotal;
  static unsigned activateTotal;
  static unsigned methodTotal;
  static unsigned deferTotal;

  static void clearTotals(void)
    {
      relinkTotal= pvTotal= siTotal= ccTotal= ncTotal= ixTotal=
	primTotal= controlTotal= activateTotal= methodTotal= deferTotal= 0;
    }

  inline static void addTotals(unsigned relink, unsigned ic,   unsigned ctype,
			       unsigned prim,   unsigned ctl,
			       unsigned activ,  unsigned meth, unsigned defer)
    {
      relinkTotal+= relink;
      switch (ctype)
	{
	case pvCacheType: pvTotal+= ic; break;
	case siCacheType: siTotal+= ic; break;
	case ccCacheType: ccTotal+= ic; break;
	case ncCacheType: ncTotal+= ic; break;
	case ixCacheType: ixTotal+= ic; break;
	default: fatal("so when, exactly, did you invent cache type %d?", ctype);
	}
      primTotal+= prim;
      controlTotal+= ctl;
      activateTotal+= activ;
      methodTotal+= meth;
      deferTotal+= defer;
    }

  static void reportTotals(unsigned expected, float elapsed)
    {
      unsigned _cacheTotal= pvTotal + siTotal + ccTotal + ncTotal + ixTotal;
      unsigned _primTotal= primTotal + controlTotal;
      unsigned _glueTotal= relinkTotal + _cacheTotal + _primTotal;

      unsigned _enterTotal= activateTotal;
      unsigned _execTotal=  methodTotal + deferTotal;
      unsigned _methodTotal= _enterTotal + _execTotal;

      unsigned overall=  _glueTotal + _methodTotal;

      printf("vertical profiles...\n");
      printf("\n section    time contrib sampled");
      printf("\n-------- ------- ------- -------");
#     define report(who) \
      printf("\n%8s %6.2fs %6.2f%% %6.2f%%", #who, \
	     (float)who##Total / (float)expected * elapsed, \
	     percent(who##Total, expected), percent(who##Total, overall));
      report(relink);
      report(pv);  report(si);  report(cc);  report(nc);  report(ix);
      report(prim);  report(control);
      report(activate);  report(method);  report(defer);
      printf("\n-------- ------- ------- -------");
      report(_cache);  printf(" (prologue cache time)");
      report(_prim);   printf(" (prologue primitive time)");
      report(_enter);  printf(" (activation time)");
      report(_exec);   printf(" (bytecode time)");
      printf("\n-------- ------- ------- -------");
      report(_glue);   printf(" (overall non-activated prologue time)");
      report(_method); printf(" (overall activated method time)");
#     undef report
      printf("\n\n");
    }

  NativeMethod *method;
  size_t count;

public:
  inline void init(NativeMethod *nm, size_t cnt)
    {
      method= nm;
      count= cnt;
    }

  inline int compare(const Profile &other) const
    {
      if (other.count < count) return -1;
      if (other.count > count) return  1;
      // equal counts are sorted by selector
      Symbol *pSel= method->selector();
      Symbol *qSel= other.method->selector();
      const int pSize= stSizeOf(pSel);
      const int qSize= stSizeOf(qSel);
      return strncmp((char *)&pSel->_bytes(0), (char *)&qSel->_bytes(0),
		     (pSize < qSize) ? pSize : qSize);
    }

  inline void profile(size_t observed, size_t expected, float elapsed, bool full)
    {
      method->profile(count, observed, expected, elapsed, full);
    }
};


Profile *Profiler::profiles=     0;
size_t   Profiler::profileCount= 0;
size_t   Profiler::profilesSize= 0;

unsigned Profile::relinkTotal=   0;
unsigned Profile::pvTotal=       0;
unsigned Profile::siTotal=       0;
unsigned Profile::ccTotal=       0;
unsigned Profile::ncTotal=       0;
unsigned Profile::ixTotal=       0;
unsigned Profile::primTotal=     0;
unsigned Profile::controlTotal=  0;
unsigned Profile::activateTotal= 0;
unsigned Profile::methodTotal=   0;
unsigned Profile::deferTotal=    0;


void Profiler::initialise(insn *o, size_t r)
{
  origin= o;
  range= r;
  printf("Profiler: initialised for %p + %d = %p\n", origin, range, profileLimit());
}


void Profiler::methodBase(insn *b)
{
  base= b;
  printf("Profiler: methods begin at %p\n", base);
}


size_t Profiler::total(insn *from, insn *to)
{
  const size_t min= indexOf(from);
  if (to == 0)
    return tallies[min];
  size_t n= 0;
  const size_t max= indexOf(to);
  for (size_t i= min; i < max; ++i)
    n+= tallies[i];
  return n;
}


void Profiler::allocProfiles(size_t nProfiles)
{
  if (nProfiles != 0)
    {
      profilesSize= nProfiles;
      profiles= (Profile *)calloc(nProfiles, sizeof(Profile));
      profileCount= 0;
    }
  else
    {
      free(profiles);
      profilesSize= 0;
      profileCount= 0;
    }
}


void Profiler::add(NativeMethod *nm, size_t count)
{
  if (profileCount == profilesSize)
    {
      profilesSize*= 2;
      profiles= (Profile *)realloc((void *)profiles, profilesSize * sizeof(Profile));
    }
  profiles[profileCount++].init(nm, count);
}


static int compareProfiles(const void *v, const void *w)
{
  const Profile *p= (Profile *)v;
  const Profile *q= (Profile *)w;
  return p->compare(*q);
}


void Profiler::profile(size_t observed, size_t expected, float elapsed)
{
  qsort((void *)profiles, profileCount, sizeof(Profile), compareProfiles);
  {
    for (size_t i= 0; i < profileCount; ++i)
      profiles[i].profile(observed, expected, elapsed, false);
  }
  printf("\nbasic block profiles...\n\n");
  Profile::clearTotals();
  {
    for (size_t i= 0; i < profileCount; ++i)
      profiles[i].profile(observed, expected, elapsed, true);
  }
  Profile::reportTotals(expected, elapsed);
}


void Profiler::addTotals(unsigned relink, unsigned ic,   unsigned ctype,
			 unsigned prim,   unsigned ctl,
			 unsigned activ,  unsigned meth, unsigned defer)
{
  Profile::addTotals(relink, ic, ctype, prim, ctl, activ, meth, defer);
}



// Smalltalk primitives


void Profiler::start(void)
{
  if (isInitialised())
    {
      if (!hasTallies())
	tallies= (tally_t *)calloc(tallyBytes(), 1);

      profil(tallies, tallyBytes(),
	     (int)origin,
	     65536 / sizeof(insn) * sizeof(tally_t));
      onTime= vMSecs();
      printf("Profiler: started at %.3f\n", (float)onTime / 1000.0);
    }
}


void Profiler::stop(void)
{
  if (hasTallies())
    {
      profil(0, 0, 0, 0);
      offTime= vMSecs();
      printf("Profiler: stopped at %.3f\n", (float)offTime / 1000.0);
      profilTime+= (offTime - onTime);
    }
}


void Profiler::clear(void)
{
  if (hasTallies())
    {
      memset((void *)tallies, 0, tallyBytes());
      printf("Profiler: cleared\n");
      profilTime= 0;
    }
}


void Profiler::dump(void)
{
  if (hasTallies())
    {
      size_t sticky=   total(origin, base);
      size_t dynamic=  total(base, profileLimit());
      size_t recorded= sticky + dynamic;
      size_t expected= profilTime / 10;			// 0,01 secs/sample
      float  elapsed=  (float)profilTime / 1000.0;	// seconds
      float  sampled=  elapsed * (float)recorded / (float)expected;

      printf("\nProfiler: %d samples covering %.3f (of %.3f virtual) seconds\n\n",
	     recorded, sampled, elapsed);

      allocProfiles(32);
      size_t observed= NativeMethod::profileAll();
      profile(recorded, expected, elapsed);
      allocProfiles(0);
      
      if (observed != dynamic)
	{
	  size_t missing= dynamic - observed;
	  printf("WARNING: %d samples (%.2f%%) are AWOL\n",
		 missing, percent(missing, recorded));
	}

      if (sticky != 0)
	gen_profile(expected, elapsed);

      printf("dynamic code: %6.2fs %6.2f%%\n",
	     elapsed * (float)dynamic / (float)expected,
	     percent(dynamic, expected));
      printf("static glue:  %6.2fs %6.2f%%\n",
	     elapsed * (float)sticky / (float)expected,
	     percent(sticky, expected));
      printf("static VM:    %6.2fs %6.2f%% (extrapolated)\n",
	     elapsed * (float)(expected - recorded) / (float)expected,
	     percent(expected - recorded, expected));
      printf("\n");
    }
}


#else // PROFILER

#include "Profiler.h"

class NativeMethod;

#ifndef size_t
# define size_t unsigned int
#endif


// ITIMER_PROF uses VIRTUAL user+system time, NOT wall-clock time!


static unsigned vMSecs(void)
{
  return 0;
}


class Profile
{
public:

  static void clearTotals(void)
    {
    }

  inline static void addTotals(unsigned relink, unsigned ic,   unsigned ctype,
			       unsigned prim,   unsigned ctl,
			       unsigned activ,  unsigned meth, unsigned defer)
    {
    }

  static void reportTotals(unsigned expected, float elapsed)
    {
    }

public:
  inline void init(NativeMethod *nm, size_t cnt)
    {
    }

  inline int compare(const Profile &other) const
    {
      return 0;
    }

  inline void profile(size_t observed, size_t expected, float elapsed, bool full)
    {
    }
};


void Profiler::initialise(insn *o, size_t r)
{
}


void Profiler::methodBase(insn *b)
{
}


size_t Profiler::total(insn *from, insn *to)
{
  return 0;
}


void Profiler::allocProfiles(size_t nProfiles)
{
}


void Profiler::add(NativeMethod *nm, size_t count)
{
}


static int compareProfiles(const void *v, const void *w)
{
  return 0;
}


void Profiler::profile(size_t observed, size_t expected, float elapsed)
{
}


void Profiler::addTotals(unsigned relink, unsigned ic,   unsigned ctype,
			 unsigned prim,   unsigned ctl,
			 unsigned activ,  unsigned meth, unsigned defer)
{
}



// Smalltalk primitives


void Profiler::start(void)
{
}


void Profiler::stop(void)
{
}


void Profiler::clear(void)
{
}


void Profiler::dump(void)
{
}


#endif
