DCL 4.0
Loading...
Searching...
No Matches
DebugAlloc.cpp
Go to the documentation of this file.
1#define _CRT_SECURE_NO_DEPRECATE
2
3#include <dcl/Config.h>
4#if __DCL_WINDOWS
5#include <windows.h> // for dcl/Thread.h
6#endif
7
8#if __DCL_HAVE_ALLOC_DEBUG
9
10#include <stdlib.h> // malloc, free
11#include <string.h> // strcpy, strlen
12#include <wchar.h> // wcslen, wcscpy
13
14#include "LibState.h"
15
16#include <dcl/Object.h>
17#include <dcl/Thread.h>
18#include <dcl/Writer.h>
19
20#define __strlen(s) wcslen(s)
21#define __strcpy(d, s) wcscpy(d, s)
22
23#undef new
24#undef malloc
25#undef realloc
26#undef calloc
27#undef free
28
29//#ifdef __GNUC__
30//#include <dcl/_trace.h>
31//#endif
32
33#define __DCL_ASSERT_N(expr) // no assert
34
35#undef __THIS_FILE__
36static const char_t __THIS_FILE__[] = __T("dcl/DebugAlloc.cpp");
37
38__DCL_BEGIN_NAMESPACE
39
40extern LibState* __pLibState;
41
42// https://en.wikipedia.org/wiki/Thread-local_storage
43#ifdef _MSC_VER
44#define THREAD_LOCAL_STORAGE __declspec(thread)
45#elif defined(__GNUC__)
46#define THREAD_LOCAL_STORAGE __thread
47#endif
48
49static THREAD_LOCAL_STORAGE const char_t* __near_filename = NULL;
50static THREAD_LOCAL_STORAGE unsigned int __near_line = 0;
51
52__DCL_END_NAMESPACE
53
54__DCL_USING_NAMESPACE
55
56DCLCAPI void DCLDebugAllocEnterNear(
57 const char_t* _filename,
58 unsigned int _line
59 )
60{
61 __near_filename = _filename;
62 __near_line = _line;
63}
64
65/*
66static const char_t* __LimiteFileName(const char_t* _filename)
67{
68 size_t n = __strlen(_filename);
69 if (n > 128)
70 return _filename + n - 128;
71 return _filename;
72}
73 */
74
75DCLCAPI void* DCLDebugMalloc(
76 size_t _size,
77 bool _check,
78 DCLAllocFunction allocFunction,
79 const char_t* _filename,
80 unsigned int _line
81 )
82{
83 if (_filename == NULL) {
84 _filename = __near_filename;
85 _line = __near_line;
86 }
87
88 if (_size == 0)
89 {
90 DCLDebugTrace(
91 _filename, _line,
92 __T("MEMDBG Warning! malloc(size:%d)!\n"), _size
93 );
94
95 return NULL;
96 }
97
98 void* p = NULL;
99
100 AllocList& listAlloc = __pLibState->listAlloc;
101 InternalMutex& lockAlloc = __pLibState->lockAlloc;
102
103 lockAlloc.lock();
104
105 p = listAlloc.allocAddTail(
106 _size,
107 _check,
108 allocFunction,
109 _filename,
110 _line
111 );
112
113 lockAlloc.unlock();
114
115 return p;
116}
117
118static const char_t* __AllocTypeStr(DCLAllocFunction allocFunction)
119{
120 switch(allocFunction)
121 {
122 case DCL_ALLOC_DEFAULT :
123 return __T("default");
124 case DCL_ALLOC_NEW :
125 return __T("new");
126 case DCL_ALLOC_NEW_ARRAY :
127 return __T("new[]");
128 case DCL_ALLOC_NEW_OBJECT :
129 return __T("new object");
130 case DCL_ALLOC_NEW_OBJECT_ARRAY :
131 return __T("new[] object");
132 case DCL_ALLOC_DELETE :
133 return __T("delete");
134 case DCL_ALLOC_DELETE_ARRAY :
135 return __T("delete[]");
136 case DCL_ALLOC_DELETE_OBJECT :
137 return __T("delete object");
138 case DCL_ALLOC_DELETE_OBJECT_ARRAY :
139 return __T("delete[] object");
140 }
141
142 return __T("unknown!");
143}
144
145static bool __IsValidAllocFunction(
146 DCLAllocFunction allocType,
147 DCLAllocFunction freeType
148 )
149{
150 switch(allocType)
151 {
152 case DCL_ALLOC_DEFAULT :
153 return DCL_ALLOC_DEFAULT == freeType;
154 case DCL_ALLOC_NEW :
155 return DCL_ALLOC_DELETE == freeType;
156 case DCL_ALLOC_NEW_ARRAY :
157 return DCL_ALLOC_DELETE_ARRAY == freeType;
158 case DCL_ALLOC_NEW_OBJECT :
159 return DCL_ALLOC_DELETE_OBJECT == freeType;
160 case DCL_ALLOC_NEW_OBJECT_ARRAY :
161 return DCL_ALLOC_DELETE_OBJECT_ARRAY == freeType;
162 // not used for GCC warnings
163 case DCL_ALLOC_DELETE :
164 return DCL_ALLOC_NEW == freeType;
165 case DCL_ALLOC_DELETE_ARRAY :
166 return DCL_ALLOC_NEW_ARRAY == freeType;
167 case DCL_ALLOC_DELETE_OBJECT :
168 return DCL_ALLOC_NEW_OBJECT == freeType;
169 case DCL_ALLOC_DELETE_OBJECT_ARRAY :
170 return DCL_ALLOC_NEW_OBJECT_ARRAY == freeType;
171 }
172 return false;
173}
174
175DCLCAPI void DCLDebugFree(
176 void* __p,
177 DCLAllocFunction allocFunction,
178 const char_t* __filename,
179 unsigned int __line
180 )
181{
182 if (!__p)
183 {
184 DCLDebugTrace(
185 __filename, __line,
186 __T("MEMDBG Error! free(p:NULL) p is NULL assigned!\n")
187 );
188
189 DCLDebugAssert(__filename, __line, __T("p != NULL"), __T("free(p:NULL)"));
190 // throw or abort
191 }
192
193 // DCLDebugAssert, DCLDebugTrace의 출력은 메모리를 할당하는 OutputStream을 사용하기 때문에
194 // 다음의 lock() ... unlock() 내부에서 DCLDebugAssert와 DCLDebugTrace를 사용할 수 없다.
195 // lock() ... unlock() 에서는 메지시를 만들고 마지막에서 출력한다.
196
197 AllocList& listAlloc = __pLibState->listAlloc;
198 InternalMutex& lockAlloc = __pLibState->lockAlloc;
199
200 AllocList::Node* pNode = NULL;
201 bool bValidFunction;
202 char_t szAllocFile[512];
203 int nAllocLine;
204 DCLAllocFunction af;
205
206 lockAlloc.lock();
207 pNode = listAlloc.rfind(__p);
208 if (pNode)
209 {
210 bValidFunction = __IsValidAllocFunction(pNode->allocFunction, allocFunction);
211 if (!bValidFunction)
212 {
213 __strcpy(szAllocFile, pNode->szFileName);
214 nAllocLine = pNode->nLine;
215 af = pNode->allocFunction;
216 }
217 listAlloc.erase(pNode);
218 }
219 lockAlloc.unlock();
220
221 if (__filename == NULL)
222 __filename = __T("delete location");
223
224 if (pNode == NULL)
225 DCLDebugTrace(__filename, __line, __T("MEMDBG Error! Cannot found prev alloc!!\n"));
226 else if (!bValidFunction)
227 DCLDebugTrace(__filename, __line,
228 __T("MEMDBG Error! No Match allocFunction!! alloc:%ls:%d:%ls ==> free:%ls\n"),
229 szAllocFile, nAllocLine, __AllocTypeStr(af),
230 __AllocTypeStr(allocFunction)
231 );
232}
233
234DCLCAPI void* DCLDebugCalloc(
235 size_t __count,
236 size_t _size,
237 bool __check,
238 const char_t* __filename,
239 unsigned int __line
240 )
241{
242 if ((__count == 0) || (_size == 0))
243 {
244 DCLDebugTrace(
245 __filename, __line,
246 __T("MEMDBG Warning! calloc(count:%d, size:%d)! %ls(%d)\n"),
247 __count, _size
248 );
249 return NULL;
250 }
251
252 size_t nTotalSize = __count * _size;
253
254 void* result = DCLDebugMalloc(
255 nTotalSize,
256 __check,
257 DCL_ALLOC_DEFAULT,
258 __filename,
259 __line
260 );
261
262 memset(result, 0, nTotalSize);
263 return result;
264}
265
266DCLCAPI void* DCLDebugRealloc(
267 void* __oldptr,
268 size_t __newsize,
269 bool __check,
270 const char_t* __filename,
271 unsigned int __line
272 )
273{
274 if ((__oldptr == NULL) || (__newsize == 0))
275 {
276 DCLDebugTrace(
277 __filename, __line,
278 __T("MEMDBG Warning! realloc(oldptr:%p, newsize:%d)\n"),
279 __oldptr,
280 __newsize
281 );
282// return NULL;
283 }
284
285 void* newptr = NULL;
286 AllocList::Node* pOldNode = NULL;
287 char_t szOldFileName[512];
288 int nOldLine;
289 DCLAllocFunction oldAF = DCL_ALLOC_DEFAULT;
290
291 AllocList& listAlloc = __pLibState->listAlloc;
292 InternalMutex& lockAlloc = __pLibState->lockAlloc;
293
294 lockAlloc.lock();
295
296 if (__oldptr)
297 {
298 pOldNode = listAlloc.rfind(__oldptr);
299 if (pOldNode)
300 {
301 if (__newsize == 0)
302 {
303 // nNewSize가 0 이다. 노드를 삭제하고
304 // return NULL
305 listAlloc.erase(pOldNode);
306 }
307 else
308 {
309 if (pOldNode->allocFunction != DCL_ALLOC_DEFAULT)
310 {
311 __strcpy(szOldFileName, pOldNode->szFileName);
312 nOldLine = pOldNode->nLine;
313 oldAF = pOldNode->allocFunction;
314 }
315 else
316 {
317 // 노드를 realloc하고 update한다.
318
319 AllocList::Node* pNewNode =
320 listAlloc.reallocNode(
321 pOldNode,
322 __newsize,
323 __filename,
324 __line
325 );
326 if (pNewNode)
327 newptr = pNewNode->data();
328 }
329 }
330 }
331 }
332 else // NULL == oldptr
333 {
334 if (__newsize)
335 newptr = listAlloc.allocAddTail(
336 __newsize,
337 __check,
338 DCL_ALLOC_DEFAULT, //allocFunction
339 __filename,
340 __line
341 );
342 }
343
344 lockAlloc.unlock();
345
346 if (pOldNode == NULL)
347 DCLDebugTrace(__filename, __line, __T("MEMDBG Error! Cannot found prev alloc!!\n"));
348 else if (oldAF != DCL_ALLOC_DEFAULT)
349 DCLDebugTrace(__filename, __line,
350 __T("MEMDBG Error! Invalid realloc!! prev:%ls:%d:%ls ==> realloc\n"),
351 szOldFileName, nOldLine, __AllocTypeStr(oldAF)
352 );
353
354 return newptr;
355}
356
357DCLCAPI bool DCLDebugAllocIsValid(const void* _ptr)
358{
359 bool bFound = false;
360
361 AllocList& listAlloc = __pLibState->listAlloc;
362 InternalMutex& lockAlloc = __pLibState->lockAlloc;
363
364 lockAlloc.lock();
365 bFound = listAlloc.find(_ptr) != NULL;
366 lockAlloc.unlock();
367
368 return bFound;
369}
370
371DCLCAPI void DCLDebugAllocSetCheckFlag(
372 const void* _ptr,
373 bool _check
374 )
375{
376 AllocList& listAlloc = __pLibState->listAlloc;
377 InternalMutex& lockAlloc = __pLibState->lockAlloc;
378
379 lockAlloc.lock();
380 AllocList::Node* pNode = listAlloc.rfind(_ptr);
381 if (pNode)
382 pNode->bCheck = _check;
383 lockAlloc.unlock();
384}
385
386DCLCAPI bool DCLDebugAllocGetPosition(
387 const void* _ptr,
388 char_t* _pfilename,
389 size_t _count,
390 unsigned int* _pline
391 )
392{
393 AllocList& listAlloc = __pLibState->listAlloc;
394 InternalMutex& lockAlloc = __pLibState->lockAlloc;
395
396 lockAlloc.lock();
397 AllocList::Node* pNode = listAlloc.rfind(_ptr);
398 if (pNode) {
399 wcsncpy(_pfilename, pNode->szFileName, _count);
400 *_pline = pNode->nLine;
401 }
402 lockAlloc.unlock();
403
404 return pNode != NULL;
405}
406
407/*
408return : uThreadId가 할당한 마지막 포지션 항목이 없으면 NULL
409*/
410DCLCAPI const void* DCLDebugGetLastAllocPosition(
411 unsigned long uThreadId
412 )
413{
414 const void* pv = NULL;
415
416 AllocList& listAlloc = __pLibState->listAlloc;
417 InternalMutex& lockAlloc = __pLibState->lockAlloc;
418
419 lockAlloc.lock();
420 AllocList::Node* pNode = listAlloc.end();
421 while(pNode)
422 {
423 if (pNode->uThreadId == uThreadId)
424 {
425 pv = (const void*)pNode;
426 break;
427 }
428
429 listAlloc.prev(pNode);
430 }
431 lockAlloc.unlock();
432
433 return pv;
434}
435
436/*
437uThreadId가 pvBegin에서 pvEnd까지의 메모리 누출 보고
438return : 메모리 누출 항목의 갯수 >= 0
439*/
440DCLCAPI size_t DCLDebugDumpThreadMemoryLeak(
441 unsigned long uThreadId,
442 const void* pvStartPosition,
443 DCLAllocLeakDumpLevel level,
444 __DCL_NAMESPACE Writer* pWriter
445 )
446{
447 AllocList& listAlloc = __pLibState->listAlloc;
448 InternalMutex& lockAlloc = __pLibState->lockAlloc;
449
450 lockAlloc.lock();
451
452 AllocList::Node* pStartNode = listAlloc.begin();
453 AllocList::Node* pNode = pStartNode;
454 if (pvStartPosition)
455 {
456 while(pNode)
457 {
458 if (pvStartPosition == (const void*)pNode)
459 {
460 listAlloc.next(pNode);
461 pStartNode = pNode;
462 break;
463 }
464 listAlloc.next(pNode);
465 }
466 }
467
468
469 // uThreadId인 노드 갯수
470 size_t nCount = 0;
471 pNode = pStartNode;
472 while(pNode)
473 {
474 if (pNode->uThreadId == uThreadId)
475 {
476 nCount++;
477 }
478 listAlloc.next(pNode);
479 }
480
481 AllocList::Node* pLeaks = NULL;
482 if (nCount)
483 {
484 // 메모리 누출이 있으면 이를 출력할 임시 버퍼를 할당한다.
485 // pWriter에서 메모리할당을 할 경우 그대로 사용하면 deadlock이 발생한다.
486 // __allocLock 이 lock 되어 있다.
487 pLeaks = (AllocList::Node*) malloc(sizeof(AllocList::Node) * nCount);
488 if (!pLeaks)
489 {
490 // 메모리를 할당할 수 없다.
491 lockAlloc.unlock();
492 return -1;
493 }
494
495 pNode = pStartNode;
496 int i = 0;
497 while(pNode)
498 {
499 if (pNode->uThreadId == uThreadId)
500 memcpy(&pLeaks[i++], pNode, sizeof(AllocList::Node));
501
502 listAlloc.next(pNode);
503 }
504 __DCL_ASSERT_N(i == nCount);
505 }
506
507 lockAlloc.unlock();
508
509 // __allocLock이 unlock 되었으므로 OutputStream은 출력 중에 메모리를 할당할 수 있다.
510 if (nCount)
511 {
512 bool bHeaderPrint = false;
513 bool bDoPrint = false;
514
515 pNode = pLeaks;
516 for(size_t i = 0; i < nCount; i++)
517 {
518 switch(level)
519 {
520 default :
521 case DCL_ALLOC_DUMP_ALL :
522 bDoPrint = true;
523 break;
524 case DCL_ALLOC_DUMP_INTERNAL :
525 bDoPrint = !(pNode->bCheck);
526 break;
527 case DCL_ALLOC_DUMP_USER :
528 bDoPrint = pNode->bCheck;
529 break;
530 }
531
532 if (bDoPrint)
533 {
534 if (!bHeaderPrint)
535 {
536 pWriter->printf(__T("Thread memory leak!\n thread:%u\n"), uThreadId);
537 bHeaderPrint = true;
538 }
539
540 try
541 {
542 pWriter->printf(L" %2d:%lc:%ls:%d:%ls:%zdbytes:%p\n",
543 i + 1,
544 pNode->bCheck ? L'U' : L'I',
545 *(pNode->szFileName) ? pNode->szFileName : L"(unknown)",
546 pNode->nLine,
547 __AllocTypeStr(pNode->allocFunction),
548 pNode->nSize,
549 pNode->data()
550 );
551 }
552 catch(IOException* e)
553 {
554 e->destroy();
555 // OutputStream 출력중에 에러가 발생했다.
556 nCount = -1;
557 break;
558 }
559 }
560 pNode++;
561 } // end of for
562 free(pLeaks);
563 }
564
565 return nCount;
566}
567
568DCLCAPI size_t DCLDebugDumpGlobalMemoryLeak(
569 DCLAllocLeakDumpLevel level,
570 __DCL_NAMESPACE Writer* pWriter
571 )
572{
573 AllocList::Node* pNode = NULL;
574 AllocList::Node* pLeaks = NULL;
575
576 AllocList& listAlloc = __pLibState->listAlloc;
577 InternalMutex& lockAlloc = __pLibState->lockAlloc;
578
579 lockAlloc.lock();
580 size_t nCount = listAlloc.count();
581 if (nCount > 0)
582 {
583 // 메모리 누출이 있으면 이를 출력할 임시 버퍼를 할당한다.
584 // pWriter에서 메모리할당을 할 경우 그대로 사용하면 deadlock이 발생한다.
585 // __allocLock 이 lock 되어 있다.
586 pLeaks = (AllocList::Node*)malloc(sizeof(AllocList::Node) * nCount);
587 if (!pLeaks)
588 {
589 // 메모리를 할당할 수 없다.
590 lockAlloc.unlock();
591 return -1;
592 }
593
594 pNode = listAlloc.begin();
595 size_t i = 0;
596 while(pNode)
597 {
598 memcpy(&pLeaks[i++], pNode, sizeof(AllocList::Node));
599
600 listAlloc.next(pNode);
601 }
602 __DCL_ASSERT_N(i == nCount);
603 }
604
605 lockAlloc.unlock();
606
607 // __allocLock이 unlock 되었으므로 OutputStream은 출력 중에 메모리를 할당할 수 있다.
608 if (nCount)
609 {
610 bool bHeaderPrint = false;
611 bool bDoPrint = false;
612
613 pNode = pLeaks;
614 for(size_t i = 0; i < nCount; i++)
615 {
616 switch(level)
617 {
618 default :
619 case DCL_ALLOC_DUMP_ALL :
620 bDoPrint = true;
621 break;
622 case DCL_ALLOC_DUMP_INTERNAL :
623 bDoPrint = !(pNode->bCheck);
624 break;
625 case DCL_ALLOC_DUMP_USER :
626 bDoPrint = pNode->bCheck;
627 break;
628 }
629
630 if (bDoPrint)
631 {
632 if (!bHeaderPrint)
633 {
634 pWriter->printf(L"Global memory leak!\n");
635 bHeaderPrint = true;
636 }
637
638 try
639 {
640 pWriter->printf(L" %2d:%u:%lc:%ls:%d:%ls:%zdbytes:%p\n",
641 i + 1,
642 pNode->uThreadId,
643 pNode->bCheck ? L'U' : L'I',
644 *(pNode->szFileName) ? pNode->szFileName : L"(unknown)",
645 pNode->nLine,
646 __AllocTypeStr(pNode->allocFunction),
647 pNode->nSize,
648 pNode->data()
649 );
650 }
651 catch(IOException* e)
652 {
653 e->destroy();
654 // OutputStream 출력중에 에러가 발생했다.
655 nCount = -1;
656 break;
657 }
658 }
659 pNode++;
660 }
661 free(pLeaks);
662 }
663
664 return nCount;
665}
666
668// C++ new/delete
670
671DCLCAPI void* operator new(
672 size_t _size,
673 bool _check,
674 const char_t* _filename,
675 unsigned int _line
676 )
677{
678 return DCLDebugMalloc(
679 _size == 0 ? 1 : _size,
680 _check,
681 DCL_ALLOC_NEW,
682 _filename,
683 _line
684 );
685}
686
687DCLCAPI void* operator new[](
688 size_t _size,
689 bool _check,
690 const char_t* _filename,
691 unsigned int _line
692 )
693{
694 return DCLDebugMalloc(
695 _size == 0 ? 1 : _size,
696 _check,
697 DCL_ALLOC_NEW_ARRAY,
698 _filename,
699 _line
700 );
701}
702
703DCLCAPI void operator delete(
704 void* _ptr,
705 bool ,// _check
706 const char_t* _filename,
707 unsigned int _line
708 )
709{
710 DCLDebugFree(
711 _ptr,
712 DCL_ALLOC_DELETE,
713 _filename,
714 _line
715 );
716}
717
718DCLCAPI void operator delete[](
719 void* _ptr,
720 bool ,// _check
721 const char_t* _filename,
722 unsigned int _line
723 )
724{
725 __DCL_TRACE0(L"delete[] _ptr bool..\n");
726 DCLDebugFree(
727 _ptr,
728 DCL_ALLOC_DELETE_ARRAY,
729 _filename,
730 _line);
731}
732
733
734#if !__DCL_WINDOWS
735// 원칙적으로 디버그 버전의 라이브러리에서는 다음의 연산자들이 사용될 수 없으나
736// 디버그 버전과 릴리즈 버전이 같은 프로세스에서 함께 사용될 경우
737// 릴리즈 버전의 new, new[], delete, delete[] 연산자가 디버그 버전에서 정의한
738// 연산자의 구현이 호출된다.
739// 다음의 new, new[]가 호출되면 이들의 호출기록을 저장하고 있다가
740// delete, delete[]에서 검사한다.
741DCLCAPI void* operator new(size_t _size)
742{
743 void* r = DCLDebugMalloc(
744 _size == 0 ? 1 : _size,
745 true,
746 DCL_ALLOC_NEW,
747 NULL,
748 0
749 );
750 // fprintf(stderr, "%ls:%d [%zd][%p]\n",
751 // __near_filename ? __near_filename : L"(unknown)",
752 // __near_line, _size, r);
753 return r;
754}
755
756DCLCAPI void* operator new[](size_t _size)
757{
758 void* r = DCLDebugMalloc(
759 _size == 0 ? 1 : _size,
760 true,
761 DCL_ALLOC_NEW_ARRAY,
762 NULL,
763 0
764 );
765 // fprintf(stderr, "%ls:%d [%zd][%p]\n",
766 // __near_filename ? __near_filename : L"(unknown)",
767 // __near_line, _size, r);
768 return r;
769}
770
771DCLCAPI void operator delete(void* _ptr)
772{
773 // fprintf(stderr, "%ls:%d [%p]\n",
774 // __near_filename ? __near_filename : L"(unknown)",
775 // __near_line, _ptr);
776 DCLDebugFree(_ptr, DCL_ALLOC_DELETE, NULL, 0);
777}
778
779DCLCAPI void operator delete[](void* _ptr)
780{
781 // fprintf(stderr, "%ls:%d [%p]\n",
782 // __near_filename ? __near_filename : L"(unknown)",
783 // __near_line, _ptr);
784 DCLDebugFree(_ptr, DCL_ALLOC_DELETE_ARRAY, NULL, 0);
785}
786
787#endif // !__DCL_WINDOWS
788#endif // __DCL_HAVE_ALLOC_DEBUG
#define __THIS_FILE__
Definition _trace.h:14
#define NULL
Definition Config.h:340
#define DCLCAPI
Definition Config.h:100
wchar_t char_t
Definition Config.h:275
__DCL_BEGIN_NAMESPACE LibState * __pLibState
Definition LibMain.cpp:62
#define __strcpy(d, s)
Definition LibState.cpp:23
#define __DCL_ASSERT_N(expr)
Definition LibState.cpp:27
#define __DCL_TRACE0(psz)
Definition Object.h:375
#define __T(str)
Definition Object.h:44
ByteString r
return result
virtual void destroy()
Definition Exception.cpp:74
void unlock()
Definition LibState.cpp:62