From: MSV,AGV <> Date: Thu, 28 Apr 2011 15:52:39 +0000 (+0000) Subject: OCC22256 Add mechanism based on malloc/free callback for debugging memory problems X-Git-Tag: V6_5_1~51 X-Git-Url: http://git.dev.opencascade.org/gitweb/?p=occt.git;a=commitdiff_plain;h=7af17f1e1aac48b3cdb30adee38315498480a4df OCC22256 Add mechanism based on malloc/free callback for debugging memory problems --- diff --git a/src/Draw/Draw_BasicCommands.cxx b/src/Draw/Draw_BasicCommands.cxx index b83c08465d..4e114ca7cf 100755 --- a/src/Draw/Draw_BasicCommands.cxx +++ b/src/Draw/Draw_BasicCommands.cxx @@ -57,6 +57,7 @@ static clock_t MDTV_CPU_CURRENT; // cpu time already used at last #endif #include +#include #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 [] - 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 []\n\ + - write report from CollectBySize handler in \n\ + open []\n\ + - open file for writing the log with LogFileHandler\n\ + close - close the log file with LogFileHandler\n\ + report2 [] [] []\n\ + - scan written with LogFileHandler\n\ + and make synthesized report in ; can be:\n\ + 0 - simple stats by sizes (default),\n\ + 1 - with alive allocation numbers\n\ +By default is \"mem-log.txt\", 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); } diff --git a/src/OSD/FILES b/src/OSD/FILES index dce09340c7..ce4f072832 100755 --- a/src/OSD/FILES +++ b/src/OSD/FILES @@ -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 index 0000000000..8ed4385a5d --- /dev/null +++ b/src/OSD/OSD_MAllocHook.cxx @@ -0,0 +1,541 @@ +// File: OSD_MAllocHook.cxx +// Created: 04.02.2011 +// Author: Mikhail SAZONOV +// Copyright: Open CASCADE S.A.S. 2011 + +#include + +#ifndef WNT +#if !defined __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#include +#endif + +#include +#include + +#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 + +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* 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& Alive() + { + if (!alive) + alive = new std::set; + return *alive; + } + const std::set& 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 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::iterator aFound = aStMap.find(aSuchInfo); + if (aFound == aStMap.end()) + aFound = aStMap.insert(aSuchInfo).first; + StorageInfo& aInfo = const_cast(*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::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::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::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 index 0000000000..4edd59bc9d --- /dev/null +++ b/src/OSD/OSD_MAllocHook.hxx @@ -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 +#include +#include + +/** + * 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 */