/*--------------------------------------------------------------------*/
/*--- Cachetool: The cachetool interface.                ct_main.c ---*/
/*--------------------------------------------------------------------*/
/*
   This file is a tool built up in Valgrind for tracing heap allocation 
   and memory reference. An output trace file will be created and then 
   fed into a cache simulator to find out cache conflicts. 

   Copyright (C) 2005-2006 
   Vince Weaver vince _at_ csl.cornell.edu
   I-Chun Li    yohowo _at_ csl.cornell.edu

   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 2 of the
   License, 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, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307, USA.

   The GNU General Public License is contained in the file COPYING.
*/

#include "pub_tool_basics.h"        //contains the basic types and other things needed everywhere. 
                                    //also included libvex.h, a library for dynamic binary instrumentation and translation.
#include "pub_tool_libcassert.h"    //clib assert replacement
#include "pub_tool_tooliface.h"     //core/tool interface
#include "pub_tool_replacemalloc.h" //library functions replacement:malloc
#include "pub_tool_mallocfree.h"    //handle free 
#include "pub_tool_libcprint.h"     //printing 
#include "pub_tool_hashtable.h"     //Generic type for a separately-chained hash table.(for realloc)
#include "pub_tool_libcbase.h"      //VG_(memset)
#include "pub_tool_options.h"       //command line options
#include "pub_tool_libcfile.h"      //file I/O
#include "pub_tool_execontext.h"    //PC
//#include "pub_tool_stacktrace.h"

#include "trace_format.h"

/*file I/O*/ 
/*trace file 16-byte header 
  The first thing written is a 16 byte long header:
  byte0 = sizeof(long) in bytes  [ie 4 on a 32 bit machine, 8 on 64)
  byte1 = 1 if big-endian, 0 if little-endian
  byte2 = version of trace file
  byte3-byte15  reserved (0 for now) 
*/

unsigned long output[10]; 
int file_ptr;
unsigned char FileName[256];  //trace file's name

#define BUFFER_SIZE 4096

char buffer[BUFFER_SIZE];
int buffer_pointer=0;




/* Record malloc'd blocks */
VgHashTable ct_malloc_list = NULL;//initialized in void ct_pre_clo_init()

//get PC
unsigned long get_alloc_callsite(void *address);

//from /memcheck/mac_share.h
/* For malloc()/new/new[] vs. free()/delete/delete[] mismatch checking. */
typedef
   enum {
      ct_AllocMalloc = 0,
      ct_AllocNew    = 1,
      ct_AllocNewVec = 2,
      ct_AllocCustom = 3
   }
   ct_AllocKind; 

struct _ExeContext {
      struct _ExeContext * next;
      /* Variable-length array.  The size is VG_(clo_backtrace_size); at
       *       least 1, at most VG_DEEPEST_BACKTRACE.  [0] is the current IP,
       *       [1] is its caller, [2] is the caller of [1], etc. */
      Addr ips[0];
};		
			  
/* first two fields must match core's VgHashNode. */
typedef
   struct _ct_Chunk {
      struct _ct_Chunk* next;
      Addr          data;           // ptr to actual block
      SizeT         size : (sizeof(UWord)*8)-2; // size requested; 30 or 62 bits
      ct_AllocKind allockind : 2;  // which wrapper did the allocation
      ExeContext*   where;          // where it was allocated
   }
   ct_Chunk;

/* memory allocation functions */
/* table is ct_malloc_list*/
void *ct_new_block ( ThreadId tid, \
                     Addr p, SizeT size, SizeT align, Bool is_zeroed, \
                     ct_AllocKind kind, VgHashTable table);
