본문 바로가기
Programming Language/C++,MFC

HOWTO: Visual C++ 어플리케이션에서 메모리 누수 추적하는 방법

by 민트초코맛꼬깔콘 2014. 12. 26.
1. https://github.com/snoopspy/vmld
2. http://support.microsoft.com/kb/601929/ko (아래글 출처)
-------------------------------------------------------------------------------------------------------------------

이 문서는 이전에 다음 ID로 출판되었음: KR601929

Collapse image이 페이지에서

Collapse image요약

Visual C++ 어플리케이션인 경우 디버그 빌드의 메모리 관련 함수를 사용하면 메모리 누수가 난 위치(파일명과 라인)를 Visual C++ 로부터 보고 받을 수 있습니다. 이들 메모리 함수에는_malloc_dbg, _calloc_dbg, _realloc_dbg, _expand_dbg, _free_dbg, _msize_dbg 및 DEBUG_NEW 등이 있습니다.

Collapse image추가 정보

MFC 가 보고하는 메모리 누수 정보

Visual C++ 의 MFC 프로그램을 개발중이라면 이 어플리케이션이 디버그 모드로 실행된 경우, 종료 시 디버그 창에 메모리 누수관련 정보가 다음과 같이 표시됩니다.
Detected memory leaks!
Dumping objects ->
plex.cpp(31) : {173587} normal block at 0x03C02F00, 124 bytes long.
 Data: <         P   j  > 00 00 00 00 00 00 00 00 C0 50 13 00 A8 6A 14 00 
map_pp.cpp(72) : {173586} normal block at 0x03C02FB0, 68 bytes long.
 Data: <                > 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
{67576} normal block at 0x04021A80, 288 bytes long.
 Data: <          L_    > 01 00 00 00 00 00 00 00 14 CB 4C 5F 01 00 CD CD 
{67575} normal block at 0x04021BD0, 288 bytes long.
 Data: <          L_    > 01 00 00 00 00 00 00 00 14 CB 4C 5F 01 00 CD CD 
{63195} normal block at 0x03CB85E0, 124 bytes long.
 Data: <    x J_    x J_> 0F 00 00 00 78 C2 4A 5F 19 08 05 96 78 C2 4A 5F 
strcore.cpp(118) : {56689} normal block at 0x03C4CB00, 19 bytes long.
 Data: <                > 01 00 00 00 06 00 00 00 06 00 00 00 B0 C5 B7 A1 
strcore.cpp(118) : {56679} normal block at 0x03C4B170, 19 bytes long.
 Data: <                > 01 00 00 00 06 00 00 00 06 00 00 00 C1 D6 B0 A1 
Object dump complete.
The thread 0xDE has exited with code 0 (0x0).
The program 'E:\Wow\BinD\Wow.exe' has exited with code 0 (0x0)
					

디버그 버전 메모리 함수

위의 내용에는 좌측에 파일 이름이 표시된 것이 있고 그렇지 못한 것이 있습니다. 파일 이름이 표시된 것은 디버그 빌드 메모리 함수를 이용하여 할당 받은 경우이고, 파일 이름이 없는 것은 그렇지 못한 경우입니다. 파일 이름을 표시하기 위해서는 요약에서 언급한 종류의 디버그 버전 함수를 사용해야 합니다.

먼저, New 함수는 MFC 프로젝트에서 기본으로 디버그 버전을 사용하도록 되어 있으며 반드시 디버그 빌드인 경우에만 파일명이 표시됩니다. Release 빌드의 경우는 누수의 위치가 표시되지 않습니다. New 함수가 디버그 빌드가 적용되는 이유는 각 CPP 파일에 다음과 같은 라인이 첫 부분에 있기 때문입니다.
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
					
기타_malloc_dbg, _calloc_dbg, _realloc_dbg, _expand_dbg, _free_dbg, _msize_dbg 함수는 crtdbg.h 에 다음과 같이 정의되어 있습니다.
#ifdef  _CRTDBG_MAP_ALLOC

