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