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