The Storage manager Class

The Storage manager Class

//*CMZ :  0.04/03 25/11/95  18.44.11  by  Fons Rademakers
//*CMZ :  0.03/10 05/10/95  11.00.10  by  Fons Rademakers
//*-- Author :    Fons Rademakers   29/07/95

// Copyright (C) 1994 CodeCERN. All rights reserved.

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TStorage                                                             //
//                                                                      //
// Storage manager. All new and delete operations in the ROOT system    //
// pass via the custom new and delete operators defined in this file.   //
// This scheme allows extensive memory checking and usage statistics    //
// gathering and an easy way to access shared memory segments.          //
// Memory checking is by default enabled and usage statistics is        //
// gathered. Using the resource (in .rootrc): Root.MemStat one can      //
// toggle statistics gathering on or off. More specifically on can trap //
// the allocation of a block of memory of a certain size. This can be   //
// specified using the resource: Root.MemStat.size, using the resource  //
// Root.MemStat.cnt one can specify after how many allocations of       //
// this size the trap should occur.                                     //
// Set the compile option NOSTATS to de-activate all memory checking    //
// statistics gathering in the system.                                  //
//                                                                      //
// When memory checking is enabled the following happens during         //
// allocation:                                                          //
//  - each allocation results in the allocation of 9 extra bytes:       //
//    2 words in front and 1 byte at the end of the memory chunck       //
//    returned to the caller.                                           //
//  - the allocated memory is set to 0.                                 //
//  - the size of the chunck is stored in the first word. The second    //
//    word is left empty (for alignment).                               //
//  - the last byte is initialized to MEM_MAGIC.                        //
//                                                                      //
// And during de-allocation this happens:                               //
//  - first the size if the block is checked. It should be >0 and       //
//    <= than any block allocated up to that moment. If not a Fatal     //
//    error is generated.                                               //
//  - the MEM_MAGIC byte at the end of the block is checked. When not   //
//    there, the memory has been overwritten and a Fatal error is       //
//    generated.                                                        //
//  - memory block is reset to 0.                                       //
//                                                                      //
// Although this does not replace powerful tools like Purify, it is a   //
// good first line of protection.                                       //
//                                                                      //
// The powerful MEM_DEBUG and MEM_STAT macros were borrowed from        //
// the ET++ framework.                                                  //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

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

//*KEEP,STORAGE.
#include "Storage.h"
//*KEEP,OBJECTTABLE.
#include "ObjectTable.h"
//*KEEP,ERROR.
#include "Error.h"
//*KEEP,RMATH.
#include "RMath.h"
//*KEEP,RSTRING.
#include "RString.h"
//*KEND.


#if !defined(NOSTATS)
#   define MEM_DEBUG
#   define MEM_STAT
#   define MEM_CHECKOBJECTPOINTERS
#endif

#if defined(MEM_STAT) && !defined(MEM_DEBUG)
#   define MEM_DEBUG
#endif

#ifdef MEM_DEBUG
#   define MEM_MAGIC ((char)0xAB)
#   define storage_size(p) ((size_t)(((int*)p)[-2]))
#   define RealStart(p) ((char*)(p) - 2*sizeof(int))
#   define StoreSize(p, sz) (*((int*)(p)) = (sz))
#   define ExtStart(p) ((char*)(p) + 2*sizeof(int))
#   define MemClear(p, start, len) \
      if ((len) > 0) memset(&((char*)(p))[(start)], 0, (len))
#   define RealSize(sz) ((sz) + 2*sizeof(int) + sizeof(char))
#   define StoreMagic(p, sz) *((char*)(p)+sz+2*sizeof(int)) = MEM_MAGIC
#   define TestMagic(p, sz) (*((char*)(p)+sz) != MEM_MAGIC)
#   define CheckMagic(p, s, where) \
      if (TestMagic(p, s))    \
         Fatal(where, "storage area overwritten");
#   define CheckFreeSize(p, where) \
      if (storage_size((p)) < 0 || storage_size((p)) > TStorage::fgMaxBlockSize) \
         Fatal(where, "unreasonable size (%d)", storage_size(p));
#   define RemoveStatMagic(p, where) \
      CheckFreeSize(p, where); \
      RemoveStat(p); \
      CheckMagic(p, storage_size(p), where)
#   define StoreSizeMagic(p, size, where) \
      StoreSize(p, size); \
      StoreMagic(p, size); \
      EnterStat(size, ExtStart(p)); \
      CheckObjPtr(ExtStart(p), where);
