OCC22256 Add mechanism based on malloc/free callback for debugging memory problems
authorMSV,AGV <>
Thu, 28 Apr 2011 15:52:39 +0000 (15:52 +0000)
committerbugmaster <bugmaster@opencascade.com>
Mon, 5 Mar 2012 15:28:17 +0000 (19:28 +0400)
src/Draw/Draw_BasicCommands.cxx
src/OSD/FILES
src/OSD/OSD_MAllocHook.cxx [new file with mode: 0755]
src/OSD/OSD_MAllocHook.hxx [new file with mode: 0755]

index b83c084..4e114ca 100755 (executable)
@@ -57,6 +57,7 @@ static clock_t MDTV_CPU_CURRENT; // cpu time already used at last
 #endif
 
 #include <Draw_Chronometer.hxx>
+#include <OSD_MAllocHook.hxx>
 
 #if defined (__hpux) || defined ( HPUX )
 #define RLIM_INFINITY   0x7fffffff
@@ -249,6 +250,131 @@ static Standard_Integer cpulimit(Draw_Interpretor& di, Standard_Integer n, const
   return 0;
 }
 
+//=======================================================================
+//function : mallochook
+//purpose  : 
+//=======================================================================
+
+static Standard_Integer mallochook(Draw_Interpretor& di, Standard_Integer n,
+                                   const char** a)
+{
+  if (n < 2)
+  {
+    di << "\
+usage: mallochook cmd\n\
+where cmd is one of:\n\
+  set [<op>]      - set callback to malloc/free; op is one of the following:\n\
+                    0 - set callback to NULL,\n\
+                    1 - set callback OSD_MAllocHook::CollectBySize (default)\n\
+                    2 - set callback OSD_MAllocHook::LogFileHandler\n\
+  reset           - reset the CollectBySize handler\n\
+  report1 [<outfile>]\n\
+                  - write report from CollectBySize handler in <outfile>\n\
+  open [<logfile>]\n\
+                  - open file for writing the log with LogFileHandler\n\
+  close           - close the log file with LogFileHandler\n\
+  report2 [<flag>] [<logfile>] [<outfile>]\n\
+                  - scan <logfile> written with LogFileHandler\n\
+                    and make synthesized report in <outfile>; <flag> can be:\n\
+                    0 - simple stats by sizes (default),\n\
+                    1 - with alive allocation numbers\n\
+By default <logfile> is \"mem-log.txt\", <outfile> is \"mem-stat.txt\""
+      << "\n";
+    return 0;
+  }
+  if (strcmp(a[1], "set") == 0)
+  {
+    int aType = (n > 2 ? atoi(a[2]) : 1);
+    if (aType < 0 || aType > 2)
+    {
+      di << "unknown op of the command set" << "\n";
+      return 1;
+    }
+    else if (aType == 0)
+    {
+      OSD_MAllocHook::SetCallback(NULL);
+      di << "callback is unset" << "\n";
+    }
+    else if (aType == 1)
+    {
+      OSD_MAllocHook::SetCallback(OSD_MAllocHook::GetCollectBySize());
+      di << "callback is set to CollectBySize" << "\n";
+    }
+    else //if (aType == 2)
+    {
+      OSD_MAllocHook::SetCallback(OSD_MAllocHook::GetLogFileHandler());
+      di << "callback is set to LogFileHandler" << "\n";
+    }
+  }
+  else if (strcmp(a[1], "reset") == 0)
+  {
+    OSD_MAllocHook::GetCollectBySize()->Reset();
+    di << "CollectBySize handler is reset" << "\n";
+  }
+  else if (strcmp(a[1], "open") == 0)
+  {
+    const char* aFileName = (n > 2 ? a[2] : "mem-log.txt");
+    if (!OSD_MAllocHook::GetLogFileHandler()->Open(aFileName))
+    {
+      di << "cannot create file " << aFileName << " for writing" << "\n";
+      return 1;
+    }
+    di << "log file " << aFileName << " is opened for writing" << "\n";
+  }
+  else if (strcmp(a[1], "close") == 0)
+  {
+    OSD_MAllocHook::GetLogFileHandler()->Close();
+    di << "log file is closed" << "\n";
+  }
+  else if (strcmp(a[1], "report1") == 0)
+  {
+    const char* aOutFile = "mem-stat.txt";
+    if (n > 2)
+      aOutFile = a[2];
+    if (OSD_MAllocHook::GetCollectBySize()->MakeReport(aOutFile))
+    {
+      di << "report " << aOutFile << " has been created" << "\n";
+    }
+    else
+    {
+      di << "cannot create report " << aOutFile << "\n";
+      return 1;
+    }
+  }
+  else if (strcmp(a[1], "report2") == 0)
+  {
+    Standard_Boolean includeAlive = Standard_False;
+    const char* aLogFile = "mem-log.txt";
+    const char* aOutFile = "mem-stat.txt";
+    if (n > 2)
+    {
+      includeAlive = (atoi(a[2]) != 0);
+      if (n > 3)
+      {
+        aLogFile = a[3];
+        if (n > 4)
+          aOutFile = a[4];
+      }
+    }
+    if (OSD_MAllocHook::LogFileHandler::MakeReport(aLogFile, aOutFile, includeAlive))
+    {
+      di << "report " << aOutFile << " has been created" << "\n";
+    }
+    else
+    {
+      di << "cannot create report " << aOutFile << " from the log file "
+        << aLogFile << "\n";
+      return 1;
+    }
+  }
+  else
+  {
+    di << "unrecognized command " << a[1] << "\n";
+    return 1;
+  }
+  return 0;
+}
+
 
 void Draw::BasicCommands(Draw_Interpretor& theCommands)
 {
@@ -270,4 +396,7 @@ void Draw::BasicCommands(Draw_Interpretor& theCommands)
                  __FILE__,chronom,g);
   theCommands.Add("dchrono","dchrono [ name start/stop/reset/show]",
                  __FILE__,dchronom,g);
