0026377: Passing Handle objects as arguments to functions as non-const reference...
[occt.git] / src / OSD / OSD_MAllocHook.cxx
CommitLineData
b311480e 1// Created on: 2011-02-04
2// Created by: Mikhail SAZONOV
973c2be1 3// Copyright (c) 2011-2014 OPEN CASCADE SAS
b311480e 4//
973c2be1 5// This file is part of Open CASCADE Technology software library.
b311480e 6//
d5f74e42 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
973c2be1 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.
b311480e 12//
973c2be1 13// Alternatively, this file may be used under the terms of Open CASCADE
14// commercial license or contractual agreement.
b311480e 15
7af17f1e
MA
16#include <OSD_MAllocHook.hxx>
17
57c28b61 18#ifndef _MSC_VER
7af17f1e
MA
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>
e2f8b392 27#include <cstdlib>
2cb44241 28#include <cstring>
64531d9c 29#include <iomanip>
7af17f1e 30
213cb888
M
31#ifndef SIZE_MAX
32#define SIZE_MAX UINT_MAX
33#endif
34
7af17f1e
MA
35#define MAX_STR 80
36
37static OSD_MAllocHook::Callback* MypCurrentCallback = NULL;
38
302f96fb 39namespace {
40 // dummy function to call at place where break point might be needed
de9a2842 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 }
a3f6f591 47}
302f96fb 48
7af17f1e
MA
49//=======================================================================
50//function : GetCallback
51//purpose :
52//=======================================================================
53
54OSD_MAllocHook::Callback* OSD_MAllocHook::GetCallback()
55{
56 return MypCurrentCallback;
57}
58
59//=======================================================================
60//function : GetLogFileHandler
61//purpose :
62//=======================================================================
63
64OSD_MAllocHook::LogFileHandler* OSD_MAllocHook::GetLogFileHandler()
65{
66 static LogFileHandler MyHandler;
67 return &MyHandler;
68}
69
70//=======================================================================
71//function : GetCollectBySize
72//purpose :
73//=======================================================================
74
75OSD_MAllocHook::CollectBySize* OSD_MAllocHook::GetCollectBySize()
76{
77 static CollectBySize MyHandler;
78 return &MyHandler;
79}
80
81//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
82// Platform-dependent methods
83//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
84
57c28b61 85#ifdef _MSC_VER
7af17f1e
MA
86#include <crtdbg.h>
87
de9a2842 88#if _MSC_VER >= 1500 /* VS 2008 */
295cb053 89
7af17f1e
MA
90static long getRequestNum(void* pvData, long lRequest, size_t& theSize)
91{
295cb053 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
105aae76 97#endif
295cb053 98
7af17f1e 99#define nNoMansLandSize 4
295cb053 100 // the header struct is taken from crt/src/dbgint.h
101 struct _CrtMemBlockHeader
102 {
7af17f1e 103#ifdef _WIN64
295cb053 104 int nBlockUse;
105 size_t nDataSize;
7af17f1e 106#else
295cb053 107 size_t nDataSize;
108 int nBlockUse;
105aae76 109#endif
295cb053 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
de9a2842 119#else /* _MSC_VER < 1500 */
295cb053 120
121static long getRequestNum(void* /*pvData*/, long lRequest, size_t& /*theSize*/)
122{
7af17f1e
MA
123 return lRequest;
124}
125
295cb053 126#endif /* _MSC_VER == 1500 */
127
7af17f1e
MA
128int __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
168void OSD_MAllocHook::SetCallback(Callback* theCB)
169{
170 MypCurrentCallback = theCB;
171 if (theCB == NULL)
172 _CrtSetAllocHook(NULL);
173 else
174 _CrtSetAllocHook(MyAllocHook);
175}
176
57c28b61 177#else // ! _MSC_VER
7af17f1e
MA
178
179// Not yet implemented for non-WNT platform
180
181void OSD_MAllocHook::SetCallback(Callback* theCB)
182{
183 MypCurrentCallback = theCB;
184}
185
57c28b61 186#endif // _MSC_VER
7af17f1e
MA
187
188//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
189// LogFileHandler handler methods
190//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
191
192//=======================================================================
193//function : LogFileHandler::LogFileHandler
194//purpose :
195//=======================================================================
196
197OSD_MAllocHook::LogFileHandler::LogFileHandler()
64531d9c 198: myBreakSize(0)
7af17f1e 199{
64531d9c 200 myLogFile.imbue (std::locale ("C"));
7af17f1e
MA
201}
202
203//=======================================================================
204//function : LogFileHandler::~LogFileHandler
205//purpose :
206//=======================================================================
207
208OSD_MAllocHook::LogFileHandler::~LogFileHandler()
209{
210 Close();
211}
212
213//=======================================================================
214//function : LogFileHandler::Open
215//purpose :
216//=======================================================================
217
218Standard_Boolean OSD_MAllocHook::LogFileHandler::Open(const char* theFileName)
219{
220 Close();
64531d9c 221 myLogFile.open (theFileName);
222 if (!myLogFile.is_open())
7af17f1e 223 {
64531d9c 224 return Standard_False;
7af17f1e 225 }
64531d9c 226
227 myLogFile << "Operation type; Request Number; Block Size\n"
228 "------------------------------------------\n";
229 return Standard_True;
7af17f1e
MA
230}
231
232//=======================================================================
233//function : LogFileHandler::Close
234//purpose :
235//=======================================================================
236
237void OSD_MAllocHook::LogFileHandler::Close()
238{
64531d9c 239 if (myLogFile.is_open())
7af17f1e 240 {
64531d9c 241 myLogFile.close();
7af17f1e
MA
242 }
243}
244
245//=======================================================================
246//function : LogFileHandler::MakeReport
247//purpose :
248//=======================================================================
249
250struct StorageInfo
251{
96f3bacc 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()
7af17f1e 264 {
7af17f1e 265 }
96f3bacc 266
267 bool operator < (const StorageInfo& theOther) const
7af17f1e 268 {
96f3bacc 269 return size < theOther.size;
7af17f1e
MA
270 }
271};
272
7af17f1e
MA
273Standard_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;
96f3bacc 338 aInfo.alive.insert(aReqNum);
7af17f1e
MA
339 }
340 else
341 {
342 std::set<unsigned long>::iterator aFoundReqNum =
96f3bacc 343 aInfo.alive.find(aReqNum);
344 if (aFoundReqNum == aInfo.alive.end())
7af17f1e
MA
345 // freeing non-registered block, skip it
346 continue;
347 aTotalLeftSize -= aSize;
96f3bacc 348 aInfo.alive.erase(aFoundReqNum);
7af17f1e
MA
349 if (aInfo.nbAlloc + 1 > 0)
350 aInfo.nbFree++;
351 }
352 }
353 fclose(aLogFile);
354
355 // print the report
64531d9c 356 std::ofstream aRepFile (theOutFile);
357 if(!aRepFile.is_open())
358 {
7af17f1e 359 return Standard_False;
64531d9c 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
7af17f1e
MA
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;
64531d9c 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
7af17f1e
MA
389 if (aTotAlloc + aSizeAlloc < aTotAlloc) // overflow ?
390 aTotAlloc = SIZE_MAX;
391 else
392 aTotAlloc += aSizeAlloc;
96f3bacc 393 if (theIncludeAlive && !aInfo.alive.empty())
7af17f1e 394 {
96f3bacc 395 for (std::set<unsigned long>::const_iterator it1 = aInfo.alive.begin();
396 it1 != aInfo.alive.end(); ++it1)
64531d9c 397 aRepFile << std::setw(10) << *it1;
7af17f1e
MA
398 }
399 }
64531d9c 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();
7af17f1e
MA
410 return Standard_True;
411}
412
413//=======================================================================
414//function : LogFileHandler::AllocEvent
415//purpose :
416//=======================================================================
417
418void OSD_MAllocHook::LogFileHandler::AllocEvent
419 (size_t theSize,
420 long theRequestNum)
421{
64531d9c 422 if (myLogFile.is_open())
7af17f1e
MA
423 {
424 myMutex.Lock();
64531d9c 425 myLogFile << "alloc "<< std::setw(10) << theRequestNum
426 << std::setw(20) << theSize << std::endl;
7af17f1e 427 if (myBreakSize == theSize)
302f96fb 428 place_for_breakpoint();
429 myMutex.Unlock();
7af17f1e
MA
430 }
431}
432
433//=======================================================================
434//function : LogFileHandler::FreeEvent
435//purpose :
436//=======================================================================
437
438void OSD_MAllocHook::LogFileHandler::FreeEvent
439 (void* /*theData*/,
440 size_t theSize,
441 long theRequestNum)
442{
64531d9c 443 if (myLogFile.is_open())
7af17f1e
MA
444 {
445 myMutex.Lock();
64531d9c 446 myLogFile << "free " << std::setw(20) << theRequestNum
447 << std::setw(20) << theSize << std::endl;
7af17f1e
MA
448 myMutex.Unlock();
449 }
450}
451
452//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
453// CollectBySize handler methods
454//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
455
456//=======================================================================
457//function : CollectBySize::CollectBySize
458//purpose :
459//=======================================================================
460
461OSD_MAllocHook::CollectBySize::CollectBySize()
462: myArray(NULL),
463 myTotalLeftSize(0),
464 myTotalPeakSize(0),
941a7a24 465 myBreakSize(0),
466 myBreakPeak(0)
7af17f1e
MA
467{
468 Reset();
469}
470
471//=======================================================================
472//function : CollectBySize::~CollectBySize
473//purpose :
474//=======================================================================
475
476OSD_MAllocHook::CollectBySize::~CollectBySize()
477{
478 if (myArray != NULL)
479 delete [] myArray;
480}
481
482//=======================================================================
483//function : CollectBySize::Reset
484//purpose :
485//=======================================================================
486
8cb69787 487#define MAX_ALLOC_SIZE 2000000
1cc1abe1 488const size_t OSD_MAllocHook::CollectBySize::myMaxAllocSize = MAX_ALLOC_SIZE;
7af17f1e
MA
489
490void 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
510Standard_Boolean OSD_MAllocHook::CollectBySize::MakeReport(const char* theOutFile)
511{
512 // print the report
64531d9c 513 std::ofstream aRepFile(theOutFile);
514 if (!aRepFile.is_open())
7af17f1e 515 return Standard_False;
64531d9c 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
7af17f1e
MA
527 Standard_Size aTotAlloc = 0;
528 for (int i = 0; i < MAX_ALLOC_SIZE; i++)
529 {
13b4230b 530 if (myArray[i].nbAlloc > 0 || myArray[i].nbFree > 0)
7af17f1e
MA
531 {
532 Standard_Integer nbLeft = myArray[i].nbAlloc - myArray[i].nbFree;
7af17f1e
MA
533 int aSize = i + 1;
534 Standard_Size aSizeAlloc = myArray[i].nbAlloc * aSize;
13b4230b 535 ptrdiff_t aSizeLeft = nbLeft * aSize;
7af17f1e 536 Standard_Size aSizePeak = myArray[i].nbLeftPeak * aSize;
64531d9c 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
7af17f1e
MA
546 if (aTotAlloc + aSizeAlloc < aTotAlloc) // overflow ?
547 aTotAlloc = SIZE_MAX;
548 else
549 aTotAlloc += aSizeAlloc;
550 }
551 }
64531d9c 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();
7af17f1e
MA
561 return Standard_True;
562}
563
564//=======================================================================
565//function : CollectBySize::AllocEvent
566//purpose :
567//=======================================================================
568
569void OSD_MAllocHook::CollectBySize::AllocEvent
570 (size_t theSize,
571 long /*theRequestNum*/)
572{
573 if (myBreakSize == theSize)
302f96fb 574 place_for_breakpoint();
7af17f1e
MA
575 if (theSize > 0)
576 {
577 myMutex.Lock();
578 int ind = (theSize > MAX_ALLOC_SIZE ? MAX_ALLOC_SIZE-1 : (int)(theSize-1));
13b4230b 579 myArray[ind].nbAlloc++;
7af17f1e
MA
580 myTotalLeftSize += theSize;
581 int nbLeft = myArray[ind].nbAlloc - myArray[ind].nbFree;
582 if (nbLeft > myArray[ind].nbLeftPeak)
941a7a24 583 {
7af17f1e 584 myArray[ind].nbLeftPeak = nbLeft;
941a7a24 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 }
13b4230b 595 if (myTotalLeftSize > (ptrdiff_t)myTotalPeakSize)
7af17f1e
MA
596 myTotalPeakSize = myTotalLeftSize;
597 myMutex.Unlock();
598 }
599}
600
601//=======================================================================
602//function : CollectBySize::FreeEvent
603//purpose :
604//=======================================================================
605
606void OSD_MAllocHook::CollectBySize::FreeEvent
607 (void* /*theData*/,
608 size_t theSize,
609 long /*theRequestNum*/)
610{
13b4230b 611 if (theSize > 0)
7af17f1e
MA
612 {
613 myMutex.Lock();
614 int ind = (theSize > MAX_ALLOC_SIZE ? MAX_ALLOC_SIZE-1 : (int)(theSize-1));
13b4230b 615 myArray[ind].nbFree++;
7af17f1e
MA
616 myTotalLeftSize -= theSize;
617 myMutex.Unlock();
618 }
619}