/* mergecompletely offset reads size

   Purpose: Test caching and tlb-limitations

   Try to merge an area of size bytes completely.

   Then, perform bytereads/size many reads on it.

   This example shows that m_merge can be used independently of the
   two other calls, if we know what we want.

*/

#include <stdlib.h>
#include <malloc.h>
#include <stdio.h>
#include <mmlib.h>
#include <sys/times.h>
#include <unistd.h>

/*
  mergetwo pid1 pid2
*/

#define ERROREXIT(msg) ( fprintf(stderr,"%s:%d: %s\n",__FILE__,__LINE__,msg), exit(1) )

static time_t lastusertimems, lastsystemtimems;
static time_t
rep(void)
{
  struct tms buf;
  time_t udiffms, sdiffms;

  times(&buf);
  udiffms = buf.tms_utime-lastusertimems;
  sdiffms = buf.tms_stime-lastsystemtimems;
  printf("delta s=%ldms, u=%ldms: ",sdiffms,udiffms);
  lastusertimems = buf.tms_utime;
  lastsystemtimems = buf.tms_stime;
  return udiffms;
}

#define vtalloc(nel, type)  ((type*)valloc(nel*sizeof(type)))
#define talloc(nel, type)  ((type*)calloc(nel,sizeof(type)))

size_t
readthrough ( unsigned char * area, size_t offset, size_t reads, size_t size )
{
  size_t sum = 0, i, j = 0;

  for (i = 0; i < reads; i++)
    {
      sum += area[j];
      j+= offset;
      if ( j > size )
	j = 0;
    }
  return sum;
}

void dosimpleioctl(void); /* semiexported */

int
main(const int argc, const char * const argv[])
{
  pid_t pid;
  int retval;
  size_t offset, reads, size, i, nel, suma, sumb;
  unsigned char *area;
  const unsigned char *page;
  MEQUALPAIR *eps;
  MPAGEINFO *is;
  time_t ta, tb;

  if (argc != 4)
    ERROREXIT("mergecompletely <offset> <reads> <size>");

  offset = atol(argv[1]);
  reads = atol(argv[2]);
  size = atol(argv[3]);

  rep(),printf("This is mergecompletely testing %.2fMb of memory\n",
	       ((float)size)/(1024*1024));

  
  area = vtalloc(size,unsigned char);
  if (area == NULL)
    ERROREXIT("no more memory");

  rep(),printf("initializing memory...\n");
  for (i = 0; i < size; i++)
    area[i] = 77;
  rep(),printf("now reading tru...\n");
  suma = readthrough(area, offset, reads, size);
  ta = rep(),printf("read\n");

  
  eps = talloc(size/offset+1, MEQUALPAIR);
  is = talloc(size/offset+1, MPAGEINFO);

  if (eps == NULL)
    ERROREXIT("no more memory");

  pid = getpid();

  /* now merge the pages, assuming that offset is the size to merge */
  i = 0;
  {
    const void * lastdest = area;
    for ( page = area+offset; page < area+size; page+=offset )
      {
	const int goodmerge = (1==1);

	eps[i].spid = eps[i].dpid = pid;
	is[i].v_addr = page;

	if (goodmerge)
	  {
	    eps[i].sv_addr = page;
	    eps[i].dv_addr = lastdest;
	    lastdest = page;
	    /* as long as all merges are succesfully completed, this
	       last assignment is not strictly needed.  But it is more
	       robust, in the case some pages are not equal!	       
	    */
	  }
	else
	  { /* This will merge a single page only!  Moving it from
	       mapping to mapping
	    */
	    eps[i].sv_addr = lastdest;
	    eps[i].dv_addr = page;
	  }
	i++;
      }
  }
  nel = i;

  rep(),printf("now built in hashing ...\n");
  retval = m_hashpages(pid, is,nel, NULL);
  if ( retval == - 1 )
    ERROREXIT(strerror(errno));
  rep(),printf("and now library hashing ...\n");
  retval = m_hashpages(pid, is,nel, m_libhash_addrothalf);
  if ( retval == -1 )
    ERROREXIT(strerror(errno));
  rep(),printf("again built in hashing ...\n");
  retval = m_hashpages(pid, is,nel, NULL);
  if ( retval == - 1 )
    ERROREXIT(strerror(errno));
  rep(),printf("now issuing m_merge...\n");
  retval = m_merge(eps, nel);
  if  (retval == -1)
    ERROREXIT(strerror(errno));
  rep();printf("m_merge merged %d pages from %d requests...\n",retval,nel);
  if (retval != nel)
    {
      for (i = 0 ; i < nel; i++)
	if (eps[i].status != 0)
	  {
	    printf("eps[%d] == { %d, %p, %d, %p, %d }\n",
		   i,
		   eps[i].spid,
		   eps[i].sv_addr,
		   eps[i].dpid,
		   eps[i].dv_addr,
		   eps[i].status
		   );
	    if ( i == nel -1)
	      printf("The last page was not merged, but that's ok\n");
	  }
    }
  

  rep(),printf("now reading tru...\n");
  sumb = readthrough(area, offset, reads, size);
  tb = rep(),printf("read\n");

  if (suma != sumb)
    ERROREXIT("the sums are not equal!");
  if (1==2)
    {
      rep(),printf("now sleeping so you can check my memory\n");
      sleep(10);
    }

  rep(),printf("now built in hashing ...\n");
  retval = m_hashpages(pid, is,nel, NULL);
  if ( retval == - 1 )
    ERROREXIT(strerror(errno));
  rep(),printf("and now library hashing ...\n");
  retval = m_hashpages(pid, is,nel, m_libhash_addrothalf);
  if ( retval == -1 )
    ERROREXIT(strerror(errno));
  rep(),printf("and now stupid library hashing ...\n");
  retval = m_hashpages(pid, is,nel, m_libhash_const);
  if ( retval == -1 )
    ERROREXIT(strerror(errno));
  rep(),printf("again built in hashing ...\n");
  retval = m_hashpages(pid, is,nel, NULL);
  if ( retval == - 1 )
    ERROREXIT(strerror(errno));


  rep(),printf("now issuing m_merge a second time...\n");
  retval = m_merge(eps, nel);
  if  (retval == -1)
    ERROREXIT(strerror(errno));
  rep();printf("m_merge merged %d pages from %d requests...\n",retval,nel);

  nel *= 10;
  rep(),printf("now issuing %d ioctls\n", nel);
  for (i = 0; i < nel; i++)
    dosimpleioctl();
  rep(),printf("now issuing %d getpids\n", nel);
  for (i = 0; i < nel; i++)
    getpid();
  rep(),printf("finished\n");
  {
    float speedup = (float)ta/(float)tb;
    printf("speedup = %ldms/%ldms = %.2f, ", ta, tb, speedup);
    if ( speedup < 1.02 )
      printf("naa - this wasn't fine, maybe better sequence for m_merge or tlb problems\n");
    else if ( speedup > 1.20 )
      printf("yeah! That's what mmlib is for!\n");
    else
      printf("could be better, maybe we are in this tlb trashing\n");
  }
    
  return 0;
}
