OCC22256 Add mechanism based on malloc/free callback for debugging memory problems
[occt.git] / src / OSD / OSD_MAllocHook.cxx
1 // File:        OSD_MAllocHook.cxx
2 // Created:     04.02.2011
3 // Author:      Mikhail SAZONOV
4 // Copyright:   Open CASCADE S.A.S. 2011
5
6 #include <OSD_MAllocHook.hxx>
7
8 #ifndef WNT
9 #if !defined __STDC_LIMIT_MACROS
10 #define __STDC_LIMIT_MACROS
11 #endif
12 #include <stdint.h>
13 #endif
14
15 #include <set>
16 #include <map>
17
18 #define MAX_STR 80
19
20 static OSD_MAllocHook::Callback* MypCurrentCallback = NULL;
21
22 //=======================================================================
23 //function : GetCallback
24 //purpose  :
25 //=======================================================================
26
27 OSD_MAllocHook::Callback* OSD_MAllocHook::GetCallback()
28 {
29   return MypCurrentCallback;
30 }
31
32 //=======================================================================
33 //function : GetLogFileHandler
34 //purpose  :
35 //=======================================================================
36
37 OSD_MAllocHook::LogFileHandler* OSD_MAllocHook::GetLogFileHandler()
38 {
39   static LogFileHandler MyHandler;
40   return &MyHandler;
41 }
42
43 //=======================================================================
44 //function : GetCollectBySize
45 //purpose  :
46 //=======================================================================
47
48 OSD_MAllocHook::CollectBySize* OSD_MAllocHook::GetCollectBySize()
49 {
50   static CollectBySize MyHandler;
51   return &MyHandler;
52 }
53
54 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
55 // Platform-dependent methods
56 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
57
58 #ifdef WNT
59 #include <crtdbg.h>
60
61 static long getRequestNum(void* pvData, long lRequest, size_t& theSize)
62 {
63   if (_CrtIsValidHeapPointer(pvData))
64   {
65 #if _MSC_VER == 1500   // VS 2008
66 #define nNoMansLandSize 4
67     // the header struct is taken from crt/src/dbgint.h
68     struct _CrtMemBlockHeader
69     {
70 #ifdef _WIN64
71         int                         nBlockUse;
72         size_t                      nDataSize;
73 #else
74         size_t                      nDataSize;
75         int                         nBlockUse;
76 #endif
77       long                        lRequest;
78       unsigned char               gap[nNoMansLandSize];
79     };
80     _CrtMemBlockHeader* aHeader = ((_CrtMemBlockHeader*)pvData)-1;
81     theSize = aHeader->nDataSize;
82     return aHeader->lRequest;
83 #endif
84   }
85   return lRequest;
86 }
87
88 int __cdecl MyAllocHook(int      nAllocType,
89                         void   * pvData,
90                         size_t   nSize,
91                         int      nBlockUse,
92                         long     lRequest,
93                         const unsigned char * /*szFileName*/,
94                         int      /*nLine*/)
95 {
96   if (nBlockUse == _CRT_BLOCK ||  // Ignore internal C runtime library allocations
97       MypCurrentCallback == NULL)
98     return(1);
99
100   if (nAllocType == _HOOK_ALLOC)
101     MypCurrentCallback->AllocEvent(nSize, lRequest);
102   else if (nAllocType == _HOOK_FREE)
103   {
104     // for free hook, lRequest is not defined,
105     // but we can take it from the CRT mem block header
106     size_t aSize = 0;
107     lRequest = getRequestNum(pvData, lRequest, aSize);
108     MypCurrentCallback->FreeEvent(pvData, aSize, lRequest);
109   }
110   else // _HOOK_REALLOC
111   {
112     // for realloc hook, lRequest shows the new request,
113     // and we should get request number for old block
114     size_t anOldSize = 0;
115     long anOldRequest = getRequestNum(pvData, 0, anOldSize);
116     MypCurrentCallback->FreeEvent(pvData, anOldSize, anOldRequest);
117     MypCurrentCallback->AllocEvent(nSize, lRequest);
118   }
119
120   return(1);         // Allow the memory operation to proceed
121 }
122
123 //=======================================================================
124 //function : SetCallback
125 //purpose  :
126 //=======================================================================
127
128 void OSD_MAllocHook::SetCallback(Callback* theCB)
129 {
130   MypCurrentCallback = theCB;
131   if (theCB == NULL)
132     _CrtSetAllocHook(NULL);
133   else
134     _CrtSetAllocHook(MyAllocHook);
135 }
136
137 #else // ! WNT
138
139 // Not yet implemented for non-WNT platform
140
141 void OSD_MAllocHook::SetCallback(Callback* theCB)
142 {
143   MypCurrentCallback = theCB;
144 }
145
146 #endif // WNT
147
148 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
149 // LogFileHandler handler methods
150 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
151
152 //=======================================================================
153 //function : LogFileHandler::LogFileHandler
154 //purpose  :
155 //=======================================================================
156
157 OSD_MAllocHook::LogFileHandler::LogFileHandler()
158 : myLogFile(NULL),
159   myBreakSize(0)
160 {
161 }
162
163 //=======================================================================
164 //function : LogFileHandler::~LogFileHandler
165 //purpose  :
166 //=======================================================================
167
168 OSD_MAllocHook::LogFileHandler::~LogFileHandler()
169 {
170   Close();
171 }
172
173 //=======================================================================
174 //function : LogFileHandler::Open
175 //purpose  :
176 //=======================================================================
177
178 Standard_Boolean OSD_MAllocHook::LogFileHandler::Open(const char* theFileName)
179 {
180   Close();
181   myLogFile = fopen(theFileName, "w");
182   if (myLogFile != NULL)
183   {
184     fputs("Operation type; Request Number; Block Size\n", myLogFile);
185     fputs("------------------------------------------\n", myLogFile);
186   }
187   return myLogFile != NULL;
188 }
189
190 //=======================================================================
191 //function : LogFileHandler::Close
192 //purpose  :
193 //=======================================================================
194
195 void OSD_MAllocHook::LogFileHandler::Close()
196 {
197   if (myLogFile != NULL)
198   {
199     fclose(myLogFile);
200     myLogFile = NULL;
201   }
202 }
203
204 //=======================================================================
205 //function : LogFileHandler::MakeReport
206 //purpose  :
207 //=======================================================================
208
209 struct StorageInfo
210 {
211   Standard_Size size;
212   int nbAlloc;
213   int nbFree;
214   int nbLeftPeak;
215   std::set<unsigned long>* alive;
216   StorageInfo()
217     : size(0), nbAlloc(0), nbFree(0), nbLeftPeak(0), alive(NULL) {}
218   StorageInfo(Standard_Size theSize)
219     : size(theSize), nbAlloc(0), nbFree(0), nbLeftPeak(0), alive(NULL) {}
220   ~StorageInfo()
221   {
222     if (alive)
223       delete alive;
224   }
225   std::set<unsigned long>& Alive()
226   {
227     if (!alive)
228       alive = new std::set<unsigned long>;
229     return *alive;
230   }
231   const std::set<unsigned long>& Alive() const
232   {
233     return *alive;
234   }
235 };
236
237 inline bool operator < (const StorageInfo& one, const StorageInfo& two)
238 {
239   return one.size < two.size;
240 }
241
242 Standard_Boolean OSD_MAllocHook::LogFileHandler::MakeReport
243                    (const char* theLogFile,
244                     const char* theOutFile,
245                     const Standard_Boolean theIncludeAlive)
246 {
247   // open log file
248   FILE* aLogFile = fopen(theLogFile, "r");
249   if (aLogFile == NULL)
250     return Standard_False;
251
252   // skip 2 header lines
253   char aStr[MAX_STR];
254   if (fgets(aStr, MAX_STR-1, aLogFile) == NULL)
255   {
256     fclose(aLogFile);
257     return Standard_False;
258   }
259   if (fgets(aStr, MAX_STR-1, aLogFile) == NULL)
260   {
261     fclose(aLogFile);
262     return Standard_False;
263   }
264
265   // scan the log file
266   size_t aTotalLeftSize = 0;
267   size_t aTotalPeakSize = 0;
268   std::set<StorageInfo> aStMap;
269   while (fgets(aStr, MAX_STR-1, aLogFile) != NULL)
270   {
271     // detect operation type, request number and block size
272     unsigned long aReqNum, aSize;
273     char* aType = aStr;
274     char* pStr = aStr;
275     //sscanf(aStr, "%5s %lu %lu", aType, &aReqNum, &aSize);
276     while (*pStr != ' ' && *pStr) pStr++;
277     *pStr++ = '\0';
278     while (*pStr == ' ' && *pStr) pStr++;
279     aReqNum = atol(pStr);
280     while (*pStr != ' ' && *pStr) pStr++;
281     while (*pStr == ' ' && *pStr) pStr++;
282     aSize = atol(pStr);
283     Standard_Boolean isAlloc = Standard_False;
284     if (strcmp(aType, "alloc") == 0)
285     {
286       isAlloc = Standard_True;
287     }
288     else if (strcmp(aType, "free") != 0)
289       continue;
290
291     // collect statistics by storage size
292     StorageInfo aSuchInfo(aSize);
293     std::set<StorageInfo>::iterator aFound = aStMap.find(aSuchInfo);
294     if (aFound == aStMap.end())
295       aFound = aStMap.insert(aSuchInfo).first;
296     StorageInfo& aInfo = const_cast<StorageInfo&>(*aFound);
297     if (isAlloc)
298     {
299       if (aInfo.nbAlloc + 1 > 0)
300         aInfo.nbAlloc++;
301       aTotalLeftSize += aSize;
302       if (aTotalLeftSize > aTotalPeakSize)
303         aTotalPeakSize = aTotalLeftSize;
304       int nbLeft = aInfo.nbAlloc - aInfo.nbFree;
305       if (nbLeft > aInfo.nbLeftPeak)
306         aInfo.nbLeftPeak = nbLeft;
307       aInfo.Alive().insert(aReqNum);
308     }
309     else
310     {
311       std::set<unsigned long>::iterator aFoundReqNum =
312         aInfo.Alive().find(aReqNum);
313       if (aFoundReqNum == aInfo.Alive().end())
314         // freeing non-registered block, skip it
315         continue;
316       aTotalLeftSize -= aSize;
317       aInfo.Alive().erase(aFoundReqNum);
318       if (aInfo.nbAlloc + 1 > 0)
319         aInfo.nbFree++;
320     }
321   }
322   fclose(aLogFile);
323
324   // print the report
325   FILE* aRepFile = fopen(theOutFile, "w");
326   if (aRepFile == NULL)
327     return Standard_False;
328   fprintf(aRepFile, "%10s %10s %10s %10s %10s %10s %10s\n",
329           "BlockSize", "NbAlloc", "NbLeft", "NbLeftPeak",
330           "AllocSize", "LeftSize", "PeakSize");
331   Standard_Size aTotAlloc = 0;
332   for (std::set<StorageInfo>::const_iterator it = aStMap.begin();
333        it != aStMap.end(); ++it)
334   {
335     const StorageInfo& aInfo = *it;
336     Standard_Integer nbLeft = aInfo.nbAlloc - aInfo.nbFree;
337     Standard_Size aSizeAlloc = aInfo.nbAlloc * aInfo.size;
338     Standard_Size aSizeLeft = nbLeft * aInfo.size;
339     Standard_Size aSizePeak = aInfo.nbLeftPeak * aInfo.size;
340     fprintf(aRepFile, "%10d %10d %10d %10d %10Iu %10Iu %10Iu\n", aInfo.size,
341             aInfo.nbAlloc, nbLeft, aInfo.nbLeftPeak,
342             aSizeAlloc, aSizeLeft, aSizePeak);
343     if (aTotAlloc + aSizeAlloc < aTotAlloc) // overflow ?
344       aTotAlloc = SIZE_MAX;
345     else
346       aTotAlloc += aSizeAlloc;
347     if (theIncludeAlive && !aInfo.Alive().empty())
348     {
349       for (std::set<unsigned long>::const_iterator it1 = aInfo.alive->begin();
350            it1 != aInfo.alive->end(); ++it1)
351         fprintf(aRepFile, "%10lu\n", *it1);
352     }
353   }
354   fprintf(aRepFile, "%10s %10s %10s %10s%c%10Iu %10Iu %10Iu\n", "Total:",
355           "", "", "", (aTotAlloc == SIZE_MAX ? '>' : ' '), aTotAlloc,
356           aTotalLeftSize, aTotalPeakSize);
357   fclose(aRepFile);
358   return Standard_True;
359 }
360
361 //=======================================================================
362 //function : LogFileHandler::AllocEvent
363 //purpose  :
364 //=======================================================================
365
366 void OSD_MAllocHook::LogFileHandler::AllocEvent
367                    (size_t      theSize,
368                     long        theRequestNum)
369 {
370   if (myLogFile != NULL)
371   {
372     myMutex.Lock();
373     fprintf(myLogFile, "alloc %10lu %10u\n", theRequestNum, theSize);
374     myMutex.Unlock();
375     if (myBreakSize == theSize)
376     {
377       int a = 1;
378     }
379   }
380 }
381
382 //=======================================================================
383 //function : LogFileHandler::FreeEvent
384 //purpose  :
385 //=======================================================================
386
387 void OSD_MAllocHook::LogFileHandler::FreeEvent
388                    (void*       /*theData*/,
389                     size_t      theSize,
390                     long        theRequestNum)
391 {
392   if (myLogFile != NULL)
393   {
394     myMutex.Lock();
395     fprintf(myLogFile, "free  %10lu %10u\n", theRequestNum, theSize);
396     myMutex.Unlock();
397   }
398 }
399
400 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
401 // CollectBySize handler methods
402 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
403
404 //=======================================================================
405 //function : CollectBySize::CollectBySize
406 //purpose  :
407 //=======================================================================
408
409 OSD_MAllocHook::CollectBySize::CollectBySize()
410 : myArray(NULL),
411   myTotalLeftSize(0),
412   myTotalPeakSize(0),
413   myBreakSize(0)
414 {
415   Reset();
416 }
417
418 //=======================================================================
419 //function : CollectBySize::~CollectBySize
420 //purpose  :
421 //=======================================================================
422
423 OSD_MAllocHook::CollectBySize::~CollectBySize()
424 {
425   if (myArray != NULL)
426     delete [] myArray;
427 }
428
429 //=======================================================================
430 //function : CollectBySize::Reset
431 //purpose  :
432 //=======================================================================
433
434 #define MAX_ALLOC_SIZE 2000000u
435
436 void OSD_MAllocHook::CollectBySize::Reset()
437 {
438   myMutex.Lock();
439   if (myArray == NULL)
440     myArray = new Numbers[MAX_ALLOC_SIZE];
441   else
442   {
443     for (int i = 0; i < MAX_ALLOC_SIZE; i++)
444       myArray[i] = Numbers();
445   }
446   myTotalLeftSize = 0;
447   myTotalPeakSize = 0;
448   myMutex.Unlock();
449 }
450
451 //=======================================================================
452 //function : CollectBySize::MakeReport
453 //purpose  :
454 //=======================================================================
455
456 Standard_Boolean OSD_MAllocHook::CollectBySize::MakeReport(const char* theOutFile)
457 {
458   // print the report
459   FILE* aRepFile = fopen(theOutFile, "w");
460   if (aRepFile == NULL)
461     return Standard_False;
462   fprintf(aRepFile, "%10s %10s %10s %10s %10s %10s %10s\n",
463           "BlockSize", "NbAlloc", "NbLeft", "NbLeftPeak",
464           "AllocSize", "LeftSize", "PeakSize");
465   Standard_Size aTotAlloc = 0;
466   for (int i = 0; i < MAX_ALLOC_SIZE; i++)
467   {
468     if (myArray[i].nbAlloc > 0)
469     {
470       Standard_Integer nbLeft = myArray[i].nbAlloc - myArray[i].nbFree;
471       if (nbLeft < 0)
472         nbLeft = 0;
473       int aSize = i + 1;
474       Standard_Size aSizeAlloc = myArray[i].nbAlloc * aSize;
475       Standard_Size aSizeLeft = nbLeft * aSize;
476       Standard_Size aSizePeak = myArray[i].nbLeftPeak * aSize;
477       fprintf(aRepFile, "%10d %10d %10d %10d %10Iu %10Iu %10Iu\n", aSize,
478               myArray[i].nbAlloc, nbLeft, myArray[i].nbLeftPeak,
479               aSizeAlloc, aSizeLeft, aSizePeak);
480       if (aTotAlloc + aSizeAlloc < aTotAlloc) // overflow ?
481         aTotAlloc = SIZE_MAX;
482       else
483         aTotAlloc += aSizeAlloc;
484     }
485   }
486   fprintf(aRepFile, "%10s %10s %10s %10s%c%10Iu %10Iu %10Iu\n", "Total:",
487           "", "", "", (aTotAlloc == SIZE_MAX ? '>' : ' '), aTotAlloc,
488           myTotalLeftSize, myTotalPeakSize);
489   fclose(aRepFile);
490   return Standard_True;
491 }
492
493 //=======================================================================
494 //function : CollectBySize::AllocEvent
495 //purpose  :
496 //=======================================================================
497
498 void OSD_MAllocHook::CollectBySize::AllocEvent
499                    (size_t      theSize,
500                     long        /*theRequestNum*/)
501 {
502   if (myBreakSize == theSize)
503   {
504     int a = 1;
505   }
506   if (theSize > 0)
507   {
508     myMutex.Lock();
509     int ind = (theSize > MAX_ALLOC_SIZE ? MAX_ALLOC_SIZE-1 : (int)(theSize-1));
510     if (myArray[ind].nbAlloc + 1 > 0)
511       myArray[ind].nbAlloc++;
512     myTotalLeftSize += theSize;
513     int nbLeft = myArray[ind].nbAlloc - myArray[ind].nbFree;
514     if (nbLeft > myArray[ind].nbLeftPeak)
515       myArray[ind].nbLeftPeak = nbLeft;
516     if (myTotalLeftSize > myTotalPeakSize)
517       myTotalPeakSize = myTotalLeftSize;
518     myMutex.Unlock();
519   }
520 }
521
522 //=======================================================================
523 //function : CollectBySize::FreeEvent
524 //purpose  :
525 //=======================================================================
526
527 void OSD_MAllocHook::CollectBySize::FreeEvent
528                    (void*       /*theData*/,
529                     size_t      theSize,
530                     long        /*theRequestNum*/)
531 {
532   if (theSize > 0 && myTotalLeftSize >= theSize)
533   {
534     myMutex.Lock();
535     int ind = (theSize > MAX_ALLOC_SIZE ? MAX_ALLOC_SIZE-1 : (int)(theSize-1));
536     if (myArray[ind].nbFree + 1 > 0)
537       myArray[ind].nbFree++;
538     myTotalLeftSize -= theSize;
539     myMutex.Unlock();
540   }
541 }