#else
#   define storage_size(p) ((size_t)0)
#   define RealSize(sz) (sz)
#   define RealStart(p) (p)
#   define ExtStart(p) (p)
#   define MemClear(p, start, len)
#   define StoreSizeMagic(p, size, where) \
      EnterStat(size, ExtStart(p)); \
      CheckObjPtr(ExtStart(p), where);
#   define RemoveStatMagic(p, where) \
      RemoveStat(p);
#endif


#define CallFreeHook(p, size) \
   if (TStorage::fgFreeHook) TStorage::fgFreeHook(TStorage::fgFreeHookData, (p), (size))

#ifdef MEM_CHECKOBJECTPOINTERS
//#   define CheckObjPtr(p, name) TObjectTable::CheckPtrAndWarn((name), (p));
#   define CheckObjPtr(p, name)
#else
#   define CheckObjPtr(p, name)
#endif

ULong_t       TStorage::fgLastObject;
ULong_t       TStorage::fgLastObjectEnd;
FreeHookFun_t TStorage::fgFreeHook;
void         *TStorage::fgFreeHookData;
size_t        TStorage::fgMaxBlockSize;

static const char *spaceErr = "storage exhausted";


//------------------------------------------------------------------------------
#ifdef MEM_STAT

const int kObjMaxSize = 1024;

static Bool_t   memStatistics;
static Int_t    allocated[kObjMaxSize], freed[kObjMaxSize];
static Int_t    allocatedTotal, freedTotal;
static void   **traceArray = 0;
static Int_t    traceCapacity = 10, traceIndex = 0, memSize = -1, memIndex = -1;

//______________________________________________________________________________
void EnterStat(size_t size, void *p)
{
   // Register a memory allocation operation. If desired one can trap an
   // allocation of a certain size in case one tries to find a memory
   // leak of that particular size.

   if (memStatistics && size == memSize) {
      if (traceIndex == memIndex)
         Fatal("EnterStat", "trapped allocation %d", memIndex);

      if (!traceArray) traceArray = (void**) malloc(sizeof(void*)*traceCapacity);

      if (traceIndex >= traceCapacity) {
         traceCapacity = traceCapacity*2;
         traceArray = (void**) realloc(traceArray, sizeof(void*)*traceCapacity);
      }
      traceArray[traceIndex++] = p;
   }
   TStorage::fgMaxBlockSize = TMath::Max(TStorage::fgMaxBlockSize, size);
   if (size >= kObjMaxSize)
      allocated[kObjMaxSize-1]++;
   else
      allocated[size]++;
   allocatedTotal += size;
}

//______________________________________________________________________________
void RemoveStat(void *vp)
{
   // Register a memory free operation.

   size_t size = storage_size(vp);
   if (memStatistics && size == memSize) {
      for (int i = 0; i < traceIndex; i++)
         if (traceArray[i] == vp) {
            traceArray[i] = 0;
            break;
         }
   }
   if (size >= kObjMaxSize)
      freed[kObjMaxSize-1]++;
   else
      freed[size]++;
   freedTotal+= size;
}

#else

#define EnterStat(s, p) \
   TStorage::fgMaxBlockSize = TMath::Max(TStorage::fgMaxBlockSize, s)
#define RemoveStat(p)

#endif
//------------------------------------------------------------------------------

//______________________________________________________________________________
void *operator new(size_t size)
{
   // Custom new() operator.

   static const char *where = "operator new";
   if (size < 0)
      Fatal(where, "size < 0");
   register void *vp = ::calloc(RealSize(size), sizeof(char));
   if (vp == 0)
      Fatal(where, spaceErr);
   StoreSizeMagic(vp, size, where);
   return ExtStart(vp);
}

//______________________________________________________________________________
void *operator new(size_t size, void *vp)
{
   // Custom new() operator with placement argument.

   static const char *where = "operator new(void *at)";
   if (size < 0)
      Fatal(where, "size < 0");
   if (vp == 0) {
      register void *vp = ::calloc(RealSize(size), sizeof(char));
      if (vp == 0)
         Fatal(where, spaceErr);
      StoreSizeMagic(vp, size, where);
      return ExtStart(vp);
   }
   return vp;
}