void *ct_malloc ( ThreadId tid, SizeT n );
void *ct_calloc ( ThreadId tid, SizeT nmemb, SizeT size1 );  
void *ct_realloc ( ThreadId tid, void* p, SizeT new_size ); 
void ct_delete ( ThreadId tid, void* p );
void *ct_vec_new ( ThreadId tid, SizeT n );
void *ct_new ( ThreadId tid, SizeT n );
void *ct_memalign ( ThreadId tid, SizeT align, SizeT n );
void ct_vec_delete ( ThreadId tid, void* p );
   
/* free memory functions */
void ct_handle_free ( ThreadId tid, Addr p, ct_AllocKind kind );                       
void ct_free ( ThreadId tid, void *p );
void die_and_free_mem ( ThreadId tid, ct_Chunk* mc, 
			ct_Chunk** prev_chunks_next_ptr); 
			

int write_to_buffer(unsigned char *input,int bytes);
int write_trace(unsigned long type,unsigned long *input,int elements);
int flush_buffer(void);


int flush_buffer(void) {
   
   VG_(write)(file_ptr,buffer,buffer_pointer);
   
   return 0;
}

int write_to_buffer(unsigned char *input,int bytes) {

   int i;
   
      /* If buffer would overflow, write to disk... */
   if (buffer_pointer+bytes>=BUFFER_SIZE) {
      flush_buffer();
      buffer_pointer=0;
   }
   
   for(i=0;i<bytes;i++) {
      buffer[buffer_pointer+i]=input[i];
   }
   buffer_pointer+=bytes;
   
   return 0;
}


int write_trace(unsigned long type,unsigned long *input,int elements) {

   int i;
   
      /* If buffer would overflow, write to disk... */
   if (buffer_pointer+((elements+1)*sizeof(long))>=BUFFER_SIZE) {
      flush_buffer();
      buffer_pointer=0;
   }
   
      /* Write out type */
   *(long *)(buffer+(buffer_pointer))=type;
   buffer_pointer+=sizeof(long);
   
   for(i=0;i<elements;i++) {
      *(long *)(buffer+(buffer_pointer+i*sizeof(long)))=input[i];   
   }
   buffer_pointer+=(elements)*sizeof(long);
		    
   return 0;
}

/* Allocate its shadow chunk, put it on the appropriate list. */
static void add_ct_Chunk ( ThreadId tid, Addr p, SizeT size, ct_AllocKind kind, VgHashTable table)
{
   ct_Chunk* mc;

   mc            = VG_(malloc)(sizeof(ct_Chunk));
   mc->data      = p;
   mc->size      = size;
   mc->allockind = kind;
   mc->where     = VG_(record_ExeContext)(tid);

   VG_(HT_add_node)( table, (VgHashNode*)mc );
}

/* Allocate memory and note change in memory available */
/* ThreadId tid
   Addr p: allocation address
   SizeT size: allocation size
   SizeT align: alignment VG_(clo_alignment)
   Bool is_zeroed: initialization for shadow area value
   ct_AllocKind kind: use only ct_AllocMalloc here
   VgHashTable table: ct_malloc_list
*/
__inline__
void* ct_new_block ( ThreadId tid,
                     Addr p, SizeT size, SizeT align, 
		     Bool is_zeroed, ct_AllocKind kind, VgHashTable table) {
   
   /* Allocate and zero if necessary */
   if (!p){ 
      p = (Addr)VG_(cli_malloc)( align, size );
      if (!p) {
         return NULL;
      }
      if (is_zeroed) {
	 VG_(memset)((void*)p, 0, size); /* initialize value to zero */
      }
   }

   add_ct_Chunk( tid, p, size, kind, table );
   
   return (void*)p;
}
 
unsigned long get_alloc_callsite(void *address) {
   ct_Chunk* mc; 
   ct_Chunk** prev_chunks_next_ptr;

      /* the "chunk" has the execution context which has the stack */
      /* backtrace which knows where we were called from           */
   mc = (ct_Chunk*)VG_(HT_get_node) ( ct_malloc_list, (UWord)address,
                                       (void*)&prev_chunks_next_ptr );
   
//   VG_(pp_StackTrace)(mc->where->ips,2);
//   VG_(printf)("Malloc from %x\n",mc->where->ips[1]);

   return mc->where->ips[1];   
}

