메모리 누수 Check 관련 소스 인데 출처는 잘 기억이 나지 않지만 나중에 참조 할 일이 있어서 일단 보관해 놓는다.
Memory_and_Exception_Trace.zip
Introduction
With this utility you can simply find memory leaks in your program (CRT and COM-Leaks!). Each leak is displayed with the callstack (including the source line) of the allocation. So, you can easily find leaks, while using the STL. It will also write a file with the callstack if your application crashes (it can also handle stack-overflows!). It almost has no runtime-overhead (runtime-cost). And the best: it is free (GNU Lesser General Public License).
Finding memory leaks
It is easy to implement this in your existing VC code:
Only use exception-handling
If you only want to use exception handing, you need to do the following:
Example
A simple example is given below:
#include <span class="code-keyword"><windows.h>
</span>
#include <span class="code-string">"Stackwalker.h"
</span>
void main()
{
// Uncomment the following if you only
// need the UnhandledException-Filter
// (to log unhandled exceptions)
// then you can remove the "(De)InitAllocCheck" lines
//OnlyInstallUnhandeldExceptionFilter();
InitAllocCheck();
// This shows how the mem-leak function works
char *pTest1 = new char[100];
// This shows a COM-Leak
CoTaskMemAlloc(120);
// This shows the exception handling
// and log-file writing for an exception:
// If you want to try it, please comment it out...
//char *p = NULL;
//*p = 'A'; // BANG!
DeInitAllocCheck();
}
##### Memory Report ########################################
11/07/02 09:43:56
##### Leaks: ###############################################
RequestID: 42, Removed: 0, Size: 100
1: 11/07/02 09:43:56
1: f:\vs70builds\9466\vc\crtbld\crt\src\dbgheap.c(359)
+30 bytes (_heap_alloc_dbg)
1: f:\vs70builds\9466\vc\crtbld\crt\src\dbgheap.c(260)
+21 bytes (_nh_malloc_dbg)
1: f:\vs70builds\9466\vc\crtbld\crt\src\dbgheap.c(139) +21 bytes (malloc)
1: f:\vs70builds\9466\vc\crtbld\crt\src\newop.cpp(12) +9 bytes (operator new)
1: d:\privat\memory_and_exception_trace\
memory_and_exception_trace\main.cpp(9) +7 bytes (main)
1: f:\vs70builds\9466\vc\crtbld\crt\src\crt0.c(259)
+25 bytes (mainCRTStartup)
**** Number of leaks: 1
##### COM-Leaks: ###############################################
(shortened)
**** Number of leaks: 1
Explanation
Now, I will explain the Memory-Report-File:
RequestID: 42, Removed: 0, Size: 100
This is the size of the allocated memory block.
1: f:\vs70builds\9466\vc\crtbld\crt\src\dbgheap.c(359)
+30 bytes (_heap_alloc_dbg)
This number is incremented for each complete callstack. You can ignore this.
This is the offset from this line in bytes (if a line produces more than one assembler instruction).
More options by calling InitAllocCheck
InitAllocCheck has three parameters:
Parameter name
Description
- eAllocCheckOutput
- eOutput
This is an enum for output-format. The following is possible:
ACOutput_Simple (default)This outputs the callstack as seen above.
ACOutput_AdvancedThis has a more detailed output of the callstack. For more info see here.
This outputs the leaks in an XML file so that you can read it easily from other applications or use some XSLT to transform it to a more readable format you want. For more info see here.
- BOOL
- bSetUnhandledExeptionFilter(default: TRUE)
If this is set, an UnhandledExceptionFilter will be installed. If an (unhandled) exception occurs it will write the callstack in a log file and terminate. For more info see here.
- ULONG
- ulShowStackAtAlloc (default: 0)
Notice: This works only for CRT-allocs.
Here you can specify the level of mallocs/frees logging. By default nothing will be logged in the log file at runtime. If you need to know what happens while executing the program you can specify a value. Then the malloc/free action will be logged to the file (either with or without callstack).
Valid values are:
- 0 = Do not write any output during runtime-alloc-call (default).
- 1 = Write only the alloc action (malloc, realloc, free).
- 2 = Write alloc action and callstack only for malloc/realloc.
- 3 = Write alloc action and callstack for all actions.
Log-output with more info
#include <span class="code-keyword"><windows.h>
</span>
#include <span class="code-string">"Stackwalker.h"
</span>
void main()
{
InitAllocCheck(ACOutput_Advanced);
// This shows how the mem-leak function works
char *pTest1 = new char[100];
DeInitAllocCheck();
}
And here is the (shortened) output:
##### Memory Report ########################################
11/04/02 09:04:04
##### Leaks: ###############################################
RequestID: 45, Removed: 0, Size: 100
1: 11/04/02 09:04:04
// ...
1: 5 main +49 bytes
1: Decl: main
1: Line: d:\privat\memory_and_exception_trace\main.cpp(27) +7 bytes
1: Mod: Memory_and_Exception_Trace, base: 00400000h
1: 6 mainCRTStartup +363 bytes
1: Decl: mainCRTStartup
1: Line: f:\vs70builds\9466\vc\crtbld\crt\src\crt0.c(259) +25 bytes
1: Mod: Memory_and_Exception_Trace, base: 00400000h
1: 7 _BaseProcessStart@4 +35 bytes
1: Decl: _BaseProcessStart@4
1: Mod: kernel32, base: 77e40000h
**** Number of leaks: 1
// ...
Explanation
Here, I will explain the Memory-Report-File:
RequestID: 45, Removed: 0, Size: 100
This line is the same as above:
1: 5 main +49 bytes
This number is incremented for each complete callstack. You can ignore this.
1: Decl: main
1: Line: d:\privat\memory_and_exception_trace\main.cpp(27) +7 bytes
1: Mod: Memory_and_Exception_Trace, base: 00400000h
The base address of this module.
XML output
<MEMREPORT date="11/08/02" time="10:43:47">
<LEAK requestID="47" size="100">
<!--<span class="code-comment"> shortened --></span>
<STACKENTRY decl="main" decl_offset="+100"
srcfile="d:\...\main.cpp" line="16"
line_offset="+7" module="Memory_and_Exception_Trace" base="00400000"/>
<STACKENTRY decl="mainCRTStartup" decl_offset="+363"
srcfile="f:\...\crt0.c" line="259"
line_offset="+25" module="Memory_and_Exception_Trace" base="00400000"/>
</LEAK>
</MEMREPORT>
It is pretty self explaining if you take a look at the "advanced log output".
Mem-leak-analyse tool
Information: This program requires .NET Framework 1.0!
A word on leaks
#include <span class="code-keyword"><windows.h>
</span>
#include <span class="code-keyword"><stdlib.h>
</span>
#include <span class="code-string">"stackwalker.h"
</span>
class MyTest
{
public:
MyTest(const char *szName)
{
// The following is the second resulting leak
m_pszName = strdup(szName);
}
~MyTest()
{
if (m_pszName != NULL)
free(m_pszName);
m_pszName = NULL;
}
protected:
char *m_pszName;
};
void main()
{
InitAllocCheck();
// This is the "main" leak
MyTest *pTest = new MyTest("This is an example");
DeInitAllocCheck();
}
How it works (CRT)
In detail
Hashtable
As hash-key, the lRequestID for each allocation is used. This ID is passed to the AllocHook function (at least for allocs). If it is not passed (for example, for freeing), then an (valid) address is passed. By having this address it is also possible to get thelRequestID, by looking into the _CrtMemBlockHeader of the allocated block.
For hashing, a very simple and fast hash-function is used:
static inline ULONG AllocHashFunction(long lRequestID) {
return lRequestID % ALLOC_HASH_ENTRIES;
} // AllocHashFunction
Insert an allocation into the Hashtable
Building the leak-list
If you call DeInitAllocCheck I simply walk through the Hashtable and output every entry which was not freed. For this I call StackWalk with a pointer to my own memory-reading-function (ReadProcMemoryFromHash). This function is called from the internals of StackWalk. If it is called it looks up the Hashtable for the given lRequestID and returns the memory which was stored in the Hashtable. The lRequestID is passed in the hProcess parameter of the StackWalk function (as stated in the documentation of StackWalk
Ignoring allocations
Allocations/frees for _CRT_BLOCK are ignored (for more info, see here). This is because CRT dynamically allocates some memory for "special purposes". The tool also checks the _CRTDBG_ALLOC_MEM_DF flag of the _crtDbgFlag variable. If it is off, then all the allocations are ignored. For more details see _CrtSetDbgFlag.
How it works (COM)
The storage of the callstack is done in the same way as for CRT-allocs (in a Hashtable). So, for more info please read the CRT-section.
A word on COM-leaks
Actually, there is nothing to say, but...
For more info see: Understanding the MSXML Garbage Collection Mechanism.
MFC usage
static struct _test
{
_test()
{
InitAllocCheck();
}
~_test()
{
DeInitAllocCheck();
}
} _myLeakFinder;
Temporarily disable logging (only CRT)
#include <span class="code-string">"Stackwalker.h"
</span>
#include <span class="code-keyword"><crtdbg.h>
</span>
bool EnableMemoryTracking(bool bTrack)
{
int nOldState = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
if (bTrack)
_CrtSetDbgFlag(nOldState | _CRTDBG_ALLOC_MEM_DF);
else
_CrtSetDbgFlag(nOldState & ~_CRTDBG_ALLOC_MEM_DF);
return nOldState & _CRTDBG_ALLOC_MEM_DF;
}
void main()
{
InitAllocCheck();
// The following will be logged
char *pTest1 = new char[100];
EnableMemoryTracking(false); // disable logging
// The following will NOT be logged
char *pTest2 = new char[200];
EnableMemoryTracking(true); // enable logging
// The following will be logged
char *pTest3 = new char[300];
DeInitAllocCheck();
}
Unhandled exceptions
There are three ways to use this tool for unhandled exceptions.
Simple using
Second simple using
int main()
{
OnlyInstallUnhandeldExceptionFilter();
// do your main code here...
}
Advanced using
static LONG __stdcall MyUnhandlerExceptionFilter(EXCEPTION_POINTERS* pExPtrs)
{
LONG lRet;
lRet = StackwalkFilter(pExPtrs,
EXCEPTION_EXECUTE_HANDLER, _T("\\exception.log"));
TCHAR lString[500];
_stprintf(lString,
_T("*** Unhandled Exception!\n")
_T(" ExpCode: 0x%8.8X\n")
_T(" ExpFlags: %d\n")
_T(" ExpAddress: 0x%8.8X\n")
_T(" Please report!"),
pExPtrs->ExceptionRecord->ExceptionCode,
pExPtrs->ExceptionRecord->ExceptionFlags,
pExPtrs->ExceptionRecord->ExceptionAddress);
FatalAppExit(-1,lString);
return lRet;
}
int main()
{
InitAllocCheck(ACOutput_Advanced, FALSE);
SetUnhandledExceptionFilter(MyUnhandlerExceptionFilter);
// do some stuff...
DeInitAlloocCheck();
}
Common mistakes
#include <span class="code-keyword"><windows.h>
</span>
#include <span class="code-string">"Stackwalker.h"
</span>
#include <span class="code-keyword"><string>
</span>
void main()
{
InitAllocCheck();
std::string szTemp;
szTemp = "This is a really long string";
DeInitAllocCheck();
}
#include <span class="code-keyword"><windows.h>
</span>
#include <span class="code-string">"Stackwalker.h"
</span>
#include <span class="code-keyword"><string>
</span>
void main()
{
InitAllocCheck();
{
std::string szTemp;
szTemp = "This is a really long string";
}
DeInitAllocCheck();
}
The second solution is to use the same technique that is used for MFC applications (see above).
Visual Studio 7 and Win2K / NT
To get it to work you have to do the following
Download the latest Debugging Tools for Windows (which includes dbghelp.dll). You have to install it to get the files. But you only need the dbghelp.dll! Now we have another problem. The installer does not replace the original dbghelp.dll. So we need to copy the dbghelp.dll in our EXE dir. Now to make sure the right version is loaded you have to put a file with the name appname.local in your EXE dir (please replace appname with the EXE name (without extension)). Now it should also work on WinNT/2K.
Known issues
References
History
'개발언어 > c++' 카테고리의 다른 글
동적인 메모리 디버깅하기 (0) | 2016.07.16 |
---|---|
Implementing the __FUNCTION__ Macro in VC++ 6.0 (0) | 2016.07.16 |
메모리누수(leak) 탐지소스 (0) | 2016.07.16 |
윈도우 시스템 정보 얻기 (0) | 2016.07.16 |
MFC 다국어 지원 방법(국가별 언어설정) (0) | 2016.07.15 |