//______________________________________________________________________________
void operator delete(void *ptr)
{
   // Custom delete() operator.

   static const char *where = "operator delete";
   TStorage::fgLastObject = TStorage::fgLastObjectEnd = 0;
   if (ptr) {
      CheckObjPtr(ptr, where);
      CallFreeHook(ptr, storage_size(ptr));
      RemoveStatMagic(ptr, where);
      MemClear(RealStart(ptr), 0, RealSize(storage_size(ptr)));
      ::errno = 0;
      ::free(RealStart(ptr));
      if (errno != 0)
         SysError(where, "free");
   }
}

//______________________________________________________________________________
void *TStorage::ReAlloc(void *ovp, size_t size)
{
   // Reallocate (i.e. resize) block of memory.

   static const char *where = "TStorage::ReAlloc";
   if (size < 0)
      Fatal(where, "size < 0");
   if (ovp == 0)
      return new char[size];
   size_t oldsize = storage_size(ovp);
   if (oldsize == size)
      return ovp;
   RemoveStatMagic(ovp, where);
   void *vp = ::realloc((char*)RealStart(ovp), RealSize(size));
   if (vp == 0)
      Fatal(where, spaceErr);
   if (size > oldsize)
      MemClear(ExtStart(vp), oldsize, size-oldsize);

   StoreSizeMagic(vp, size, where);
   return ExtStart(vp);
}

//______________________________________________________________________________
void *TStorage::ReAlloc(void *ovp, size_t size, size_t oldsize)
{
   // Reallocate (i.e. resize) block of memory. Checks if current size is
   // equal to oldsize. If not memory was overwritten.

   static const char *where = "TStorage::ReAlloc";
   if (size < 0)
      Fatal(where, "size < 0");
   if (ovp == 0)
      return new char[size];
#if defined(MEM_DEBUG)
   if (oldsize != storage_size(ovp))
      fprintf(stderr, "TStorage::ReAlloc: oldsize != size\n");
#endif
   if (oldsize == size)
      return ovp;
   RemoveStatMagic(ovp, where);
   void *vp = ::realloc((char*)RealStart(ovp), RealSize(size));
   if (vp == 0)
      Fatal(where, spaceErr);
   if (size > oldsize)
      MemClear(ExtStart(vp), oldsize, size-oldsize);

   StoreSizeMagic(vp, size, where);
   return ExtStart(vp);
}

//______________________________________________________________________________
void *TStorage::ObjectAlloc(size_t sz)
{
   // Used to allocate a TObject on the heap (via TObject::new()). Directly
   // after this routine one can call (in the TObject ctor) TStorage::IsOnHeap()
   // to find out if the just created object is on the heap.

   fgLastObject    = (ULong_t) new char[sz];
   fgLastObjectEnd = fgLastObject + sz;
   return (void*) fgLastObject;
}

//______________________________________________________________________________
void TStorage::SetFreeHook(FreeHookFun_t fh, void *data)
{
   // Set a free handler.

   fgFreeHook     = fh;
   fgFreeHookData = data;
}

//______________________________________________________________________________
void TStorage::PrintStatistics()
{
   // Print memory usage statistics.

#if defined(MEM_DEBUG) && defined(MEM_STAT)

   if (!memStatistics)
      return;

//   Printf("");
   Printf("Heap statistics");
   Printf("%12s%12s%12s%12s", "size", "alloc", "free", "diff");
   Printf("================================================");

   int i;
   for (i= 0; i < kObjMaxSize; i++)
      if (allocated[i] != freed[i])
      //if (allocated[i])
         Printf("%12d%12d%12d%12d", i, allocated[i], freed[i],
                allocated[i]-freed[i]);

   if (allocatedTotal != freedTotal) {
      Printf("------------------------------------------------");
      Printf("Total:      %12d%12d%12d", allocatedTotal, freedTotal,
              allocatedTotal-freedTotal);
   }

   if (memSize != -1) {
      Printf("------------------------------------------------");
      for (i= 0; i < traceIndex; i++)
         if (traceArray[i])
            Printf("block %d of size %d not freed", i, memSize);
   }
   Printf("================================================");
   Printf("");
#endif
}

//______________________________________________________________________________
void TStorage::EnableStatistics(int size, int ix)
{
   // Enable memory usage statistics gathering. Size is the size of the memory
   // block that should be trapped and ix is after how many such allocations
   // the trap should happen.

#ifdef MEM_STAT
   memSize       = size;
   memIndex      = ix;
   memStatistics = kTRUE;
#else
   int idum = size; int iidum = ix;
#endif
}




[ROOT page] [Class index] [Top of the page]


This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.