/* malloc */
void *ct_malloc ( ThreadId tid, SizeT n ) {
   
   void *temp_pointer;

   temp_pointer=ct_new_block ( tid, 0, n, VG_(clo_alignment), 
			       /*is_zeroed*/False, ct_AllocMalloc, 
			       ct_malloc_list);
         
   // VG_(printf) ("Malloc of size %d to address %p\n",n,temp_pointer);
   
   output[MALLOC_SIZE]=n;
   output[MALLOC_CALLSITE]=get_alloc_callsite(temp_pointer);
   write_trace(MALLOC_INFO,output,2);

   output[ALLOC_ADDRESS]=(unsigned long)temp_pointer;
   write_trace(MALLOC_ADDRESS,output,1);
         
   return temp_pointer; 
}

/* memalign */
void *ct_memalign ( ThreadId tid, SizeT align, SizeT n ) {
   
   void *temp_pointer;

   temp_pointer=ct_new_block ( tid, 0, n, VG_(clo_alignment), 
			       /*is_zeroed*/False, ct_AllocMalloc, 
			       ct_malloc_list);
         
   // VG_(printf) ("Memalign of size %d to address %p\n",n,temp_pointer);
  
   output[MEMALIGN_SIZE]=n;
   output[MEMALIGN_ALIGN]=align;
   output[MEMALIGN_CALLSITE]=get_alloc_callsite(temp_pointer);
   write_trace(MEMALIGN_INFO,output,3);
         
   output[ALLOC_ADDRESS]=(unsigned long)temp_pointer;
   write_trace(MEMALIGN_ADDRESS,output,1);
      
   return temp_pointer; 
}

/* new() */
void *ct_new ( ThreadId tid, SizeT n ) {
   
   void *temp_pointer;

   
   temp_pointer=ct_new_block ( tid, 0, n, VG_(clo_alignment), 
			       /*is_zeroed*/False, ct_AllocNew, 
			       ct_malloc_list);
         
   //VG_(printf) ("New of size %d to address %p\n",n,temp_pointer);
   
   output[NEW_SIZE]=n;
   output[NEW_CALLSITE]=get_alloc_callsite(temp_pointer);
   write_trace(NEW_INFO,output,2);
   
   output[ALLOC_ADDRESS]=(unsigned long)temp_pointer;
   write_trace(NEW_ADDRESS,output,1);
            
   return temp_pointer; 
}

/* vec_new() */
void *ct_vec_new ( ThreadId tid, SizeT n ) {
   
   void *temp_pointer;

   temp_pointer=ct_new_block ( tid, 0, n, VG_(clo_alignment), 
			       /*is_zeroed*/False, ct_AllocNewVec, 
			       ct_malloc_list);
         
   // VG_(printf) ("Malloc of size %d to address %p\n",n,temp_pointer);
   
   output[VEC_NEW_SIZE]=n;
   output[VEC_NEW_CALLSITE]=get_alloc_callsite(temp_pointer);
   write_trace(VEC_NEW_INFO,output,2);
   
   output[ALLOC_ADDRESS]=(unsigned long)temp_pointer;
   write_trace(VEC_NEW_ADDRESS,output,1);
         
   return temp_pointer; 
}


   /* calloc */
void *ct_calloc ( ThreadId tid, SizeT nmemb, SizeT size1 ) {         
  	 	
   void *temp_calloc_ptr;
   
   temp_calloc_ptr=ct_new_block ( tid, 0, nmemb*size1, VG_(clo_alignment), 
                                  /*is_zeroed*/True, 
				  ct_AllocMalloc,ct_malloc_list);
   
   //VG_(printf) ("Calloc of size %d to address %p\n",nmemb*size1,temp_calloc_ptr); 
   
   output[CALLOC_COUNT]=nmemb;
   output[CALLOC_SIZE]=size1;
   output[CALLOC_CALLSITE]=get_alloc_callsite(temp_calloc_ptr);
   write_trace(CALLOC_INFO,output,3);
   
   output[ALLOC_ADDRESS]=(unsigned long)temp_calloc_ptr;
   write_trace(CALLOC_ADDRESS,output,1);
   
   return temp_calloc_ptr;
}


