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