#define   malloc(s)         _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
#define   calloc(c, s)      _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__)
#define   realloc(p, s)     _realloc_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__)
#define   _expand(p, s)     _expand_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__)
#define   free(p)           _free_dbg(p, _NORMAL_BLOCK)
#define   _msize(p)         _msize_dbg(p, _NORMAL_BLOCK)

#endif  /* _CRTDBG_MAP_ALLOC */
					
따라서, 다음과 같이 선언하시면 됩니다.
#define _CRTDBG_MAP_ALLOC
#include "crtdbg.h"
					

할당번호

‘MFC 가 보고하는 메모리 누수 정보’ 부분의 정보에서 “{“ 와 “}” 으로 둘러싸인 부분에 숫자가 있습니다. 이는 할당번호라고 하며 이를 이용해서 메모리 누수가 나는 곳으로 직접 Break Point 를 걸어 확인할 수도 있습니다.

예를 들어, 다음과 같은 함수가 있습니다. 이 함수는 특정 할당번호를 갖는 위치의 malloc 함수에서 멈추어서 사용자가 그 위치를 확인하게 해 줍니다.
// testbreak.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <malloc.h>
#include <crtdbg.h>

int main(int argc, char* argv[])
{

        long allocReqNum;
        char *my_pointer;

        /* 
         * Allocate "my_pointer" for the first
         * time and ensure that it gets allocated correctly
         */
        my_pointer = (char*)malloc(20);
        _CrtIsMemoryBlock(my_pointer, 10, &allocReqNum, NULL, NULL);

        /* 
         * Set a breakpoint on the allocation request
         * number for "my_pointer"
         */
        _CrtSetBreakAlloc(allocReqNum+2);
        _crtBreakAlloc = allocReqNum+2;

        /* 
         * Alternate freeing and reallocating "my_pointer"
         * to verify that the debugger halts program execution
         * when it reaches the allocation request
         */
        free(my_pointer);
        printf("wow1\n");
        my_pointer = (char*)malloc(10);
        free(my_pointer);
        printf("wow2\n");
        my_pointer = (char*)malloc(10);
        free(my_pointer);
}
					
프로그램을 디버그 모드로 실행하면 _heap_alloc_dbg 에서 멈춥니다. 여기서 Step Out 하면 차례로 _nh_malloc_dbg 를 거쳐서 malloc 을 통해서 본래 위치인 my_pointer = (char*)malloc(10); 로 돌아오므로 누수가 난 위치를 알아내는데 사용할 수 있습니다.

이 방법은 할당 번호가 고정적으로 누수 된다고 보고되는 경우에만 사용가능 합니다.

MFC 이외의 어플리케이션

MFC 는 사용자가 특별히 작업을 해주지 않아도 메모리 누수를 보고해 주지만 그 외의 Visual C++ 함수인 경우는 메모리 누수를 보고해 주는 함수인 _CrtDumpMemoryLeaks() 를 사용합니다.

콘솔 프로그램에서 누수보고 예제

// console.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
/*****************************************************************
 *  EXAMPLE  1                                                   *
 *  This simple program illustrates the basic debugging features *
 *  of the C runtime libraries, and the kind of debug output     *
 *  that these features generate.                                *
 *****************************************************************/

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <crtdbg.h>

// This routine place comments at the head of a section of debug output
void OutputHeading( const char * explanation )
{
   _RPT1( _CRT_WARN, "\n\n%s:\n**************************************\
************************************\n", explanation );
}

