//-*-c++-*-
//
// RCS data:
// $Date: 2003/06/30 15:10:38 $
// $Revision: 1.2 $
// $Source: /cvsroot/miwm/miwm/miwm/mlchckr.cc,v $
// $Id: mlchckr.cc,v 1.2 2003/06/30 15:10:38 sgbeal Exp $
// $RCSfile: mlchckr.cc,v $
//
// ----------------------------------
//        Start of mlchckr.cc
// ----------------------------------
// Copyright by Ben Paul Wise.
// ----------------------------------
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
// ----------------------------------

#include "mlchckr.h"
#include <cassert>

typedef list< MemoryHolderBlock * > MemoryList, *pMemoryList;


// ----------------------------------

// when you have a memory leak on the 42719 block allocated,
// it is really tedious to step through 42718 allocations to
// get to where the bad one happened. So, I added this
// to control what statements xxgdb sees. Of course, you
// must have complete repeatability of random numbers,
// as well as access to the source code. Of course,
// anyone editting code to fix memory leaks has that access.
//
// if you are busy in the edit/compile/debug cycle, just 
// reset this value in YOUR code or inside xxgdb as desired,
// and set a breakpoint on the dummy function

// here is where you would set it to some positive value.
unsigned int FirstSuspectBlock = 60;

void  xxgdbDummyFN(unsigned int bid) {
  assert (bid >= FirstSuspectBlock);
  return;
}

// ----------------------------------

static bool InNewFunction = false;    //Flag that you are in the New function
static bool InDeleteFunction = false;    //Flag that you are in the Delete Function

// One might expect a major logical error here, as follows.
// Imagine that we have three classes, A, B, C (Like Game, Position, Piece).
// A could be any complex structure with lots of pointers to subordinates
// which lots of pointers to subordinates, like a game player with interlinked
// data structures of features, search trees, etc. It could be a GUI, with levels
// of user interface blocks (that contain other UI blocks), and commands invoked
// be several UI controls. And so on.
//
// B has pointers to C's (and other stuff).  A has pointer to B's (and other stuff).
//
// I would like to delete an A, which deletes its subordinate B's, each of which
// delete subordinate C's. The logic below will set 'inDeleteFunction' to TRUE
// when you start to delete A, and you might think it would fail to register the deletes on
// B's and C's.
//
// Not so, as the little 'class Dog' example shows. The recursive deletion starts from
// the top, works down, then begins actual deallocation from the bottom up. Hence,
// nothing is missed.


unsigned int NumberOfAllocatedBlocks = 0;  // Count of blocks allocated.
static unsigned int BlockID=1;
unsigned int VerboseLevel=0;
MemoryList MemoryHolderList;


// ----------------------------------


MemoryHolderBlock::MemoryHolderBlock():BID(0),BSize(0),BPointer(NULL) {
}

MemoryHolderBlock::MemoryHolderBlock (unsigned int BlockID,  
				      unsigned int stAllocateBlock,
				      void * NewMemory):BID (BlockID),
							BSize(stAllocateBlock), 
							BPointer(NewMemory)
{
}

MemoryHolderBlock* 
GetHoldBlock(pMemoryList L, void * pMem) {
  for (MemoryList::iterator i = L->begin(); i != L->end(); ++i)     {
    if ( (*i)->BPointer == pMem) 	{
      return *i;
    }
  }
  return NULL;
}

// ----------------------------------

