1. AutoMemoryLeak.h
 => #include "AutoMemoryLeak.h" 를 해주기만 하면 출력창에 누수가 난곳을 출력해준다.
#if !defined (__AutoDetectMemoryLeak_h__)
#define __AutoDetectMemoryLeak_h__

#if defined(_MSC_VER) && defined (_DEBUG)
#define _CRTDBG_MAP_ALLOC // 메모리 누수를 탐지하기 위해 선언 해주어야 한다.
#include <crtdbg.h>
#if !defined (_CONSOLE)
#include <cstdlib> // for Consol Application
#endif

class __AutoDetectMemoryLeak
{
public:
    __AutoDetectMemoryLeak ()
    {
        _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF |
            _CRTDBG_LEAK_CHECK_DF);

        // Consol Application인 경우
#if defined (_CONSOLE)
        // 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 );


        /* new로 할당된 메모리에 누수가 있을 경우 소스상의
        정확한 위치를 덤프해준다.
        *  ※ _AFXDLL을 사용할때는 자동으로 되지만 CONSOLE
        모드에서 아래처럼
        * 재정의를 해주어야만 합니다.
        *    date: 2000-12-05
        */

#define DEBUG_NORMALBLOCK   new ( _NORMAL_BLOCK, __FILE__, __LINE__ )
#ifdef new
#undef new
#endif
#define new DEBUG_NORMALBLOCK

#else

        // Send all reports to DEBUG window
        _CrtSetReportMode( _CRT_WARN,  
            _CRTDBG_MODE_DEBUG  );
        _CrtSetReportMode( _CRT_ERROR, 
            _CRTDBG_MODE_DEBUG  );
        _CrtSetReportMode( _CRT_ASSERT,
            _CRTDBG_MODE_DEBUG  );

#endif

#ifdef malloc
#undef malloc
#endif
        /*
        * malloc으로 할당된 메모리에 누수가 있을 경우 위치
        를 덤프
        * CONSOLE 모드일 경우 crtdbg.h에 malloc이 정의되어
        있지만,
        * _AXFDLL 모드일 경우에는 약간 다른방식으로 덤프 하
        게된다.
        * date: 2001-01-30
        */

#define malloc(s) (_malloc_dbg( s, _NORMAL_BLOCK, __FILE__, __LINE__ ))
    }
};

// 몇 가지 초기화를 생성자를 통해 자동으로 해주기 위해 전역으로 선언한다.
static __AutoDetectMemoryLeak __autoDetectMemoryLeak;

#endif // if defined(_MSC_VER) && defined (_DEBUG)

#endif // __AutoDetectMemoryLeak_h__



1. MiniDump.h
#pragma once

class CMiniDump
{
public:
    static BOOL Begin(VOID);
    static BOOL End(VOID);
};

2. MiniDump.cpp
#include "stdafx.h"
#include "MiniDump.h"
#include <DbgHelp.h>


typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)( // Callback 함수의 원형
    HANDLE hProcess,
    DWORD dwPid,
    HANDLE hFile,
    MINIDUMP_TYPE DumpType,
    CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
    CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
    CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);

LPTOP_LEVEL_EXCEPTION_FILTER PreviousExceptionFilter = NULL;

LONG WINAPI UnHandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionInfo)
{
    HMODULE    DllHandle        = NULL;

    // Windows 2000 이전에는 따로 DBGHELP를 배포해서 설정해 주어야 한다.
    DllHandle                = LoadLibrary(_T("DBGHELP.DLL"));

    if (DllHandle)
    {
        MINIDUMPWRITEDUMP Dump = (MINIDUMPWRITEDUMP) GetProcAddress(DllHandle, "MiniDumpWriteDump");

        if (Dump)
        {
            TCHAR        DumpPath[MAX_PATH] = {0,};
            SYSTEMTIME    SystemTime;

            GetLocalTime(&SystemTime);

            _sntprintf(DumpPath, MAX_PATH, _T("%d-%d-%d %d_%d_%d.dmp"),
                SystemTime.wYear,
                SystemTime.wMonth,
                SystemTime.wDay,
                SystemTime.wHour,
                SystemTime.wMinute,
                SystemTime.wSecond);
           
            HANDLE FileHandle = CreateFile(
                DumpPath,
                GENERIC_WRITE,
                FILE_SHARE_WRITE,
                NULL, CREATE_ALWAYS,
                FILE_ATTRIBUTE_NORMAL,
                NULL);

            if (FileHandle != INVALID_HANDLE_VALUE)
            {
                _MINIDUMP_EXCEPTION_INFORMATION MiniDumpExceptionInfo;
               
                MiniDumpExceptionInfo.ThreadId            = GetCurrentThreadId();
                MiniDumpExceptionInfo.ExceptionPointers    = exceptionInfo;
                MiniDumpExceptionInfo.ClientPointers    = NULL;

                BOOL Success = Dump(
                    GetCurrentProcess(),
                    GetCurrentProcessId(),
                    FileHandle,
                    MiniDumpNormal,
                    &MiniDumpExceptionInfo,
                    NULL,
                    NULL);

                if (Success)
                {
                    CloseHandle(FileHandle);

                    return EXCEPTION_EXECUTE_HANDLER;
                }
            }

            CloseHandle(FileHandle);
        }
    }

    return EXCEPTION_CONTINUE_SEARCH;
}