+  theCommands.Add("mallochook",
+                  "debug memory allocation/deallocation, w/o args for help",
+                  __FILE__, mallochook, g);
 }
index dce0934..ce4f072 100755 (executable)
@@ -23,3 +23,5 @@ OSD_Localizer.cxx
 OSD_PerfMeter.c
 OSD_PerfMeter.h
 OSD_PerfMeter.hxx
+OSD_MAllocHook.cxx
+OSD_MAllocHook.hxx
diff --git a/src/OSD/OSD_MAllocHook.cxx b/src/OSD/OSD_MAllocHook.cxx
new file mode 100755 (executable)
index 0000000..8ed4385
--- /dev/null
@@ -0,0 +1,541 @@
+// File:       OSD_MAllocHook.cxx
+// Created:    04.02.2011
+// Author:     Mikhail SAZONOV
+// Copyright:  Open CASCADE S.A.S. 2011
+
+#include <OSD_MAllocHook.hxx>
+
+#ifndef WNT
+#if !defined __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif
+#include <stdint.h>
+#endif
+
+#include <set>
+#include <map>
+
+#define MAX_STR 80
+
+static OSD_MAllocHook::Callback* MypCurrentCallback = NULL;
+
+//=======================================================================
+//function : GetCallback
+//purpose  :
+//=======================================================================
+
+OSD_MAllocHook::Callback* OSD_MAllocHook::GetCallback()
+{
+  return MypCurrentCallback;
+}
+
+//=======================================================================
+//function : GetLogFileHandler
+//purpose  :
+//=======================================================================
+
+OSD_MAllocHook::LogFileHandler* OSD_MAllocHook::GetLogFileHandler()
+{
+  static LogFileHandler MyHandler;
+  return &MyHandler;
+}
+
+//=======================================================================
+//function : GetCollectBySize
+//purpose  :
+//=======================================================================
+
+OSD_MAllocHook::CollectBySize* OSD_MAllocHook::GetCollectBySize()
+{
+  static CollectBySize MyHandler;
+  return &MyHandler;
+}
+
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// Platform-dependent methods
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+#ifdef WNT
+#include <crtdbg.h>
+
+static long getRequestNum(void* pvData, long lRequest, size_t& theSize)
+{
+  if (_CrtIsValidHeapPointer(pvData))
+  {
+#if _MSC_VER == 1500   // VS 2008
+#define nNoMansLandSize 4
+    // the header struct is taken from crt/src/dbgint.h
+    struct _CrtMemBlockHeader
+    {
+#ifdef _WIN64
+        int                         nBlockUse;
+        size_t                      nDataSize;
+#else
+        size_t                      nDataSize;
+        int                         nBlockUse;
+#endif
+      long                        lRequest;
+      unsigned char               gap[nNoMansLandSize];
+    };
+    _CrtMemBlockHeader* aHeader = ((_CrtMemBlockHeader*)pvData)-1;
+    theSize = aHeader->nDataSize;
+    return aHeader->lRequest;
+#endif
+  }
+  return lRequest;
+}
+
+int __cdecl MyAllocHook(int      nAllocType,
+                        void   * pvData,
+                        size_t   nSize,
+                        int      nBlockUse,
+                        long     lRequest,
+                        const unsigned char * /*szFileName*/,
+                        int      /*nLine*/)
+{
+  if (nBlockUse == _CRT_BLOCK ||  // Ignore internal C runtime library allocations
+      MypCurrentCallback == NULL)
+    return(1);
+
+  if (nAllocType == _HOOK_ALLOC)
+    MypCurrentCallback->AllocEvent(nSize, lRequest);
+  else if (nAllocType == _HOOK_FREE)
+  {
+    // for free hook, lRequest is not defined,
+    // but we can take it from the CRT mem block header
+    size_t aSize = 0;
+    lRequest = getRequestNum(pvData, lRequest, aSize);
+    MypCurrentCallback->FreeEvent(pvData, aSize, lRequest);
+  }
+  else // _HOOK_REALLOC
+  {
+    // for realloc hook, lRequest shows the new request,
+    // and we should get request number for old block
+    size_t anOldSize = 0;
+    long anOldRequest = getRequestNum(pvData, 0, anOldSize);
+    MypCurrentCallback->FreeEvent(pvData, anOldSize, anOldRequest);
+    MypCurrentCallback->AllocEvent(nSize, lRequest);
+  }
+
+  return(1);         // Allow the memory operation to proceed
+}
+
+//=======================================================================
+//function : SetCallback
+//purpose  :
+//=======================================================================
+
+void OSD_MAllocHook::SetCallback(Callback* theCB)
+{
+  MypCurrentCallback = theCB;
+  if (theCB == NULL)
+    _CrtSetAllocHook(NULL);
+  else
+    _CrtSetAllocHook(MyAllocHook);
+}
+
+#else // ! WNT
+
+// Not yet implemented for non-WNT platform
+
+void OSD_MAllocHook::SetCallback(Callback* theCB)
+{
+  MypCurrentCallback = theCB;
+}
+
+#endif // WNT
+
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// LogFileHandler handler methods
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+//=======================================================================
+//function : LogFileHandler::LogFileHandler
+//purpose  :
+//=======================================================================
+
+OSD_MAllocHook::LogFileHandler::LogFileHandler()
+: myLogFile(NULL),
+  myBreakSize(0)
+{
+}
+
+//=======================================================================
+//function : LogFileHandler::~LogFileHandler
+//purpose  :
+//=======================================================================
+
+OSD_MAllocHook::LogFileHandler::~LogFileHandler()
+{
+  Close();
+}
+
+//=======================================================================
+//function : LogFileHandler::Open
+//purpose  :
+//=======================================================================
+
+Standard_Boolean OSD_MAllocHook::LogFileHandler::Open(const char* theFileName)
+{
+  Close();
+  myLogFile = fopen(theFileName, "w");
+  if (myLogFile != NULL)
+  {
+    fputs("Operation type; Request Number; Block Size\n", myLogFile);
+    fputs("------------------------------------------\n", myLogFile);
+  }
+  return myLogFile != NULL;
+}
+
+//=======================================================================
+//function : LogFileHandler::Close
+//purpose  :
+//=======================================================================
+
+void OSD_MAllocHook::LogFileHandler::Close()
+{
+  if (myLogFile != NULL)
+  {
+    fclose(myLogFile);
+    myLogFile = NULL;
+  }
+}
+
+//=======================================================================
+//function : LogFileHandler::MakeReport
+//purpose  :
+//=======================================================================
+
+struct StorageInfo
+{
+  Standard_Size size;
+  int nbAlloc;
+  int nbFree;
+  int nbLeftPeak;
+  std::set<unsigned long>* alive;
+  StorageInfo()
+    : size(0), nbAlloc(0), nbFree(0), nbLeftPeak(0), alive(NULL) {}
+  StorageInfo(Standard_Size theSize)
+    : size(theSize), nbAlloc(0), nbFree(0), nbLeftPeak(0), alive(NULL) {}
+  ~StorageInfo()
+  {
+    if (alive)
+      delete alive;
+  }
+  std::set<unsigned long>& Alive()
+  {
+    if (!alive)
+      alive = new std::set<unsigned long>;
+    return *alive;
+  }
+  const std::set<unsigned long>& Alive() const
+  {
+    return *alive;
+  }
+};
+
+inline bool operator < (const StorageInfo& one, const StorageInfo& two)
+{
+  return one.size < two.size;
+}
+
+Standard_Boolean OSD_MAllocHook::LogFileHandler::MakeReport
+                   (const char* theLogFile,
+                    const char* theOutFile,
+                    const Standard_Boolean theIncludeAlive)
+{
+  // open log file
+  FILE* aLogFile = fopen(theLogFile, "r");
+  if (aLogFile == NULL)
+    return Standard_False;
+
+  // skip 2 header lines
+  char aStr[MAX_STR];
+  if (fgets(aStr, MAX_STR-1, aLogFile) == NULL)
+  {
+    fclose(aLogFile);
+    return Standard_False;
+  }
+  if (fgets(aStr, MAX_STR-1, aLogFile) == NULL)
+  {
+    fclose(aLogFile);
+    return Standard_False;
+  }
+
+  // scan the log file
+  size_t aTotalLeftSize = 0;
+  size_t aTotalPeakSize = 0;
+  std::set<StorageInfo> aStMap;
+  while (fgets(aStr, MAX_STR-1, aLogFile) != NULL)
+  {
+    // detect operation type, request number and block size
+    unsigned long aReqNum, aSize;
+    char* aType = aStr;
+    char* pStr = aStr;
+    //sscanf(aStr, "%5s %lu %lu", aType, &aReqNum, &aSize);
+    while (*pStr != ' ' && *pStr) pStr++;
+    *pStr++ = '\0';
+    while (*pStr == ' ' && *pStr) pStr++;
+    aReqNum = atol(pStr);
+    while (*pStr != ' ' && *pStr) pStr++;
+    while (*pStr == ' ' && *pStr) pStr++;
+    aSize = atol(pStr);
+    Standard_Boolean isAlloc = Standard_False;
+    if (strcmp(aType, "alloc") == 0)
+    {
+      isAlloc = Standard_True;
+    }
+    else if (strcmp(aType, "free") != 0)
+      continue;
+
+    // collect statistics by storage size
+    StorageInfo aSuchInfo(aSize);
+    std::set<StorageInfo>::iterator aFound = aStMap.find(aSuchInfo);
+    if (aFound == aStMap.end())
+      aFound = aStMap.insert(aSuchInfo).first;
+    StorageInfo& aInfo = const_cast<StorageInfo&>(*aFound);
+    if (isAlloc)
+    {
+      if (aInfo.nbAlloc + 1 > 0)
+        aInfo.nbAlloc++;
+      aTotalLeftSize += aSize;
+      if (aTotalLeftSize > aTotalPeakSize)
+        aTotalPeakSize = aTotalLeftSize;
+      int nbLeft = aInfo.nbAlloc - aInfo.nbFree;
+      if (nbLeft > aInfo.nbLeftPeak)
+        aInfo.nbLeftPeak = nbLeft;
+      aInfo.Alive().insert(aReqNum);
+    }
+    else
+    {
+      std::set<unsigned long>::iterator aFoundReqNum =
+        aInfo.Alive().find(aReqNum);
+      if (aFoundReqNum == aInfo.Alive().end())
+        // freeing non-registered block, skip it
+        continue;
+      aTotalLeftSize -= aSize;
+      aInfo.Alive().erase(aFoundReqNum);
+      if (aInfo.nbAlloc + 1 > 0)
+        aInfo.nbFree++;
+    }
+  }
+  fclose(aLogFile);
+
+  // print the report
+  FILE* aRepFile = fopen(theOutFile, "w");
+  if (aRepFile == NULL)
+    return Standard_False;
+  fprintf(aRepFile, "%10s %10s %10s %10s %10s %10s %10s\n",
+          "BlockSize", "NbAlloc", "NbLeft", "NbLeftPeak",
+          "AllocSize", "LeftSize", "PeakSize");
+  Standard_Size aTotAlloc = 0;
+  for (std::set<StorageInfo>::const_iterator it = aStMap.begin();
+       it != aStMap.end(); ++it)
+  {
+    const StorageInfo& aInfo = *it;
+    Standard_Integer nbLeft = aInfo.nbAlloc - aInfo.nbFree;
+    Standard_Size aSizeAlloc = aInfo.nbAlloc * aInfo.size;
+    Standard_Size aSizeLeft = nbLeft * aInfo.size;
+    Standard_Size aSizePeak = aInfo.nbLeftPeak * aInfo.size;
+    fprintf(aRepFile, "%10d %10d %10d %10d %10Iu %10Iu %10Iu\n", aInfo.size,
+            aInfo.nbAlloc, nbLeft, aInfo.nbLeftPeak,
+            aSizeAlloc, aSizeLeft, aSizePeak);
+    if (aTotAlloc + aSizeAlloc < aTotAlloc) // overflow ?
+      aTotAlloc = SIZE_MAX;
+    else
+      aTotAlloc += aSizeAlloc;
+    if (theIncludeAlive && !aInfo.Alive().empty())
+    {
+      for (std::set<unsigned long>::const_iterator it1 = aInfo.alive->begin();
+           it1 != aInfo.alive->end(); ++it1)
+        fprintf(aRepFile, "%10lu\n", *it1);
+    }
+  }
+  fprintf(aRepFile, "%10s %10s %10s %10s%c%10Iu %10Iu %10Iu\n", "Total:",
+          "", "", "", (aTotAlloc == SIZE_MAX ? '>' : ' '), aTotAlloc,
+          aTotalLeftSize, aTotalPeakSize);
+  fclose(aRepFile);
+  return Standard_True;
+}
+
+//=======================================================================
+//function : LogFileHandler::AllocEvent
+//purpose  :
+//=======================================================================
+
+void OSD_MAllocHook::LogFileHandler::AllocEvent
+                   (size_t      theSize,
+                    long        theRequestNum)
+{
+  if (myLogFile != NULL)
+  {
+    myMutex.Lock();
+    fprintf(myLogFile, "alloc %10lu %10u\n", theRequestNum, theSize);
+    myMutex.Unlock();
+    if (myBreakSize == theSize)
+    {
+      int a = 1;
+    }
+  }
+}
+
+//=======================================================================
+//function : LogFileHandler::FreeEvent
+//purpose  :
+//=======================================================================
+
+void OSD_MAllocHook::LogFileHandler::FreeEvent
+                   (void*       /*theData*/,
+                    size_t      theSize,
+                    long        theRequestNum)
+{
+  if (myLogFile != NULL)
+  {
+    myMutex.Lock();
+    fprintf(myLogFile, "free  %10lu %10u\n", theRequestNum, theSize);
+    myMutex.Unlock();
+  }
+}
+
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// CollectBySize handler methods
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+//=======================================================================
+//function : CollectBySize::CollectBySize
+//purpose  :
+//=======================================================================
+
+OSD_MAllocHook::CollectBySize::CollectBySize()
+: myArray(NULL),
+  myTotalLeftSize(0),
+  myTotalPeakSize(0),
+  myBreakSize(0)
+{
+  Reset();
+}
+
+//=======================================================================
+//function : CollectBySize::~CollectBySize
+//purpose  :
+//=======================================================================
+
+OSD_MAllocHook::CollectBySize::~CollectBySize()
+{
+  if (myArray != NULL)
+    delete [] myArray;
+}
+
+//=======================================================================
+//function : CollectBySize::Reset
+//purpose  :
+//=======================================================================
+
+#define MAX_ALLOC_SIZE 2000000u
+
+void OSD_MAllocHook::CollectBySize::Reset()
+{
+  myMutex.Lock();
+  if (myArray == NULL)
+    myArray = new Numbers[MAX_ALLOC_SIZE];
+  else
+  {
+    for (int i = 0; i < MAX_ALLOC_SIZE; i++)
+      myArray[i] = Numbers();
+  }
+  myTotalLeftSize = 0;
+  myTotalPeakSize = 0;
+  myMutex.Unlock();
+}
+
+//=======================================================================
+//function : CollectBySize::MakeReport
+//purpose  :
+//=======================================================================
+
+Standard_Boolean OSD_MAllocHook::CollectBySize::MakeReport(const char* theOutFile)
+{
+  // print the report
+  FILE* aRepFile = fopen(theOutFile, "w");
+  if (aRepFile == NULL)
+    return Standard_False;
+  fprintf(aRepFile, "%10s %10s %10s %10s %10s %10s %10s\n",
+          "BlockSize", "NbAlloc", "NbLeft", "NbLeftPeak",
+          "AllocSize", "LeftSize", "PeakSize");
+  Standard_Size aTotAlloc = 0;
+  for (int i = 0; i < MAX_ALLOC_SIZE; i++)
+  {
+    if (myArray[i].nbAlloc > 0)
+    {
+      Standard_Integer nbLeft = myArray[i].nbAlloc - myArray[i].nbFree;
+      if (nbLeft < 0)
+        nbLeft = 0;
+      int aSize = i + 1;
+      Standard_Size aSizeAlloc = myArray[i].nbAlloc * aSize;
+      Standard_Size aSizeLeft = nbLeft * aSize;
+      Standard_Size aSizePeak = myArray[i].nbLeftPeak * aSize;
+      fprintf(aRepFile, "%10d %10d %10d %10d %10Iu %10Iu %10Iu\n", aSize,
+              myArray[i].nbAlloc, nbLeft, myArray[i].nbLeftPeak,
+              aSizeAlloc, aSizeLeft, aSizePeak);
+      if (aTotAlloc + aSizeAlloc < aTotAlloc) // overflow ?
+        aTotAlloc = SIZE_MAX;
+      else
+        aTotAlloc += aSizeAlloc;
+    }
+  }
+  fprintf(aRepFile, "%10s %10s %10s %10s%c%10Iu %10Iu %10Iu\n", "Total:",
+          "", "", "", (aTotAlloc == SIZE_MAX ? '>' : ' '), aTotAlloc,
+          myTotalLeftSize, myTotalPeakSize);
+  fclose(aRepFile);
+  return Standard_True;
+}
+
+//=======================================================================
+//function : CollectBySize::AllocEvent
+//purpose  :
+//=======================================================================
+
+void OSD_MAllocHook::CollectBySize::AllocEvent
+                   (size_t      theSize,
+                    long        /*theRequestNum*/)
+{
+  if (myBreakSize == theSize)
+  {
+    int a = 1;
+  }
+  if (theSize > 0)
+  {
+    myMutex.Lock();
+    int ind = (theSize > MAX_ALLOC_SIZE ? MAX_ALLOC_SIZE-1 : (int)(theSize-1));
+    if (myArray[ind].nbAlloc + 1 > 0)
+      myArray[ind].nbAlloc++;
+    myTotalLeftSize += theSize;
+    int nbLeft = myArray[ind].nbAlloc - myArray[ind].nbFree;
+    if (nbLeft > myArray[ind].nbLeftPeak)
+      myArray[ind].nbLeftPeak = nbLeft;
+    if (myTotalLeftSize > myTotalPeakSize)
+      myTotalPeakSize = myTotalLeftSize;
+    myMutex.Unlock();
+  }
+}
+
+//=======================================================================
+//function : CollectBySize::FreeEvent
+//purpose  :
+//=======================================================================
+
+void OSD_MAllocHook::CollectBySize::FreeEvent
+                   (void*       /*theData*/,
+                    size_t      theSize,
+                    long        /*theRequestNum*/)
+{
+  if (theSize > 0 && myTotalLeftSize >= theSize)
+  {
+    myMutex.Lock();
+    int ind = (theSize > MAX_ALLOC_SIZE ? MAX_ALLOC_SIZE-1 : (int)(theSize-1));
+    if (myArray[ind].nbFree + 1 > 0)
+      myArray[ind].nbFree++;
+    myTotalLeftSize -= theSize;
+    myMutex.Unlock();
+  }
+}
diff --git a/src/OSD/OSD_MAllocHook.hxx b/src/OSD/OSD_MAllocHook.hxx
new file mode 100755 (executable)
index 0000000..4edd59b
--- /dev/null
@@ -0,0 +1,156 @@
+// File:       OSD_MAllocHook.hxx
+// Created:    03.02.2011
+// Author:     Mikhail SAZONOV
+// Copyright:  Open CASCADE S.A.S. 2011
+
+#ifndef _OSD_MAllocHook_HeaderFile
+#define _OSD_MAllocHook_HeaderFile
+
+#include <Standard_TypeDef.hxx>
+#include <Standard_Mutex.hxx>
+#include <stdio.h>
+
+/**
+ * This class provides the possibility to set callback for memory
+ * allocation/deallocation.
+ * On MS Windows, it works only in Debug builds. It relies on the
+ * debug CRT function _CrtSetAllocHook (see MSDN for help).
+ */
+class OSD_MAllocHook
+{
+public:
+  /**
+   * Interface of a class that should handle allocation/deallocation events
+   */
+  class Callback
+  {
+  public:
+    //! Allocation event handler
+    /**
+     * It is called when allocation is done
+     * @param theSize
+     *   the size of the memory block in bytes
+     * @param theRequestNum
+     *   the allocation order number of the memory block
+     */
+    virtual void AllocEvent
+                   (size_t      theSize,
+                    long        theRequestNum) = 0;
+
+    //! Freeing event handler
+    /**
+     * It is called when the block is freed
+     * @param theData
+     *   the pointer to the user data section of the memory block
+     * @param theSize
+     *   the size of the memory block in bytes
+     * @param theRequestNum
+     *   the allocation order number of the memory block
+     */
+    virtual void FreeEvent
+                   (void*       theData,
+                    size_t      theSize,
+                    long        theRequestNum) = 0;
+  };
+
+  /**
+   * Implementation of the handler that collects all events
+   * to the log file. It contains the method to generate the report
+   * from the log file.
+   */
+  class LogFileHandler: public Callback
+  {
+  public:
+    //! Constructor
+    Standard_EXPORT LogFileHandler();
+
+    //! Destructor
+    Standard_EXPORT ~LogFileHandler();
+
+    //! Create the file and start collecting events.
+    //! Return false if the file with the given name cannot be created.
+    Standard_EXPORT Standard_Boolean Open(const char* theFileName);
+
+    //! Close the file and stop collecting events
+    Standard_EXPORT void Close();
+
+    //! Make synthesized report on the given log file.
+    /**
+     * Generate an easy to use report in the
+     * new file with the given name, taking the given log file as input.
+     * If theIncludeAlive is true then
+     * include into the report the alive allocation numbers.
+     */
+    Standard_EXPORT static Standard_Boolean MakeReport
+                   (const char* theLogFile,
+                    const char* theOutFile,
+                    const Standard_Boolean theIncludeAlive = Standard_False);
+
+    Standard_EXPORT virtual void AllocEvent(size_t, long);
+    Standard_EXPORT virtual void FreeEvent(void*, size_t, long);
+
+  private:
+    FILE*          myLogFile;
+    Standard_Mutex myMutex;
+    size_t         myBreakSize;
+  };
+
+  /**
+   * Implementation of the handler that collects numbers of
+   * allocations/deallocations for each block size directly in the memory.
+   */
+  class CollectBySize: public Callback
+  {
+  public:
+    //! Constructor
+    Standard_EXPORT CollectBySize();
+
+    //! Destructor
+    Standard_EXPORT ~CollectBySize();
+
+    //! Reset the buffer and start collecting events.
+    Standard_EXPORT void Reset();
+
+    //! Write report in the given file.
+    Standard_EXPORT Standard_Boolean MakeReport(const char* theOutFile);
+
+    Standard_EXPORT virtual void AllocEvent(size_t, long);
+    Standard_EXPORT virtual void FreeEvent(void*, size_t, long);
+
+  private:
+    struct Numbers
+    {
+      int nbAlloc;
+      int nbFree;
+      int nbLeftPeak;
+      Numbers() : nbAlloc(0), nbFree(0), nbLeftPeak(0) {}
+    };
+
+    Standard_Mutex myMutex;
+    Numbers*       myArray;
+    size_t         myTotalLeftSize;
+    size_t         myTotalPeakSize;
+    size_t         myBreakSize;
+  };
+
+  //! Set handler of allocation/deallocation events
+  /**
+   * You can pass here any implementation. For easy start, you can try
+   * with the predefined handler LogFileHandler, which static instance 
+   * is returned by GetLogFileHandler().
+   * To clear the handler, pass NULL here.
+   */
+  Standard_EXPORT static void SetCallback
+                   (Callback* theCB);
+
+  //! Get current handler of allocation/deallocation events
+  Standard_EXPORT static Callback* GetCallback();
+
+  //! Get static instance of LogFileHandler handler
+  Standard_EXPORT static LogFileHandler* GetLogFileHandler();
+
+  //! Get static instance of CollectBySize handler
+  Standard_EXPORT static CollectBySize* GetCollectBySize();
+};
+
+#endif /* _OSD_MAllocHook_HeaderFile */