0024380: OSD_MAllocHook - Improvement of malloc hook to follow maximal peack size
[occt.git] / src / OSD / OSD_MAllocHook.cxx
1 // Created on: 2011-02-04
2 // Created by: Mikhail SAZONOV
3 // Copyright (c) 2011-2012 OPEN CASCADE SAS
4 //
5 // The content of this file is subject to the Open CASCADE Technology Public
6 // License Version 6.5 (the "License"). You may not use the content of this file
7 // except in compliance with the License. Please obtain a copy of the License
8 // at http://www.opencascade.org and read it completely before using this file.
9 //
10 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
11 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
12 //
13 // The Original Code and all software distributed under the License is
14 // distributed on an "AS IS" basis, without warranty of any kind, and the
15 // Initial Developer hereby disclaims all such warranties, including without
16 // limitation, any warranties of merchantability, fitness for a particular
17 // purpose or non-infringement. Please see the License for the specific terms
18 // and conditions governing the rights and limitations under the License.
19
20 #include <OSD_MAllocHook.hxx>
21
22 #ifndef WNT
23 #if !defined __STDC_LIMIT_MACROS
24 #define __STDC_LIMIT_MACROS
25 #endif
26 #include <stdint.h>
27 #endif
28
29 #include <set>
30 #include <map>
31 #include <cstdlib>
32 #include <iomanip>
33
34 #ifndef SIZE_MAX
35 #define SIZE_MAX UINT_MAX
36 #endif
37
38 #define MAX_STR 80
39
40 static OSD_MAllocHook::Callback* MypCurrentCallback = NULL;
41
42 namespace {
43   // dummy function to call at place where break point might be needed
44   inline void place_for_breakpoint () {}
45 };
46
47 //=======================================================================
48 //function : GetCallback
49 //purpose  :
50 //=======================================================================
51
52 OSD_MAllocHook::Callback* OSD_MAllocHook::GetCallback()
53 {
54   return MypCurrentCallback;
55 }
56
57 //=======================================================================
58 //function : GetLogFileHandler
59 //purpose  :
60 //=======================================================================
61
62 OSD_MAllocHook::LogFileHandler* OSD_MAllocHook::GetLogFileHandler()
63 {
64   static LogFileHandler MyHandler;
65   return &MyHandler;
66 }
67
68 //=======================================================================
69 //function : GetCollectBySize
70 //purpose  :
71 //=======================================================================
72
73 OSD_MAllocHook::CollectBySize* OSD_MAllocHook::GetCollectBySize()
74 {
75   static CollectBySize MyHandler;
76   return &MyHandler;
77 }
78
79 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
80 // Platform-dependent methods
81 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
82
83 #ifdef WNT
84 #include <crtdbg.h>
85
86 #if _MSC_VER == 1500  /* VS 2008 */
87
88 static long getRequestNum(void* pvData, long lRequest, size_t& theSize)
89 {
90 #ifdef _DEBUG /* protect against invalid pointer; in Release, _CrtIsValidHeapPointer is always 1 */
91   if (!_CrtIsValidHeapPointer(pvData))
92     return lRequest;
93 #else
94   (void)lRequest; // avoid compiler warning on unused arg
95 #endif
96
97 #define nNoMansLandSize 4
98   // the header struct is taken from crt/src/dbgint.h
99   struct _CrtMemBlockHeader
100   {
101 #ifdef _WIN64
102       int                         nBlockUse;
103       size_t                      nDataSize;
104 #else
105       size_t                      nDataSize;
106       int                         nBlockUse;
107 #endif
108     long                        lRequest;
109     unsigned char               gap[nNoMansLandSize];
110   };
111
112   _CrtMemBlockHeader* aHeader = ((_CrtMemBlockHeader*)pvData)-1;
113   theSize = aHeader->nDataSize;
114   return aHeader->lRequest;
115 }
116
117 #else /* _MSC_VER == 1500 */
118
119 static long getRequestNum(void* /*pvData*/, long lRequest, size_t& /*theSize*/)
120 {
121   return lRequest;
122 }
123
124 #endif /* _MSC_VER == 1500 */
125
126 int __cdecl MyAllocHook(int      nAllocType,
127                         void   * pvData,
128                         size_t   nSize,
129                         int      nBlockUse,
130                         long     lRequest,
131                         const unsigned char * /*szFileName*/,
132                         int      /*nLine*/)
133 {
134   if (nBlockUse == _CRT_BLOCK ||  // Ignore internal C runtime library allocations
135       MypCurrentCallback == NULL)
136     return(1);
137
138   if (nAllocType == _HOOK_ALLOC)
139     MypCurrentCallback->AllocEvent(nSize, lRequest);
140   else if (nAllocType == _HOOK_FREE)
141   {
142     // for free hook, lRequest is not defined,
143     // but we can take it from the CRT mem block header
144     size_t aSize = 0;
145     lRequest = getRequestNum(pvData, lRequest, aSize);
146     MypCurrentCallback->FreeEvent(pvData, aSize, lRequest);
147   }
148   else // _HOOK_REALLOC
149   {
150     // for realloc hook, lRequest shows the new request,
151     // and we should get request number for old block
152     size_t anOldSize = 0;
153     long anOldRequest = getRequestNum(pvData, 0, anOldSize);
154     MypCurrentCallback->FreeEvent(pvData, anOldSize, anOldRequest);
155     MypCurrentCallback->AllocEvent(nSize, lRequest);
156   }
157
158   return(1);         // Allow the memory operation to proceed
159 }
160
161 //=======================================================================
162 //function : SetCallback
163 //purpose  :
164 //=======================================================================
165
166 void OSD_MAllocHook::SetCallback(Callback* theCB)
167 {
168   MypCurrentCallback = theCB;
169   if (theCB == NULL)
170     _CrtSetAllocHook(NULL);
171   else
172     _CrtSetAllocHook(MyAllocHook);
173 }
174
175 #else // ! WNT
176
177 // Not yet implemented for non-WNT platform
178
179 void OSD_MAllocHook::SetCallback(Callback* theCB)
180 {
181   MypCurrentCallback = theCB;
182 }
183
184 #endif // WNT
185
186 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
187 // LogFileHandler handler methods
188 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
189
190 //=======================================================================
191 //function : LogFileHandler::LogFileHandler
192 //purpose  :
193 //=======================================================================
194
195 OSD_MAllocHook::LogFileHandler::LogFileHandler()
196 : myBreakSize(0)
197 {
198   myLogFile.imbue (std::locale ("C"));
199 }
200
201 //=======================================================================
202 //function : LogFileHandler::~LogFileHandler
203 //purpose  :
204 //=======================================================================
205
206 OSD_MAllocHook::LogFileHandler::~LogFileHandler()
207 {
208   Close();
209 }
210
211 //=======================================================================
212 //function : LogFileHandler::Open
213 //purpose  :
214 //=======================================================================
215
216 Standard_Boolean OSD_MAllocHook::LogFileHandler::Open(const char* theFileName)
217 {
218   Close();
219   myLogFile.open (theFileName);
220   if (!myLogFile.is_open())
221   {
222     return Standard_False;
223   }
224
225   myLogFile << "Operation type; Request Number; Block Size\n"
226                "------------------------------------------\n";
227   return Standard_True;
228 }
229
230 //=======================================================================
231 //function : LogFileHandler::Close
232 //purpose  :
233 //=======================================================================
234
235 void OSD_MAllocHook::LogFileHandler::Close()
236 {
237   if (myLogFile.is_open())
238   {
239     myLogFile.close();
240   }
241 }
242
243 //=======================================================================
244 //function : LogFileHandler::MakeReport
245 //purpose  :
246 //=======================================================================
247
248 struct StorageInfo
249 {
250   Standard_Size size;
251   int nbAlloc;
252   int nbFree;
253   int nbLeftPeak;
254   std::set<unsigned long>* alive;
255   StorageInfo()
256     : size(0), nbAlloc(0), nbFree(0), nbLeftPeak(0), alive(NULL) {}
257   StorageInfo(Standard_Size theSize)
258     : size(theSize), nbAlloc(0), nbFree(0), nbLeftPeak(0), alive(NULL) {}
259   ~StorageInfo()
260   {
261     if (alive)
262       delete alive;
263   }
264   std::set<unsigned long>& Alive()
265   {
266     if (!alive)
267       alive = new std::set<unsigned long>;
268     return *alive;
269   }
270   const std::set<unsigned long>& Alive() const
271   {
272     return *alive;
273   }
274 };
275
276 inline bool operator < (const StorageInfo& one, const StorageInfo& two)
277 {
278   return one.size < two.size;
279 }
280
281 Standard_Boolean OSD_MAllocHook::LogFileHandler::MakeReport
282                    (const char* theLogFile,
283                     const char* theOutFile,
284                     const Standard_Boolean theIncludeAlive)
285 {
286   // open log file
287   FILE* aLogFile = fopen(theLogFile, "r");
288   if (aLogFile == NULL)
289     return Standard_False;
290
291   // skip 2 header lines
292   char aStr[MAX_STR];
293   if (fgets(aStr, MAX_STR-1, aLogFile) == NULL)
294   {
295     fclose(aLogFile);
296     return Standard_False;
297   }
298   if (fgets(aStr, MAX_STR-1, aLogFile) == NULL)
299   {
300     fclose(aLogFile);
301     return Standard_False;
302   }
303
304   // scan the log file
305   size_t aTotalLeftSize = 0;
306   size_t aTotalPeakSize = 0;
307   std::set<StorageInfo> aStMap;
308   while (fgets(aStr, MAX_STR-1, aLogFile) != NULL)
309   {
310     // detect operation type, request number and block size
311     unsigned long aReqNum, aSize;
312     char* aType = aStr;
313     char* pStr = aStr;
314     //sscanf(aStr, "%5s %lu %lu", aType, &aReqNum, &aSize);
315     while (*pStr != ' ' && *pStr) pStr++;
316     *pStr++ = '\0';
317     while (*pStr == ' ' && *pStr) pStr++;
318     aReqNum = atol(pStr);
319     while (*pStr != ' ' && *pStr) pStr++;
320     while (*pStr == ' ' && *pStr) pStr++;
321     aSize = atol(pStr);
322     Standard_Boolean isAlloc = Standard_False;
323     if (strcmp(aType, "alloc") == 0)
324     {
325       isAlloc = Standard_True;
326     }
327     else if (strcmp(aType, "free") != 0)
328       continue;
329
330     // collect statistics by storage size
331     StorageInfo aSuchInfo(aSize);
332     std::set<StorageInfo>::iterator aFound = aStMap.find(aSuchInfo);
333     if (aFound == aStMap.end())
334       aFound = aStMap.insert(aSuchInfo).first;
335     StorageInfo& aInfo = const_cast<StorageInfo&>(*aFound);
336     if (isAlloc)
337     {
338       if (aInfo.nbAlloc + 1 > 0)
339         aInfo.nbAlloc++;
340       aTotalLeftSize += aSize;
341       if (aTotalLeftSize > aTotalPeakSize)
342         aTotalPeakSize = aTotalLeftSize;
343       int nbLeft = aInfo.nbAlloc - aInfo.nbFree;
344       if (nbLeft > aInfo.nbLeftPeak)
345         aInfo.nbLeftPeak = nbLeft;
346       aInfo.Alive().insert(aReqNum);
347     }
348     else
349     {
350       std::set<unsigned long>::iterator aFoundReqNum =
351         aInfo.Alive().find(aReqNum);
352       if (aFoundReqNum == aInfo.Alive().end())
353         // freeing non-registered block, skip it
354         continue;
355       aTotalLeftSize -= aSize;
356       aInfo.Alive().erase(aFoundReqNum);
357       if (aInfo.nbAlloc + 1 > 0)
358         aInfo.nbFree++;
359     }
360   }
361   fclose(aLogFile);
362
363   // print the report
364   std::ofstream aRepFile (theOutFile);
365   if(!aRepFile.is_open())
366   {
367     return Standard_False;
368   }
369   aRepFile.imbue (std::locale ("C"));
370
371   aRepFile << std::setw(20) << "BlockSize "
372            << std::setw(10) << "NbAlloc "
373            << std::setw(10) << "NbLeft "
374            << std::setw(10) << "NbLeftPeak "
375            << std::setw(20) << "AllocSize "
376            << std::setw(20) << "LeftSize "
377            << std::setw(20) << "PeakSize " << std::endl;
378
379   Standard_Size aTotAlloc = 0;
380   for (std::set<StorageInfo>::const_iterator it = aStMap.begin();
381        it != aStMap.end(); ++it)
382   {
383     const StorageInfo& aInfo = *it;
384     Standard_Integer nbLeft = aInfo.nbAlloc - aInfo.nbFree;
385     Standard_Size aSizeAlloc = aInfo.nbAlloc * aInfo.size;
386     Standard_Size aSizeLeft = nbLeft * aInfo.size;
387     Standard_Size aSizePeak = aInfo.nbLeftPeak * aInfo.size;
388
389     aRepFile << std::setw(20) << aInfo.size << ' '
390              << std::setw(10) << aInfo.nbAlloc << ' '
391              << std::setw(10) << nbLeft << ' '
392              << std::setw(10) << aInfo.nbLeftPeak << ' '
393              << std::setw(20) << aSizeAlloc << ' '
394              << std::setw(20) << aSizeLeft << ' '
395              << std::setw(20) << aSizePeak << std::endl;
396
397     if (aTotAlloc + aSizeAlloc < aTotAlloc) // overflow ?
398       aTotAlloc = SIZE_MAX;
399     else
400       aTotAlloc += aSizeAlloc;
401     if (theIncludeAlive && !aInfo.Alive().empty())
402     {
403       for (std::set<unsigned long>::const_iterator it1 = aInfo.alive->begin();
404            it1 != aInfo.alive->end(); ++it1)
405       aRepFile << std::setw(10) << *it1;
406     }
407   }
408   aRepFile << std::setw(20) << "Total:"
409            << std::setw(10) << "" << ' '
410            << std::setw(10) << "" << ' '
411            << std::setw(10) << "" << ' '
412            << (aTotAlloc == SIZE_MAX ? '>' : ' ')
413            << std::setw(20) << aTotAlloc << ' '
414            << std::setw(20) << aTotalLeftSize << ' '
415            << std::setw(20) << aTotalPeakSize << std::endl;
416
417   aRepFile.close();
418   return Standard_True;
419 }
420
421 //=======================================================================
422 //function : LogFileHandler::AllocEvent
423 //purpose  :
424 //=======================================================================
425
426 void OSD_MAllocHook::LogFileHandler::AllocEvent
427                    (size_t      theSize,
428                     long        theRequestNum)
429 {
430   if (myLogFile.is_open())
431   {
432     myMutex.Lock();
433     myLogFile << "alloc "<< std::setw(10) << theRequestNum
434               << std::setw(20) << theSize << std::endl;
435     if (myBreakSize == theSize)
436       place_for_breakpoint();
437     myMutex.Unlock();
438   }
439 }
440
441 //=======================================================================
442 //function : LogFileHandler::FreeEvent
443 //purpose  :
444 //=======================================================================
445
446 void OSD_MAllocHook::LogFileHandler::FreeEvent
447                    (void*       /*theData*/,
448                     size_t      theSize,
449                     long        theRequestNum)
450 {
451   if (myLogFile.is_open())
452   {
453     myMutex.Lock();
454     myLogFile << "free " << std::setw(20) << theRequestNum
455               << std::setw(20) << theSize << std::endl;
456     myMutex.Unlock();
457   }
458 }
459
460 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
461 // CollectBySize handler methods
462 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
463
464 //=======================================================================
465 //function : CollectBySize::CollectBySize
466 //purpose  :
467 //=======================================================================
468
469 OSD_MAllocHook::CollectBySize::CollectBySize()
470 : myArray(NULL),
471   myTotalLeftSize(0),
472   myTotalPeakSize(0),
473   myBreakSize(0),
474   myBreakPeak(0)
475 {
476   Reset();
477 }
478
479 //=======================================================================
480 //function : CollectBySize::~CollectBySize
481 //purpose  :
482 //=======================================================================
483
484 OSD_MAllocHook::CollectBySize::~CollectBySize()
485 {
486   if (myArray != NULL)
487     delete [] myArray;
488 }
489
490 //=======================================================================
491 //function : CollectBySize::Reset
492 //purpose  :
493 //=======================================================================
494
495 #define MAX_ALLOC_SIZE 2000000u
496 const size_t OSD_MAllocHook::CollectBySize::myMaxAllocSize = MAX_ALLOC_SIZE;
497
498 void OSD_MAllocHook::CollectBySize::Reset()
499 {
500   myMutex.Lock();
501   if (myArray == NULL)
502     myArray = new Numbers[MAX_ALLOC_SIZE];
503   else
504   {
505     for (int i = 0; i < MAX_ALLOC_SIZE; i++)
506       myArray[i] = Numbers();
507   }
508   myTotalLeftSize = 0;
509   myTotalPeakSize = 0;
510   myMutex.Unlock();
511 }
512
513 //=======================================================================
514 //function : CollectBySize::MakeReport
515 //purpose  :
516 //=======================================================================
517
518 Standard_Boolean OSD_MAllocHook::CollectBySize::MakeReport(const char* theOutFile)
519 {
520   // print the report
521   std::ofstream aRepFile(theOutFile);
522   if (!aRepFile.is_open())
523     return Standard_False;
524   std::locale aCLoc("C");
525   aRepFile.imbue(aCLoc);
526
527   aRepFile << std::setw(10) << "BlockSize "
528            << std::setw(10) << "NbAlloc "
529            << std::setw(10) << "NbLeft "
530            << std::setw(10) << "NbLeftPeak "
531            << std::setw(20) << "AllocSize "
532            << std::setw(20) << "LeftSize "
533            << std::setw(20) << "PeakSize " << std::endl;
534
535   Standard_Size aTotAlloc = 0;
536   for (int i = 0; i < MAX_ALLOC_SIZE; i++)
537   {
538     if (myArray[i].nbAlloc > 0 || myArray[i].nbFree > 0)
539     {
540       Standard_Integer nbLeft = myArray[i].nbAlloc - myArray[i].nbFree;
541       int aSize = i + 1;
542       Standard_Size aSizeAlloc = myArray[i].nbAlloc * aSize;
543       ptrdiff_t     aSizeLeft = nbLeft * aSize;
544       Standard_Size aSizePeak = myArray[i].nbLeftPeak * aSize;
545
546       aRepFile << std::setw(10) << aSize << ' '
547                << std::setw(10) << myArray[i].nbAlloc << ' '
548                << std::setw(10) << nbLeft << ' '
549                << std::setw(10) << myArray[i].nbLeftPeak << ' '
550                << std::setw(20) << aSizeAlloc << ' '
551                << std::setw(20) << aSizeLeft << ' '
552                << std::setw(20) << aSizePeak << std::endl;
553
554       if (aTotAlloc + aSizeAlloc < aTotAlloc) // overflow ?
555         aTotAlloc = SIZE_MAX;
556       else
557         aTotAlloc += aSizeAlloc;
558     }
559   }
560   aRepFile << std::setw(10) << "Total:" << ' '
561            << std::setw(10) << "" << ' '
562            << std::setw(10) << "" << ' '
563            << std::setw(10) << "" << ' '
564            << (aTotAlloc == SIZE_MAX ? '>' : ' ')
565            << std::setw(20) << aTotAlloc  << ' '
566            << std::setw(20) << myTotalLeftSize  << ' '
567            << std::setw(20) << myTotalPeakSize << std::endl;
568   aRepFile.close();
569   return Standard_True;
570 }
571
572 //=======================================================================
573 //function : CollectBySize::AllocEvent
574 //purpose  :
575 //=======================================================================
576
577 void OSD_MAllocHook::CollectBySize::AllocEvent
578                    (size_t      theSize,
579                     long        /*theRequestNum*/)
580 {
581   if (myBreakSize == theSize)
582     place_for_breakpoint();
583   if (theSize > 0)
584   {
585     myMutex.Lock();
586     int ind = (theSize > MAX_ALLOC_SIZE ? MAX_ALLOC_SIZE-1 : (int)(theSize-1));
587     myArray[ind].nbAlloc++;
588     myTotalLeftSize += theSize;
589     int nbLeft = myArray[ind].nbAlloc - myArray[ind].nbFree;
590     if (nbLeft > myArray[ind].nbLeftPeak)
591     {
592       myArray[ind].nbLeftPeak = nbLeft;
593       if (myBreakPeak != 0
594        && (myBreakSize == theSize || myBreakSize == 0))
595       {
596         const Standard_Size aSizePeak = myArray[ind].nbLeftPeak * theSize;
597         if (aSizePeak > myBreakPeak)
598         {
599           place_for_breakpoint();
600         }
601       }
602     }
603     if (myTotalLeftSize > (ptrdiff_t)myTotalPeakSize)
604       myTotalPeakSize = myTotalLeftSize;
605     myMutex.Unlock();
606   }
607 }
608
609 //=======================================================================
610 //function : CollectBySize::FreeEvent
611 //purpose  :
612 //=======================================================================
613
614 void OSD_MAllocHook::CollectBySize::FreeEvent
615                    (void*       /*theData*/,
616                     size_t      theSize,
617                     long        /*theRequestNum*/)
618 {
619   if (theSize > 0)
620   {
621     myMutex.Lock();
622     int ind = (theSize > MAX_ALLOC_SIZE ? MAX_ALLOC_SIZE-1 : (int)(theSize-1));
623     myArray[ind].nbFree++;
624     myTotalLeftSize -= theSize;
625     myMutex.Unlock();
626   }
627 }