// The following macros set and clear, respectively, given bits
// of the C runtime library debug flag, as specified by a bitmask.
#ifdef   _DEBUG
#define  SET_CRT_DEBUG_FIELD(a) \
            _CrtSetDbgFlag((a) | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
#define  CLEAR_CRT_DEBUG_FIELD(a) \
            _CrtSetDbgFlag(~(a) & _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
#else
#define  SET_CRT_DEBUG_FIELD(a)   ((void) 0)
#define  CLEAR_CRT_DEBUG_FIELD(a) ((void) 0)
#endif


void main( )
{
   char *p1, *p2;
   _CrtMemState s1, s2, s3;

   // Send all reports to STDOUT
   _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
   _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT );
   _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );
   _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDOUT );
   _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
   _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDOUT );

   // Allocate 2 memory blocks and store a string in each
   p1 = (char*)malloc( 34 );
   strcpy( p1, "This is the p1 string (34 bytes)." );

   p2 = (char*)malloc( 34 );
   strcpy( p2, "This is the p2 string (34 bytes)." );


   OutputHeading( 
      "Use _ASSERTE to check that the two strings are identical" );
   _ASSERTE( strcmp( p1, p2 ) == 0 );

   OutputHeading( 
      "Use a _RPT macro to report the string contents as a warning" );
   _RPT2( _CRT_WARN, "p1 points to '%s' and \np2 points to '%s'\n", p1, p2 );

   OutputHeading( 
      "Use _CRTMemDumpAllObjectsSince to check the p1 and p2 allocations" );
   _CrtMemDumpAllObjectsSince( NULL );

   free( p2 );

   OutputHeading( 
      "Having freed p2, dump allocation information about p1 only" );
   _CrtMemDumpAllObjectsSince( NULL );

   // Store a memory checkpoint in the s1 memory-state structure
   _CrtMemCheckpoint( &s1 );

   // Allocate another block, pointed to by p2
   p2 = (char*)malloc( 38 );
   strcpy( p2, "This new p2 string occupies 38 bytes.");

   // Store a 2nd memory checkpoint in s2
   _CrtMemCheckpoint( &s2 );

   OutputHeading( 
      "Dump the changes that occurred between two memory checkpoints" );
   if ( _CrtMemDifference( &s3, &s1, &s2 ) )
      _CrtMemDumpStatistics( &s3 );

   // Free p2 again and store a new memory checkpoint in s2
   free( p2 );
   _CrtMemCheckpoint( &s2 );

   OutputHeading( 
      "Now the memory state at the two checkpoints is the same" );
   if ( _CrtMemDifference( &s3, &s1, &s2 ) )
      _CrtMemDumpStatistics( &s3 );

   strcpy( p1, "This new p1 string is over 34 bytes" );
   OutputHeading( "Free p1 after overwriting the end of the allocation" );
   free( p1 );

   // Set the debug-heap flag so that freed blocks are kept on the
   // linked list, to catch any inadvertent use of freed memory
   SET_CRT_DEBUG_FIELD( _CRTDBG_DELAY_FREE_MEM_DF );

   p1 = (char*)malloc( 10 );
   free( p1 );
   strcpy( p1, "Oops" );

   OutputHeading( "Perform a memory check after corrupting freed memory" );
   _CrtCheckMemory( );

   // Use explicit calls to _malloc_dbg to save file name and line number
   // information, and also to allocate Client type blocks for tracking
   p1 = (char*)_malloc_dbg( 40, _NORMAL_BLOCK, __FILE__, __LINE__ );
   p2 = (char*)_malloc_dbg( 40, _CLIENT_BLOCK, __FILE__, __LINE__ );
   strcpy( p1, "p1 points to a Normal allocation block" );
   strcpy( p2, "p2 points to a Client allocation block" );

   // You must use _free_dbg to free a Client block
   OutputHeading( 
      "Using free( ) to free a Client block causes an assertion failure" );
   free( p1 );
   free( p2 );

   p1 = (char*)malloc( 10 );
   OutputHeading( "Examine outstanding allocations (dump memory leaks)" );
   _CrtDumpMemoryLeaks( );

   // Set the debug-heap flag so that memory leaks are reported when
   // the process terminates. Then, exit.
   OutputHeading( "Program exits without freeing a memory block" );
   SET_CRT_DEBUG_FIELD( _CRTDBG_LEAK_CHECK_DF );
}

					

콘솔 프로그램에서 누수보고 예제 결과

G:\works\console\Debug>console


Use _ASSERTE to check that the two strings are identical:
**************************************************************************
G:\works\console\console.cpp(60) : Assertion failed: strcmp( p1, p2 ) == 0


