/******************************************************************************
*                                                                             *
*   Copyright 2005 University of Cambridge Computer Laboratory.               *
*                                                                             *
*   This file is part of Nprobe.                                              *
*                                                                             *
*   Nprobe 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 2 of the License, or         *
*   (at your option) any later version.                                       *
*                                                                             *
*   Nprobe 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 Nprobe; if not, write to the Free Software                     *
*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *
*                                                                             *
******************************************************************************/


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>

#include "list.h"
#include "malloc_rec.h"

#ifdef REC_MALLOC

#define NBKTS 16*16384
#define HASHMOD 16381

#undef malloc
#undef free

struct mrec
{
  list_t hlist;
  void *addr;
  int size;
  int gen;
#ifdef REC_MALLOC_MED
  char *what;
#endif
};

typedef struct mrec mrec_t;


static listhdr_t mrec_pool;
static listhdr_t mrecs[NBKTS];

#define NWBKTS 1024
#define WHASHMOD 1021
struct whatrec 
{
  list_t hlist;
  char *what;
};


typedef struct whatrec whatrec_t;

static listhdr_t whatrecs[NWBKTS];

static void 
init_whatrecs(void)
{
  int i;
  
  for (i = 0; i < NWBKTS; i++)
    L_INIT(&whatrecs[i]); 
}

static int 
hashwhat(char *what)
{
  int l = strlen(what);
  int ind;
  
  while( l > sizeof(int))
    {
      ind ^= *((int *)what);
      what += sizeof(int);
      l -= sizeof(int);
    }
  while (l)
    {
      ind ^= (int)*what;
      l -= 1;
      what += 1;    
    }

  return ind % WHASHMOD;
}
  

static char 
*getwhat(char* what)
{
  whatrec_t *wp;
  char *nwhat;
  listhdr_t *bkt = &whatrecs[hashwhat(what)];
  int found = 0;
  
  L_WALK(wp, bkt, hlist, whatrec_t)
    {
      if (!strcmp(what, wp->what))
	{
	  found = 1;
	  break;
	}
    }
  
  if (!found)
    {
      if ((wp = (whatrec_t *)malloc(sizeof(whatrec_t))) ==NULL)
	{
	  perror("whatrec malloc()");
	  exit(1);
	}
      if ((nwhat = (char *)malloc(strlen(what))) ==NULL)
	{
	  perror("whatrec char malloc()");
	  exit(1);
	}

      strcpy(nwhat, what);
      wp->what = nwhat;
    }
  
  return wp->what;
}

      

  

static int started = 0;
static int nmallocs = 0;
static int nfrees = 0;
static int mused = 0;


void report_rec_malloc_full(void); /* forward */

static void 
mrec_pool_init(int n, int init)
{
  int i;
  mrec_t *mrp;

  if (init)
    fprintf(stderr, "initialising %d mrecs ", n);

  if ((mrp = (mrec_t *)malloc(n * sizeof(mrec_t))) 
      == NULL)
    perror("mrec_pool_init");

  L_INIT(&mrec_pool);

  for (i = 0; i < n; i++)
    {
      L_INS_TAIL(&mrec_pool, &mrp[i], hlist, mrec_t);
      if (init && !(i%(n/20)))
	fprintf(stderr, ".");
    }

  if (init)
    fprintf(stderr, "\n");

  return;
}

static mrec_t 
*get_mrec(void)
{
  mrec_t *mrp;

  if (L_EMPTY(&mrec_pool))
    {
      //fprintf(stderr, "Initialising another %d mrecs in pool\n", NBKTS/2);
      mrec_pool_init(NBKTS/2, 0);
    }

  mrp = (mrec_t *)mrec_pool.lnext;
  L_REM(&mrec_pool, mrp, hlist, mrec_t);

  mrp->gen = 0;
  
  return mrp;
}

static void 
recycle_mrec(mrec_t *mrp)
{
  L_INS_HEAD(&mrec_pool, mrp, hlist, mrec_t);

  return;
}


static void 
hash_init(void)
{
  int i;

  started = 1;
  for (i=0; i <NBKTS; i++)
    L_INIT(&mrecs[i]);

  mrec_pool_init(2*NBKTS, 1);

#ifdef REC_MALLOC_MED
  init_whatrecs();
#endif
  return;
}


#ifdef REC_MALLOC_MED
void *
_rec_malloc(size_t sz, char *what)
#elif defined REC_MALLOC_MAX
void *
_rec_malloc(size_t sz, char *what, int line, char *file)
#else
void *
_rec_malloc(size_t sz)
#endif

{
  list_t *bkt;
  mrec_t *mrecp;
  void *ad;

#ifdef REC_MALLOC_MED
  //printf("%s = %d\n", what, sz);
#elif defined REC_MALLOC_MAX
  printf("%s = %d %s:%d\n", what, sz, file, line);
  //#else
  //  printf("rec_malloc called\n");
#endif
  
  if (!started)
    hash_init();
  
  nmallocs += 1;
  mused += sz;

  ad = malloc(sz);
  if (ad == NULL)
    return NULL;

  mrecp = get_mrec();
  mrecp->addr = ad;
  mrecp->size = sz;
#ifdef REC_MALLOC_MED
  mrecp->what = getwhat(what);
#endif
  
  bkt = &mrecs[(int)ad%HASHMOD];
  L_INS_TAIL(bkt, mrecp, hlist, mrec_t);
  
  return ad;
}