// new operator overload.
void *operator new( size_t stAllocateBlock)  throw (std::bad_alloc){
  void * NewMemory = malloc( stAllocateBlock );
  if( (VerboseLevel > 0) && !InNewFunction )    {
    InNewFunction = true;	// now stop checking (logging) so you can add ID to our list
    //This is used to keep these memory locations transparent to the memory checking
    //....plus that fact this would be an infinite loop if you didn't stop checking!!! very bad
    ++BlockID;
    ++NumberOfAllocatedBlocks;
    if (VerboseLevel>2) {
      cout << endl;
      cout << "ALLOCATING Block ID: "   << BlockID<<"   ";
      cout << "Size: " << stAllocateBlock<< " bytes"<<"   ";
      cout << "Total blocks now allocated: "   << NumberOfAllocatedBlocks<<endl;
    }
    MemoryHolderList.insert( MemoryHolderList.begin() , 
			     new MemoryHolderBlock (BlockID,  stAllocateBlock, NewMemory) );

    InNewFunction = false;
    
    if ((FirstSuspectBlock > 0) && (BlockID >= FirstSuspectBlock))
      xxgdbDummyFN(BlockID);
  }
  return NewMemory;
}



// new operator overload.
void *operator new[]( size_t stAllocateBlock)  throw (std::bad_alloc){
  void * NewMemory = malloc( stAllocateBlock );
  if( (VerboseLevel > 0) && !InNewFunction )   {
    InNewFunction = true;	// now stop checking (logging) so you can add ID to our list
    //This is used to keep these memory locations transparent to the memory checking
    //....plus that fact this would be an infinite loop if you didn't stop checking!!! very bad
    ++BlockID;
    ++NumberOfAllocatedBlocks;
    if (VerboseLevel>2)    {
      cout << endl;
      cout << "ALLOCATING[] Block ID: "   << BlockID<<"   ";
      cout << "Size: " << stAllocateBlock<< " bytes"<<"   ";
      cout << "Total blocks now allocated: "   << NumberOfAllocatedBlocks<<endl;
    }
    MemoryHolderList.insert( MemoryHolderList.begin() , 
			     new MemoryHolderBlock (BlockID,  stAllocateBlock, NewMemory) );
    InNewFunction = false;
    if ((FirstSuspectBlock > 0) && (BlockID >= FirstSuspectBlock))
      xxgdbDummyFN(BlockID);
  }
  return NewMemory;
}

// ----------------------------------

// delete operator overload.
void operator delete( void *pvMem) throw(){
  if( (VerboseLevel > 0) && !InDeleteFunction )    {
    if (NULL == pvMem) {
      cout <<"-----------------------"<<endl<< "Delete of NULL attempted "<<endl;
      cout <<"-----------------------"<<endl << flush;
    }
    InDeleteFunction = true;// now stop checking (logging)
    //This is used to keep these memory locations transparent to the memory checking
    MemoryHolderBlock * temp = GetHoldBlock(&MemoryHolderList, pvMem);
    MemoryHolderList. remove(temp);
    if (NULL == temp) {
      cout <<"-----------------------"<<endl<< "Double Delete attempted? "<<endl;
      cout <<"-----------------------"<<endl << flush;
      assert (NULL != temp);
    }
    --NumberOfAllocatedBlocks;
    if ( VerboseLevel>2)  {
      cout <<endl;
      cout << "DEALLOCATING  Block ID: "   << temp->BID <<"   ";
      cout << "Size : " << temp->BSize << " bytes"<<"   ";
      cout << "Total blocks still allocated: "   << NumberOfAllocatedBlocks<<endl;
    }
    delete temp;
    temp = NULL;
    InDeleteFunction = false;
  }
  free( pvMem );
  pvMem = NULL;
  return;
}