BOOL CMiniDump::Begin(VOID)
{
    SetErrorMode(SEM_FAILCRITICALERRORS);

    PreviousExceptionFilter = SetUnhandledExceptionFilter(UnHandledExceptionFilter);

    return true;
}

BOOL CMiniDump::End(VOID)
{
    SetUnhandledExceptionFilter(PreviousExceptionFilter);

    return true;
}

3. 사용예제
class MyObject
{
public:
    BOOL mIsOpened;
};
//////////////////////////////////////////////////////////////////////
    CMiniDump::Begin();

    MyObject* Test = new MyObject;
    Test = NULL;
    Test->mIsOpened = TRUE;    

    CMiniDump::End();
//////////////////////////////////////////////////////////////////////
// 1. 디버그모드로 생성된 exe를 실행시 dmp 파일이 생성된다.!!
// 2. 2007-11-7 23_22_39.dmp ( Crash Dump File ) 이 생성된다.
// 3. vc80.pdb ( Program Debug Database ) 가 같은 폴더내에 있어야 에러위치를 볼수 있다.
// 4. dmp 파일을 실행시킨후 디버깅 해주면 에러난곳에 멈춘다.
//////////////////////////////////////////////////////////////////////


Tag | ,

멀티스레드에 안전해지자.

from Study/Network 2007/11/07 20:10 view 29693
- CriticalSection 의 Enter, Leave 를 지역변수로 선언하여 데드락을 피하는 클래스

1.  CRITICAL_SECTION의 기능형 클래스
#pragma once

class CCriticalSection
{
public:
    CCriticalSection(VOID)
    {
        InitializeCriticalSection(&mSync);
    }

    ~CCriticalSection(VOID)
    {
        DeleteCriticalSection(&mSync);
    }

    inline VOID Enter(VOID)
    {
        EnterCriticalSection(&mSync);
    }

    inline VOID Leave(VOID)
    {
        LeaveCriticalSection(&mSync);
    }

private:
    CRITICAL_SECTION    mSync;
};

2. Enter, Leave를 지역변수화.
#pragma once

template <class T>
class CMultiThreadSync
{
friend class CThreadSync;
public:
    class CThreadSync
    {
    public:
        CThreadSync(VOID)
        {
            T::mSync.Enter();
        }

        ~CThreadSync(VOID)
        {
            T::mSync.Leave();
        }
    };

private:
    static CCriticalSection mSync;
};

template <class T>
CCriticalSection CMultiThreadSync<T>::mSync;


- 사용 예제 ( 메모리 풀 )

more..



포인터 변수의 임시변수 문제.

from Study/Error 2007/11/07 12:21 view 28577
아래와 같이 소켓이 생성될때 소켓을 복사해주고 소멸시킬때 close 해줄 때 임시변수는 큰 문제가 된다.

CClientSocket::CClientSocket( SOCKET client )
: m_client( client )
{
}

CClientSocket::~CClientSocket(void)
{
    Disconnect();
}

void CClientSocket::Disconnect()
{
    if( m_client == INVALID_SOCKET )
        return;

    closesocket( m_client );
    m_client = INVALID_SOCKET;
}


vector<CClientSocket> g_Clients;

       SOCKET client = server.Accept(&clientInfo);
        assert( client != INVALID_SOCKET );

        g_Clients.push_back( client );
=> push_back 할때 임시 변수가 생성되는데 이때 생성자가 또 호출되게 된다. 그리고 사라진다.
이것이 문제다. client는 서로간의 참조관계가 되므로 같이 죽게 된다. client는 SOCKET이고 g_Clients는 CClientSocket 이므로 암시적인 형변환이라고 해야하나 이를 위해 임시변수가 생성되는것이다.

vector<SOCKET> g_Clients;
=> 그런데 SOCKET으로 한다면 죽지 않는다. 임시변수를 생성하지 않아서 그런거 같은데 -_-.. 모르겠다. 


 소멸자에서 특정 포인터를 닫을 경우엔 조심 또 조심하자. 임시변수가 생성되도 소멸자가 호출되니깐.!!
Tag |