0031687: Draw Harness, ViewerTest - extend command vrenderparams with option updating...
[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 namespace
250 {
251   struct StorageInfo
252   {
253     Standard_Size           size;
254     Standard_Integer        nbAlloc;
255     Standard_Integer        nbFree;
256     Standard_Integer        nbLeftPeak;
257     std::set<unsigned long> alive;
258
259     StorageInfo(Standard_Size theSize = 0)
260     : size      (theSize),
261       nbAlloc   (0),
262       nbFree    (0),
263       nbLeftPeak(0),
264       alive()
265     {
266     }
267
268     bool operator < (const StorageInfo& theOther) const
269     {
270       return size < theOther.size;
271     }
272   };
273 }
274
275 Standard_Boolean OSD_MAllocHook::LogFileHandler::MakeReport
276                    (const char* theLogFile,
277                     const char* theOutFile,
278                     const Standard_Boolean theIncludeAlive)
279 {
280   // open log file
281   FILE* aLogFile = fopen(theLogFile, "r");
282   if (aLogFile == NULL)
283     return Standard_False;
284
285   // skip 2 header lines
286   char aStr[MAX_STR];
287   if (fgets(aStr, MAX_STR-1, aLogFile) == NULL)
288   {
289     fclose(aLogFile);
290     return Standard_False;
291   }
292   if (fgets(aStr, MAX_STR-1, aLogFile) == NULL)
293   {
294     fclose(aLogFile);
295     return Standard_False;
296   }
297
298   // scan the log file
299   size_t aTotalLeftSize = 0;
300   size_t aTotalPeakSize = 0;
301   std::set<StorageInfo> aStMap;
302   while (fgets(aStr, MAX_STR-1, aLogFile) != NULL)
303   {
304     // detect operation type, request number and block size
305     unsigned long aReqNum, aSize;
306     char* aType = aStr;
307     char* pStr = aStr;
308     //sscanf(aStr, "%5s %lu %lu", aType, &aReqNum, &aSize);
309     while (*pStr != ' ' && *pStr) pStr++;
310     *pStr++ = '\0';
311     while (*pStr == ' ' && *pStr) pStr++;
312     aReqNum = atol(pStr);
313     while (*pStr != ' ' && *pStr) pStr++;
314     while (*pStr == ' ' && *pStr) pStr++;
315     aSize = atol(pStr);
316     Standard_Boolean isAlloc = Standard_False;
317     if (strcmp(aType, "alloc") == 0)
318     {
319       isAlloc = Standard_True;
320     }
321     else if (strcmp(aType, "free") != 0)
322       continue;
323
324     // collect statistics by storage size
325     StorageInfo aSuchInfo(aSize);
326     std::set<StorageInfo>::iterator aFound = aStMap.find(aSuchInfo);
327     if (aFound == aStMap.end())
328       aFound = aStMap.insert(aSuchInfo).first;
329     StorageInfo& aInfo = const_cast<StorageInfo&>(*aFound);
330     if (isAlloc)
331     {
332       if (aInfo.nbAlloc + 1 > 0)
333         aInfo.nbAlloc++;
334       aTotalLeftSize += aSize;
335       if (aTotalLeftSize > aTotalPeakSize)
336         aTotalPeakSize = aTotalLeftSize;
337       int nbLeft = aInfo.nbAlloc - aInfo.nbFree;
338       if (nbLeft > aInfo.nbLeftPeak)
339         aInfo.nbLeftPeak = nbLeft;
340       aInfo.alive.insert(aReqNum);
341     }
342     else
343     {
344       std::set<unsigned long>::iterator aFoundReqNum =
345         aInfo.alive.find(aReqNum);
346       if (aFoundReqNum == aInfo.alive.end())
347         // freeing non-registered block, skip it
348         continue;
349       aTotalLeftSize -= aSize;
350       aInfo.alive.erase(aFoundReqNum);
351       if (aInfo.nbAlloc + 1 > 0)
352         aInfo.nbFree++;
353     }
354   }
355   fclose(aLogFile);
356
357   // print the report
358   std::ofstream aRepFile (theOutFile);
359   if(!aRepFile.is_open())
360   {
361     return Standard_False;
362   }
363   aRepFile.imbue (std::locale ("C"));
364
365   aRepFile << std::setw(20) << "BlockSize "
366            << std::setw(10) << "NbAlloc "
367            << std::setw(10) << "NbLeft "
368            << std::setw(10) << "NbLeftPeak "
369            << std::setw(20) << "AllocSize "
370            << std::setw(20) << "LeftSize "
371            << std::setw(20) << "PeakSize " << std::endl;
372
373   Standard_Size aTotAlloc = 0;
374   for (std::set<StorageInfo>::const_iterator it = aStMap.begin();
375        it != aStMap.end(); ++it)
376   {
377     const StorageInfo& aInfo = *it;
378     Standard_Integer nbLeft = aInfo.nbAlloc - aInfo.nbFree;
379     Standard_Size aSizeAlloc = aInfo.nbAlloc * aInfo.size;
380     Standard_Size aSizeLeft = nbLeft * aInfo.size;
381     Standard_Size aSizePeak = aInfo.nbLeftPeak * aInfo.size;
382
383     aRepFile << std::setw(20) << aInfo.size << ' '
384              << std::setw(10) << aInfo.nbAlloc << ' '
385              << std::setw(10) << nbLeft << ' '
386              << std::setw(10) << aInfo.nbLeftPeak << ' '
387              << std::setw(20) << aSizeAlloc << ' '
388              << std::setw(20) << aSizeLeft << ' '
389              << std::setw(20) << aSizePeak << std::endl;
390
391     if (aTotAlloc + aSizeAlloc < aTotAlloc) // overflow ?
392       aTotAlloc = SIZE_MAX;
393     else
394       aTotAlloc += aSizeAlloc;
395     if (theIncludeAlive && !aInfo.alive.empty())
396     {
397       for (std::set<unsigned long>::const_iterator it1 = aInfo.alive.begin();
398            it1 != aInfo.alive.end(); ++it1)
399       aRepFile << std::setw(10) << *it1;
400     }
401   }
402   aRepFile << std::setw(20) << "Total:"
403            << std::setw(10) << "" << ' '
404            << std::setw(10) << "" << ' '
405            << std::setw(10) << "" << ' '
406            << (aTotAlloc == SIZE_MAX ? '>' : ' ')
407            << std::setw(20) << aTotAlloc << ' '
408            << std::setw(20) << aTotalLeftSize << ' '
409            << std::setw(20) << aTotalPeakSize << std::endl;
410
411   aRepFile.close();
412   return Standard_True;
413 }
414
415 //=======================================================================
416 //function : LogFileHandler::AllocEvent
417 //purpose  :
418 //=======================================================================
419
420 void OSD_MAllocHook::LogFileHandler::AllocEvent
421                    (size_t      theSize,
422                     long        theRequestNum)
423 {
424   if (myLogFile.is_open())
425   {
426     myMutex.Lock();
427     myLogFile << "alloc "<< std::setw(10) << theRequestNum
428               << std::setw(20) << theSize << std::endl;
429     if (myBreakSize == theSize)
430       place_for_breakpoint();
431     myMutex.Unlock();
432   }
433 }
434
435 //=======================================================================
436 //function : LogFileHandler::FreeEvent
437 //purpose  :
438 //=======================================================================
439
440 void OSD_MAllocHook::LogFileHandler::FreeEvent
441                    (void*       /*theData*/,
442                     size_t      theSize,
443                     long        theRequestNum)
444 {
445   if (myLogFile.is_open())
446   {
447     myMutex.Lock();
448     myLogFile << "free " << std::setw(20) << theRequestNum
449               << std::setw(20) << theSize << std::endl;
450     myMutex.Unlock();
451   }
452 }
453
454 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
455 // CollectBySize handler methods
456 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
457
458 //=======================================================================
459 //function : CollectBySize::CollectBySize
460 //purpose  :
461 //=======================================================================
462
463 OSD_MAllocHook::CollectBySize::CollectBySize()
464 : myArray(NULL),
465   myTotalLeftSize(0),
466   myTotalPeakSize(0),
467   myBreakSize(0),
468   myBreakPeak(0)
469 {
470   Reset();
471 }
472
473 //=======================================================================
474 //function : CollectBySize::~CollectBySize
475 //purpose  :
476 //=======================================================================
477
478 OSD_MAllocHook::CollectBySize::~CollectBySize()
479 {
480   if (myArray != NULL)
481     delete [] myArray;
482 }
483
484 //=======================================================================
485 //function : CollectBySize::Reset
486 //purpose  :
487 //=======================================================================
488
489 #define MAX_ALLOC_SIZE 2000000
490 const size_t OSD_MAllocHook::CollectBySize::myMaxAllocSize = MAX_ALLOC_SIZE;
491
492 void OSD_MAllocHook::CollectBySize::Reset()
493 {
494   myMutex.Lock();
495   if (myArray == NULL)
496     myArray = new Numbers[MAX_ALLOC_SIZE];
497   else
498   {
499     for (int i = 0; i < MAX_ALLOC_SIZE; i++)
500       myArray[i] = Numbers();
501   }
502   myTotalLeftSize = 0;
503   myTotalPeakSize = 0;
504   myMutex.Unlock();
505 }
506
507 //=======================================================================
508 //function : CollectBySize::MakeReport
509 //purpose  :
510 //=======================================================================
511
512 Standard_Boolean OSD_MAllocHook::CollectBySize::MakeReport(const char* theOutFile)
513 {
514   // print the report
515   std::ofstream aRepFile(theOutFile);
516   if (!aRepFile.is_open())
517     return Standard_False;
518   std::locale aCLoc("C");
519   aRepFile.imbue(aCLoc);
520
521   aRepFile << std::setw(10) << "BlockSize "
522            << std::setw(10) << "NbAlloc "
523            << std::setw(10) << "NbLeft "
524            << std::setw(10) << "NbLeftPeak "
525            << std::setw(20) << "AllocSize "
526            << std::setw(20) << "LeftSize "
527            << std::setw(20) << "PeakSize " << std::endl;
528
529   Standard_Size aTotAlloc = 0;
530   for (int i = 0; i < MAX_ALLOC_SIZE; i++)
531   {
532     if (myArray[i].nbAlloc > 0 || myArray[i].nbFree > 0)
533     {
534       Standard_Integer nbLeft = myArray[i].nbAlloc - myArray[i].nbFree;
535       int aSize = i + 1;
536       Standard_Size aSizeAlloc = myArray[i].nbAlloc * aSize;
537       ptrdiff_t     aSizeLeft = nbLeft * aSize;
538       Standard_Size aSizePeak = myArray[i].nbLeftPeak * aSize;
539
540       aRepFile << std::setw(10) << aSize << ' '
541                << std::setw(10) << myArray[i].nbAlloc << ' '
542                << std::setw(10) << nbLeft << ' '
543                << std::setw(10) << myArray[i].nbLeftPeak << ' '
544                << std::setw(20) << aSizeAlloc << ' '
545                << std::setw(20) << aSizeLeft << ' '
546                << std::setw(20) << aSizePeak << std::endl;
547
548       if (aTotAlloc + aSizeAlloc < aTotAlloc) // overflow ?
549         aTotAlloc = SIZE_MAX;
550       else
551         aTotAlloc += aSizeAlloc;
552     }
553   }
554   aRepFile << std::setw(10) << "Total:" << ' '
555            << std::setw(10) << "" << ' '
556            << std::setw(10) << "" << ' '
557            << std::setw(10) << "" << ' '
558            << (aTotAlloc == SIZE_MAX ? '>' : ' ')
559            << std::setw(20) << aTotAlloc  << ' '
560            << std::setw(20) << myTotalLeftSize  << ' '
561            << std::setw(20) << myTotalPeakSize << std::endl;
562   aRepFile.close();
563   return Standard_True;
564 }
565
566 //=======================================================================
567 //function : CollectBySize::AllocEvent
568 //purpose  :
569 //=======================================================================
570
571 void OSD_MAllocHook::CollectBySize::AllocEvent
572                    (size_t      theSize,
573                     long        /*theRequestNum*/)
574 {
575   if (myBreakSize == theSize)
576     place_for_breakpoint();
577   if (theSize > 0)
578   {
579     myMutex.Lock();
580     int ind = (theSize > MAX_ALLOC_SIZE ? MAX_ALLOC_SIZE-1 : (int)(theSize-1));
581     myArray[ind].nbAlloc++;
582     myTotalLeftSize += theSize;
583     int nbLeft = myArray[ind].nbAlloc - myArray[ind].nbFree;
584     if (nbLeft > myArray[ind].nbLeftPeak)
585     {
586       myArray[ind].nbLeftPeak = nbLeft;
587       if (myBreakPeak != 0
588        && (myBreakSize == theSize || myBreakSize == 0))
589       {
590         const Standard_Size aSizePeak = myArray[ind].nbLeftPeak * theSize;
591         if (aSizePeak > myBreakPeak)
592         {
593           place_for_breakpoint();
594         }
595       }
596     }
597     if (myTotalLeftSize > (ptrdiff_t)myTotalPeakSize)
598       myTotalPeakSize = myTotalLeftSize;
599     myMutex.Unlock();
600   }
601 }
602
603 //=======================================================================
604 //function : CollectBySize::FreeEvent
605 //purpose  :
606 //=======================================================================
607
608 void OSD_MAllocHook::CollectBySize::FreeEvent
609                    (void*       /*theData*/,
610                     size_t      theSize,
611                     long        /*theRequestNum*/)
612 {
613   if (theSize > 0)
614   {
615     myMutex.Lock();
616     int ind = (theSize > MAX_ALLOC_SIZE ? MAX_ALLOC_SIZE-1 : (int)(theSize-1));
617     myArray[ind].nbFree++;
618     myTotalLeftSize -= theSize;
619     myMutex.Unlock();
620   }
621 }