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