Use a _RPT macro to report the string contents as a warning:
**************************************************************************
p1 points to 'This is the p1 string (34 bytes).' and
p2 points to 'This is the p2 string (34 bytes).'


Use _CRTMemDumpAllObjectsSince to check the p1 and p2 allocations:
**************************************************************************
Dumping objects ->
{45} normal block at 0x00431F40, 34 bytes long.
 Data: <This is the p2 s> 54 68 69 73 20 69 73 20 74 68 65 20 70 32 20 73
{44} normal block at 0x00431F90, 34 bytes long.
 Data: <This is the p1 s> 54 68 69 73 20 69 73 20 74 68 65 20 70 31 20 73
Object dump complete.


Having freed p2, dump allocation information about p1 only:
**************************************************************************
Dumping objects ->
{44} normal block at 0x00431F90, 34 bytes long.
 Data: <This is the p1 s> 54 68 69 73 20 69 73 20 74 68 65 20 70 31 20 73
Object dump complete.


Dump the changes that occurred between two memory checkpoints:
**************************************************************************
0 bytes in 0 Free Blocks.
38 bytes in 1 Normal Blocks.
0 bytes in 0 CRT Blocks.
0 bytes in 0 Ignore Blocks.
0 bytes in 0 Client Blocks.
Largest number used: 4 bytes.
Total allocations: 38 bytes.


Now the memory state at the two checkpoints is the same:
**************************************************************************


Free p1 after overwriting the end of the allocation:
**************************************************************************
memory check error at 0x00431FB2 = 0x73, should be 0xFD.
memory check error at 0x00431FB3 = 0x00, should be 0xFD.
DAMAGE: after Normal block (#44) at 0x00431F90.


Perform a memory check after corrupting freed memory:
**************************************************************************
memory check error at 0x00431FA0 = 0x4F, should be 0xDD.
memory check error at 0x00431FA1 = 0x6F, should be 0xDD.
memory check error at 0x00431FA2 = 0x70, should be 0xDD.
memory check error at 0x00431FA3 = 0x73, should be 0xDD.
memory check error at 0x00431FA4 = 0x00, should be 0xDD.
DAMAGE: on top of Free block at 0x00431FA0.
DAMAGED located at 0x00431FA0 is 10 bytes long.


Using free( ) to free a Client block causes an assertion failure:
**************************************************************************
dbgheap.c(1051) : Assertion failed: pHead->nBlockUse == nBlockUse


Examine outstanding allocations (dump memory leaks):
**************************************************************************
Detected memory leaks!
Dumping objects ->
{50} normal block at 0x00431EA0, 10 bytes long.
 Data: <          > CD CD CD CD CD CD CD CD CD CD
Object dump complete.


Program exits without freeing a memory block:
**************************************************************************
Detected memory leaks!
Dumping objects ->
{50} normal block at 0x00431EA0, 10 bytes long.
 Data: <          > CD CD CD CD CD CD CD CD CD CD
Object dump complete.

G:\works\console\Debug>

					

Collapse image속성

기술 자료: 601929 - 마지막 검토: 2004년 9월 21일 화요일 - 수정: 2.0
본 문서의 정보는 다음의 제품에 적용됩니다.
  • Microsoft Visual C++ 6.0 서비스 팩 5
키워드: 
kbhowto KB601929
더 이상 지원되지 않는 제품의 KB 내용에 대한 고지 사항
이 문서에서는 Microsoft에서 더 이상 지원하지 않는 제품에 대해 설명합니다. 따라서 이 문서는 "있는 그대로" 제공되며 업데이트되지 않습니다.


'Programming Language > C++,MFC' 카테고리의 다른 글

구조체 패딩(padding) 문제  (0) 2014.10.24
MFC 작업자 쓰레드(Worker Thread) 사용법  (0) 2014.10.20
MFC 콤보박스  (0) 2010.10.05
MFC에서 printf() 사용하기  (0) 2010.07.16