/* free requires 3 function:
   ct_free -> ct_handlefree -> die_and_free_mem  */

void die_and_free_mem ( ThreadId tid, ct_Chunk* mc, 
			ct_Chunk** prev_chunks_next_ptr) {
   
   /* Remove mc from the malloclist using prev_chunks_next_ptr to
      avoid repeating the hash table lookup.  Can't remove until at least
      after free and free_mismatch errors are done because they use
      describe_addr() which looks for it in malloclist. */
   *prev_chunks_next_ptr = mc->next;

   VG_(free) ( mc );
}

__inline__
void ct_handle_free ( ThreadId tid, Addr p, ct_AllocKind kind ) {
   
   ct_Chunk*  mc;
   ct_Chunk** prev_chunks_next_ptr;
    
   mc = (ct_Chunk*)VG_(HT_get_node) ( ct_malloc_list, (UWord)p,
                                       (void*)&prev_chunks_next_ptr );

   die_and_free_mem ( tid, mc, prev_chunks_next_ptr);
}

   /* free */
void ct_free ( ThreadId tid, void* p ) {
   
      //VG_(printf) ("free at:  %p\n",p);

   output[FREE_ADDRESS]=(unsigned long)p;
   write_trace(FREE_INFO,output,1);
   
   write_trace(FREE_FINISHED,output,0);
      	 
   ct_handle_free( tid, (Addr)p, ct_AllocMalloc );
}               

   /* delete */
void ct_delete ( ThreadId tid, void* p ) {
   
      //VG_(printf) ("delete at:  %p\n",p);
   
   output[FREE_ADDRESS]=(unsigned long)p;
   write_trace(DELETE,output,1);
      	 
   ct_handle_free( tid, (Addr)p, ct_AllocNew );
}               


   /* delete */
void ct_vec_delete ( ThreadId tid, void* p ) {
   
      //VG_(printf) ("vec_delete at:  %p\n",p);
   output[FREE_ADDRESS]=(unsigned long)p;
   write_trace(VEC_DELETE,output,1);
	 
   ct_handle_free( tid, (Addr)p, ct_AllocNewVec );
}               


   /* realloc */
void* ct_realloc ( ThreadId tid, void* p, SizeT new_size ) {
   
   ct_Chunk  *mc;
   ct_Chunk **prev_chunks_next_ptr;
   UInt        i;

   mc = (ct_Chunk*)VG_(HT_get_node) ( ct_malloc_list, (UWord)p,
                                       (void*)&prev_chunks_next_ptr );

   
      // VG_(printf)("Realloc happened!\n");
   
   output[REALLOC_OLD_ADDRESS]=(unsigned long)p;
   output[REALLOC_SIZE]=(unsigned long)new_size; 
   output[REALLOC_CALLSITE]=mc->where->ips[1];
   write_trace(REALLOC_INFO,output,3);
   
   if (mc->size == new_size) {/* size unchanged */
   
      mc->where = VG_(record_ExeContext)(tid);
      //VG_(printf) ("realloc of the same size %d to address %p\n",new_size,p);
      output[ALLOC_ADDRESS]=(unsigned long)p;
      write_trace(REALLOC_ADDRESS,output,1);
      return p;
            
   } else if (mc->size > new_size) {/* new size is smaller */
      
      mc->size = new_size;
      mc->where = VG_(record_ExeContext)(tid);
      output[ALLOC_ADDRESS]=(unsigned long)p;
     // VG_(printf) ("realloc of smaller size %d to address %p\n",new_size,p);
      write_trace(REALLOC_ADDRESS,output,1);
      return p;
      
   } else {/* new size is bigger */
      
      Addr p_new;

      /* Get new memory */
      p_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_size);

      /* Copy from old to new */
      for (i = 0; i < mc->size; i++)
         ((UChar*)p_new)[i] = ((UChar*)p)[i];

      /* Free old memory */
      die_and_free_mem ( tid, mc, prev_chunks_next_ptr );

      /* this has to be after die_and_free_mem, otherwise the
         former succeeds in shorting out the new block, not the
         old, in the case when both are on the same list.  */
      add_ct_Chunk ( tid, p_new, new_size, 
                           ct_AllocMalloc, ct_malloc_list );

      output[ALLOC_ADDRESS]=(unsigned long)p_new;
     // VG_(printf) ("realloc of bigger size %d to address %p\n",new_size,p_new);
      write_trace(REALLOC_ADDRESS,output,1);
      return (void*)p_new;
   }  

}