void 
_rec_free(void *addr)
{
  int found = 0;
  list_t *bkt;
  mrec_t *mrecp;

  if (!started)
    {
      fprintf(stderr, "_rec_free(): freeing address %#x before first allocated\n", (int)addr);
      exit(1);
    }

  //if (!(nfrees%100000))
  //report_rec_malloc_full();

  //fprintf (stderr, "_rec_free %#x\n", (int)addr);

  bkt = &mrecs[(int)addr%HASHMOD];
  L_WALK(mrecp, bkt, hlist, mrec_t)
    {
      if (mrecp->addr == addr)
	{
	  found = 1;
	  break;
	  
	}
    }
      
  if (!found)
    {
      fprintf(stderr, "_rec_free(): freeing address %#x before allocated\n", 
	      (int)addr);
      exit(1);
    }
  else
    {

      free(addr);
      nfrees += 1;
      mused -= mrecp->size;
      L_REM(bkt, mrecp, hlist, mrec_t);
      recycle_mrec(mrecp);
    }
  
  return;
  
}

void 
report_rec_malloc(void)
{
  fprintf(stderr, "%d mallocs %d frees %d used\n\n", 
	  nmallocs, nfrees, mused);
}

#define NSB 2048
#define NSBMOD 2039

struct msize_rec 
{
  list_t hlist;
  int size;
  int num;
  int gen;
#ifdef REC_MALLOC_MED
  char *what;
#endif
};
typedef struct msize_rec ms_t;

int 
sortfn(const void *aa, const void *bb)
{
  ms_t **ap = aa;
  ms_t **bp = bb;
  ms_t *a = *ap;
  ms_t *b = *bp;
  
  return (b->num*b->size) 
    - (a->num*a->size);
}

void 
report_rec_malloc_full(void)
{
  int i, j;
  int found;
  int gen;
  list_t *bkt, *sbkt;
  mrec_t *mrecp;
  ms_t *mstp, **mstpp;
  int sz;
  int nsizes;
  
  listhdr_t sizes[NSB];

  int szrep = 0;

  struct rusage ru;
   
  for (i=0; i <NSB; i++)
    L_INIT(&sizes[i]);

  nsizes = 0;
  for (i=0; i <NBKTS; i++)
    {
      bkt = &mrecs[i];
      L_WALK(mrecp, bkt, hlist, mrec_t)
	{
	  sz = mrecp->size;
	  gen = mrecp->gen;
	  mrecp->gen += 1;
	  sbkt = &sizes[sz%NSBMOD];
	  found = 0;
	  L_WALK(mstp, sbkt, hlist, ms_t)
	    {
#ifdef REC_MALLOC_MED
	      if(mstp->size == sz && mstp->gen == gen && !strcmp(mstp->what, mrecp->what))
#else
	      if(mstp->size == sz && mstp->gen == gen)
#endif
		{
		  found = 1;
		  break;
		}
	    }
	  if (found)
	    {
	      mstp->num += 1;
	    }
	  else
	    {
	      if ((mstp = (ms_t *)malloc(sizeof(ms_t))) == NULL)
		{
		  perror("report_rec_malloc_full: malloc");
		  exit(1);
		}
	      mstp->size = sz;
	      mstp->gen = gen;
	      mstp->num = 1;
#ifdef REC_MALLOC_MED
	      mstp->what = mrecp->what;
#endif
	      L_INS_TAIL(sbkt, mstp, hlist, ms_t);
	      nsizes += 1;
	    }
	}
    }

  if ((mstpp = (ms_t **)malloc(nsizes*sizeof(ms_t *))) == NULL)
    {
      perror("report_rec_malloc_full: malloc2");
      exit(1);
    }

  j = 0;
  for (i=0; i <NSB; i++)
    {
      sbkt = &sizes[i];
      L_WALK(mstp, sbkt, hlist, ms_t)
	if (mstp->num)
	  mstpp[j++] = mstp;
    }
  
  qsort(mstpp, nsizes, sizeof(ms_t *), sortfn);

  fprintf(stderr, "%d mallocs %d frees %.2fM total used\n", 
	  nmallocs, nfrees, mused/1000000.0);
  fprintf(stderr, "Top 75+%%:\n");
  for (i = 0; i < nsizes; i++)
    {
      ms_t *msp = mstpp[i];
      szrep += msp->num * msp->size;
#ifdef REC_MALLOC_MED
      fprintf(stderr, "\t%d of size %d = %.2fM gen=%d  %s running tot %.2fM\n", 
	      msp->num, msp->size, 
	     (msp->num * msp->size)/1000000.0, msp->gen, msp->what, szrep/1000000.0);
#else
      fprintf(stderr, "\t%d of size %d = %.2fM gen %d  running tot %.2fM\n", 
	      msp->num, msp->size, 
	     (msp->num * msp->size)/1000000.0, msp->gen, szrep/1000000.0);
#endif
      if (szrep > (mused*3)/4)
	break;
    }

  for (i = 0; i < nsizes; i++)
    free(mstpp[i]);
  free(mstpp);

  if (getrusage(RUSAGE_SELF, &ru) != 0)
    {
      perror("malloc_rec: rusage()");
      exit(1);
    }
#if 0  
  fprintf(stderr, "mrss %d shm %d unshm %d st %d\n", ru.ru_maxrss, ru.ru_ixrss, ru.ru_idrss, ru.ru_isrss);
#endif  

}

#else /* ifdef REC_MALLOC */

void 
report_rec_malloc_full(void)
{
  return;
}


#endif /* ifdef REC_MALLOC */

/*
 * end malloc_trace.c
 */     
  
