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