/* Tell Valgrind this function has one parameter */ 
/* write load information to trace file*/
static VG_REGPARM(3) void print_Load (Addr a, SizeT size, Addr callsite) {

   //VG_(printf)(" Load  of address: %p\n",a);
   
   output[ADDRESS_LOADSTORE]=ADDRESS_LOAD;
   output[ADDRESS_ADDRESS]=(unsigned long)a;  //load address 
   output[ADDRESS_CALLSITE]=callsite;                 //PC
   output[ADDRESS_SIZE]=size;                 //?
   write_trace(ADDRESS,output,4);
   
}
/* write store information to trace file*/
static VG_REGPARM(3) void print_Store (Addr a, SizeT size, Addr callsite) {

      //VG_(printf)(" Store of address: %p\n",a);
   output[ADDRESS_LOADSTORE]=ADDRESS_STORE;
   output[ADDRESS_ADDRESS]=(unsigned long)a;  //store address 
   output[ADDRESS_CALLSITE]=callsite;                 //PC
   output[ADDRESS_SIZE]=size;                 //?
   write_trace(ADDRESS,output,4);

}


static VG_REGPARM(3) void InstBB(Int address, Int size, Int instructions) {
   
   output[BLOCK_BLOCK]=0;
   output[BLOCK_ADDRESS]=address;
   output[BLOCK_SIZE]=size;
   output[BLOCK_INSTRUCTIONS]=instructions;
   write_trace(BLOCK_BEGIN,output,4);
}