// delete[] operator overload.
void operator delete[]( void *pvMem) throw(){
  if( (VerboseLevel > 0) && !InDeleteFunction ) {
    InDeleteFunction = true;// now stop checking (logging)
    //This is used to keep these memory locations transparent to the memory checking
    if (NULL == pvMem) {
      cout <<"-----------------------"<<endl<< "Delete[] of NULL attempted "<<endl;
      cout <<"-----------------------"<<endl << flush;
    }
    MemoryHolderBlock * temp = GetHoldBlock(&MemoryHolderList, pvMem);
    MemoryHolderList. remove(temp);
    if (temp == NULL) {
      cout <<"-----------------------"<<endl<< "Double Delete[] attempted? "<<endl;
      cout <<"-----------------------"<<endl << flush;
      // BPW: temporarily commented this out
      //      assert (NULL != temp);
    }
    --NumberOfAllocatedBlocks;
    if (VerboseLevel>2) {
      cout <<endl;
      cout << "DEALLOCATING[]  Block ID: "   << temp->BID <<"   ";
      cout << "Size : " << temp->BSize << " bytes"<<"   ";
      cout << "Total blocks still allocated: "   << NumberOfAllocatedBlocks<<endl;
    }
    delete temp;
    temp = NULL;
    InDeleteFunction = false;
  }
  free( pvMem );
  pvMem = NULL;
  return;
}

// ----------------------------------
 
void StartMemoryLeakCheck(unsigned int level) {
  // leave it at whatever was set above!
  //  FirstSuspectBlock = 0;
  VerboseLevel = level;
  NumberOfAllocatedBlocks = 0;
  assert (level >= 0);
  assert (level <= 3);
  if (VerboseLevel > 0) {
    cout << endl;
    cout <<"---------------------------------" <<endl;
    cout <<" Memory leak checking started       " <<endl;
    cout <<"---------------------------------" <<endl;
  }
  return;
}


void
StopMemoryLeakCheck() {
  unsigned int totalLeakage = 0;
  unsigned int largestBlockID = 0;
  unsigned int largestBlockSize = 0;
  MemoryList::iterator i;
  if (VerboseLevel > 0) {
    cout << endl;
    cout <<"---------------------------------" << endl;
    cout <<"  Memory leak checking stopped      " <<endl;
    cout <<"---------------------------------" << endl;

    if (0 == NumberOfAllocatedBlocks) {
      cout << "  No memory leaks detected"<<endl;
    }
    else {
      cout << "  Memory leaks detected:"<<endl ;
      for ( i = MemoryHolderList.begin(); i !=MemoryHolderList.end(); ++i)
	totalLeakage = totalLeakage + (*i)->BSize;

      if (VerboseLevel >= 2) {
	for (MemoryList::iterator i = MemoryHolderList.begin();
	     i !=MemoryHolderList.end();
	     ++i) {
	  cout << "  Block ID: "   << (*i)->BID <<"   ";
	  cout << "Size: " << (*i)->BSize << " bytes"<<endl;
	  if ( (*i)->BSize > largestBlockSize ) {
	    largestBlockSize = (*i)->BSize;
	    largestBlockID = (*i)->BID;
	  }
	}
      }
      cout <<"---------------------------------" << endl;
      cout << "  Largest block's ID: "   << largestBlockID <<"   ";
      cout << "Size: " << largestBlockSize << " bytes"<<endl;
      cout << "  Total blocks left: " << NumberOfAllocatedBlocks << endl;
      cout << "  Total leakage: " << totalLeakage << endl;
    }
    cout <<"---------------------------------" << endl;
  }
  VerboseLevel = 0;
  while ( !MemoryHolderList.empty() ) {
    delete MemoryHolderList.front();
    MemoryHolderList.pop_front();
  }
  return;
}




void
AbortMemoryLeakCheck() {
//   unsigned int totalLeakage = 0;
//   unsigned int largestBlockID = 0;
//   unsigned int largestBlockSize = 0;
//   MemoryList::iterator i;
  if (VerboseLevel > 0) {
    cout << endl;
    cout <<"---------------------------------" << endl;
    cout <<"  Memory leak checking aborted   " <<endl;
    cout <<"---------------------------------" << endl;
  }
  VerboseLevel = 0;
  while ( !MemoryHolderList.empty() ) {
    delete MemoryHolderList.front();
    MemoryHolderList.pop_front();
  }
  return;
}


// ----------------------------------
//        End of mlchckr.cc
// ----------------------------------

