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