/*------------------------------------------------------------*/
/*--- Our instrumenter                                     ---*/
/*--- Translates the Basic Block passed in as "bb_in"      ---*/
/*---    into a new "instrumented" basic block "bb"        ---*/
/*------------------------------------------------------------*/
//from ac_main.c

    
static IRBB* ct_instrument ( VgCallbackClosure* closure,
			     IRBB* bb_in, VexGuestLayout* layout,
			     VexGuestExtents* vge,
                             IRType gWordTy, IRType hWordTy ) {

   Int         i,access_size;
   IRStmt*     st;
   IRExpr*     data;
   IRExpr      *access_address,*access_callsite;
   IRExpr*     guard;
   IRDirty*    di;
   Bool        isLoad;
   IRBB*       bb;
   Addr        block_addr;
   Int         block_size=0,block_instructions=0;

   /* Create a new basic block */
   /* We'll put all of the original instructions, plus our     */
   /* instrumentations into it, and return it back to valgrind */

   /* create an empty basic block */
   bb           = emptyIRBB();

   /* copy over configuration from the original basic block */
   bb->tyenv    = dopyIRTypeEnv(bb_in->tyenv);
   bb->next     = dopyIRExpr(bb_in->next);
   bb->jumpkind = bb_in->jumpkind;


   /* Walk through each statement, */
   /* from first (0) to last (bb_in->stmts_used) */

      /* see lackey if this happens */
   if (bb_in->stmts[0]->tag != Ist_IMark) {
      VG_(printf)("Basic block doesn't start with MARK\n");
   }

      /* Count number of original instrs and size of  BB */
   
   for (i = 0; i < bb_in->stmts_used; i++) {
       st = bb_in->stmts[i];
       if (Ist_IMark == st->tag) {
	  block_instructions++;
	  block_size+=st->Ist.IMark.len;
       }
   }
   
   block_addr = (Addr)bb_in->stmts[0]->Ist.IMark.addr;
      
   di = unsafeIRDirty_0_N( 3 , "InstBB", &InstBB, 
          mkIRExprVec_3(
		mkIRExpr_HWord(block_addr),	
		mkIRExpr_HWord(block_size),	
		mkIRExpr_HWord(block_instructions)));
   addStmtToIRBB( bb, IRStmt_Dirty(di) );
   
   
   for (i = 0; i <  bb_in->stmts_used; i++) {

      st = bb_in->stmts[i];

      /* clear these variables */
      access_size     = 0;
      access_address  = NULL;
     
      isLoad = True;

      switch (st->tag) {

	    /* Ist_Tmp means we are copying data into a */
	    /* "Temporary" register                     */
         case Ist_Tmp:
              data = st->Ist.Tmp.data;
	       /* We only care if it's a load instruction */
              if (data->tag == Iex_Load) {
                 access_address  = data->Iex.Load.addr;
                 access_size     = sizeofIRType(data->Iex.Load.ty);
                 isLoad = True;
	      }
	      break;

	    /* Ist_Store means we are storing data */
         case Ist_Store:
	      data  = st->Ist.Store.data;
              access_address = st->Ist.Store.addr;
              access_size = sizeofIRType(typeOfIRExpr(bb_in->tyenv, data));
	      isLoad = False;
              break;
	    
	    /* We ignore these */
         case Ist_Put: /* We are copying some "guest state" */
         case Ist_PutI:/* We are copying some "guest state" */
         case Ist_Exit:/* We are conditionally leaving a basic block  */
         case Ist_NoOp:
         case Ist_IMark:
         case Ist_MFence:
              break;

	    /* We are in a "dirty" function? */
         case Ist_Dirty:
	 
            if (st->Ist.Dirty.details->mFx != Ifx_None) {
               /* We classify Ifx_Modify as a load. */
               isLoad = st->Ist.Dirty.details->mFx != Ifx_Write;
               access_size    = st->Ist.Dirty.details->mSize;
               access_address  = st->Ist.Dirty.details->mAddr;

	       if (st->Ist.Dirty.details->mFx == Ifx_Read || 
		   st->Ist.Dirty.details->mFx == Ifx_Modify) {
		   isLoad=True;
	       }
	       if (st->Ist.Dirty.details->mFx == Ifx_Write || 
		   st->Ist.Dirty.details->mFx == Ifx_Modify) {
		   isLoad=False;
	       }
	       
             }
             break;

	    /* Print an error if an unknown statement type */
         default:
            VG_(printf)("\n");
            ppIRStmt(st);
            VG_(printf)("\n");
            VG_(tool_panic)("unhandled IRStmt");
            break;
      }

      /* If we were a load or store, add a call to print it */
      if (access_address) {

	 if (isLoad) {
	    /* Create a new "instruction" called "di" */
	    /* This is a dirty instruction, meaning it has side effects */
	    /* the "0" means we don't expect a return value */
	    /* the "N" means we can pass many arguments     */
	    /* We pass 1 argument, the name of the function, */
	    /* a pointer to the function, and an "argument vector" */
	    /* which in this case only has one, the address */
            di = unsafeIRDirty_0_N( 3, "print_Load", &print_Load,
                                       mkIRExprVec_3(access_address,
						     mkIRExpr_HWord(access_size),
						     mkIRExpr_HWord(access_callsite)));
       	 }
         else {
            di = unsafeIRDirty_0_N( 3, "print_Store", &print_Store,
                                       mkIRExprVec_3(access_address,
						     mkIRExpr_HWord(access_size),
						     mkIRExpr_HWord(access_callsite)));
	       }
	 

         /* put the helper call into the new Basic Block */
	       /* before the load or store */
         addStmtToIRBB( bb, IRStmt_Dirty(di) );
      }

      /* Make sure the original instruction gets added to the basic block. */
      addStmtToIRBB( bb, st );
   }
   return bb;
}
               
