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