static void ct_post_clo_init(void) {
   
   SysRes sysr;
   unsigned char header[16];
   
   sysr=VG_(open)(FileName,
		  VKI_O_CREAT|VKI_O_TRUNC|VKI_O_WRONLY|VKI_O_LARGEFILE,
		  VKI_S_IRUSR|VKI_S_IWUSR);

   if (sysr.isError) {
      VG_(printf)("file %s can not be opened\n",FileName);
   }
   else {
      VG_(printf)("trace file %s opened\n",FileName);                         
   }
      
   file_ptr=sysr.val;   
      
   header[HEADER_LONGSIZE]=sizeof(unsigned long);
   header[HEADER_ENDIANESS]=0; /* little-endian */
   header[HEADER_VERSION]=TRACE_VERSION;
   header[HEADER_BUFFERSIZE]=BUFFER_SIZE/1024;
   
   write_to_buffer(header,16);
   VG_(printf)("header written\n");
}

   /* Parse the command line options */
static Bool ct_process_cmd_line_option(Char* arg) {
   
        /* 12 is length of "--tracefile=" */
   if (VG_CLO_STREQN(12, arg, "--tracefile=")) {
      VG_(sprintf)(FileName,"%s",&arg[12]);
   }
   else {
      return False;
   }
 
   return True;
}
   

static void ct_print_usage(void) {
   VG_(printf) ("   --tracefile=<file>  filename to use for tracefile\n");
}

static void ct_print_debug_usage(void) {
   VG_(printf)("    (none)\n");
}

static void ct_fini(Int exitcode) {   
   flush_buffer();
   VG_(close)(file_ptr);
   VG_(printf) ("\n\nCachetool:Exiting! \n\n");
}

static void ct_pre_clo_init(void)
{
   VG_(details_name)            ("Cachetool");
   VG_(details_version)         (NULL);
   VG_(details_description)     ("generates traces for the cachteool program");
   VG_(details_copyright_author)("Copyright (C) 2006 - Vince Weaver/yohowo");
   VG_(details_bug_reports_to)  (VG_BUGS_TO);

   /* set up default output file */
   VG_(sprintf)(FileName,"trace2.out");
   
   VG_(basic_tool_funcs)        (ct_post_clo_init,
                                 ct_instrument,
                                 ct_fini);
   
   VG_(needs_command_line_options)(ct_process_cmd_line_option,
				   ct_print_usage,
				   ct_print_debug_usage);

   VG_(needs_malloc_replacement)(ct_malloc,    /* malloc()     */
                                 ct_new,       /* new()        */
				 ct_vec_new,   /* vec_new()    */
				 ct_memalign,  /* memalign()   */
				 ct_calloc,    /* calloc()     */
				 ct_free,      /* free()       */
				 ct_delete,    /* delete()     */
				 ct_vec_delete,/* vec_delete() */
				 ct_realloc,   /* realloc()    */
				 16);          /* redzone block size? */

   //initialize the hash table,from mac_share.c   
   ct_malloc_list  = VG_(HT_construct)( 80021 );   // prime, big

}
  
VG_DETERMINE_INTERFACE_VERSION(ct_pre_clo_init) 

/*--------------------------------------------------------------------*/
/*--- end                                                          ---*/
/*--------------------------------------------------------------------*/
