디버깅시 new 함수 메크로는 당췌 어떻게 구현한거지?

어떻게 작성하는거지?!
예를 들어 MFC의 DEBUG_NEW


 int* tmp = new int[10];


이 디버깅 시에는


int* tmp = (int*)(::operator new(sizeof(int)*10, __FILE__, __LINE__));


으로 재정의 되야 되는데 이걸 어떻게 매크로로 만든거지?!

좀 찾아봐야 겠군.

ps1.
1. int* tmp = new int[10] -> new 는 DEBUG_NEW 로 치환된다
2. int* tmp = DEBUG_NEW int[10] -> DEBUG_NEW 는 new(THIS_FILE, __LINE__) 으로 치환된다
3. int* tmp = new(THIS_FILE, __LINE__) int[10] 

????

흠…

GDI+ 의 메모리 릭 디텍트 방법

음.. 오늘 GDI+ 에서 memory leak 이 발생할 경우 비주얼스튜디오가 전혀 감지를 못하는걸 보고 미친듯이 뷁뷁을 외쳐주다가 찾은자료.

http://www.codeproject.com/vcpp/gdiplus/gdiplush.asp

일단 읽어보니 GDI+ 의 경우 자체적인 메모리 할당 연산자를 사용하는데 이것이 Gdiplus.dll 내부에서 어찌어찌 쌓여서.. 비주얼 스튜디오에서는 감지를 못하는 상황인듯 하다.
위에서 사용한 방법은 특정 메크로가 정의 되어있지 않을때(!)는 GdiplusBase 객체를 자체적으로 다시 정의해서 내부의 메모리 할당 연산을 일반적인 new/delete (윈도우에서 가장 기본적으로 제공함으로써 비주얼스튜디오에서 감지가능한것)을 사용하게 하는것이다.

위의 URL에서 다운을 받으면 헤더 파일이 나오는데 그걸 Gdiplus.h 대신 인클루드 하고 사용하면 된다.

MFC에서 new 를 DEBUG_NEW로 재정의 하는 것과 비슷한… 거시기.

아래는 위의 URL의 소스를 기억용으로 한글로 주석 달아놓은것
아직 깔끔하게 적용하지 못해서 좀 찝찝하다.
예를 들어 메모리 릭이 났을 경우 소스파일의 라인을 출력해야 되는데 이건 그냥 저 헤더파일을 출력한다. 메크로로 잘 정의하면 될것 같긴 한데 느므느므 귀찮다.



GeSHi © 2004, Nigel McNie



  1. #ifndef _GDIPLUSH_H_INCLUDED_


  2. #define _GDIPLUSH_H_INCLUDED_


  3.  


  4. /// @file               AntGdiplusHelper.h


  5. /// @brief              GDI+ 메모리 연산자 재정의 헤더파일. 메모리 누수시 감지를 위함. 


  6. /// @warning    Gdiplus.h 대신 해당 파일을 인클루드 해야 함.


  7. /// @warning    Debug 에서는 GDIPLUS_USE_GDIPLUS_MEM 정의 하지 않음 -> Gdiplus 메모리 연산자를 사용하지 않고 릭 감지 가능


  8. /// @warning    Release 에서는 GDIPLUS_USE_GDIPLUS_MEM를 정의함 -> Gdiplus 메모리 연산자를 사용하지 않고 릭 감지 못함


  9.  


  10. //


  11. // GDI+ helper file v1.0


  12. // Written by Zoltan Csizmadia (zoltan_csizmadia@yahoo.com)


  13. //


  14.  


  15. // GDIPLUS_USE_GDIPLUS_MEM:


  16. // 위의 메크로가 정의 되어있을때 GDI+ 에서 제공하는 메모리 할당 함수를 사용한다.


  17. // 이 경우 _CrtXXXXX에 해당하는 메모리 디버깅용 함수가 일반적으로 작동하지 않는다.


  18. // (원래 GDI+는 _CrtXXXX 가 작동하지 못했다. 즉 해당 메크로를 설정하는게 기본설정.)


  19. //#define GDIPLUS_USE_GDIPLUS_MEM


  20.  


  21.  


  22. #ifdef _GDIPLUS_H


  23. #error Gdiplus.h is already included. You have to include this file instead.


  24. #endif


  25.  


  26.  


  27. #define _GDIPLUSBASE_H


  28.  


  29. namespace Gdiplus


  30. {


  31.     namespace DllExports


  32.     {


  33.         #include “GdiplusMem.h”


  34.     };


  35.  


  36.     class GdiplusBase


  37.     {


  38.     public:


  39. #ifdef _DEBUG


  40.         static void* __cdecl GdiplusAlloc( size_t nSize, LPCSTR szFileName, int nLine )


  41.         {


  42. #ifdef GDIPLUS_USE_GDIPLUS_MEM


  43.             UNREFERENCED_PARAMETER(szFileName);


  44.             UNREFERENCED_PARAMETER(nLine);


  45.             return DllExports::GdipAlloc(nSize);


  46. #else


  47.             return ::operator new( nSize, szFileName, nLine );


  48. #endif  // GDIPLUS_USE_GDIPLUS_MEM


  49.         }


  50.  


  51.         static void GdiplusFree( void* pVoid, LPCSTR szFileName, int nLine )


  52.         {


  53. #ifdef GDIPLUS_USE_GDIPLUS_MEM


  54.             UNREFERENCED_PARAMETER(szFileName);


  55.             UNREFERENCED_PARAMETER(nLine);


  56.             DllExports::GdipFree(pVoid);


  57. #else


  58.             ::operator delete( pVoid, szFileName, nLine );


  59. #endif  // GDIPLUS_USE_GDIPLUS_MEM


  60.         }


  61.        


  62.         void* (operator new)(size_t nSize)


  63.         {


  64.             return GdiplusAlloc( nSize, __FILE__, __LINE__ );


  65.         }


  66.         void* (operator new[])(size_t nSize)


  67.         {


  68.             return GdiplusAlloc( nSize, __FILE__, __LINE__ );


  69.         }


  70.         void * (operator new)(size_t nSize, LPCSTR lpszFileName, int nLine)


  71.         {


  72.             return GdiplusAlloc( nSize, lpszFileName, nLine );


  73.         }


  74.         void (operator delete)(void* pVoid)


  75.         {


  76.             GdiplusFree( pVoid, __FILE__, __LINE__ );


  77.         }


  78.         void (operator delete[])(void* pVoid)


  79.         {


  80.             GdiplusFree( pVoid, __FILE__, __LINE__ );


  81.         }


  82.         void operator delete(void* pVoid, LPCSTR lpszFileName, int nLine)


  83.         {


  84.             GdiplusFree( pVoid, lpszFileName, nLine);


  85.         }


  86. #else // _DEBUG


  87.  


  88.         static void* __cdecl GdiplusAlloc( size_t nSize )


  89.         {


  90. #ifdef GDIPLUS_USE_GDIPLUS_MEM


  91.             return DllExports::GdipAlloc(nSize);


  92. #else


  93.             return ::operator new(nSize);


  94. #endif  // GDIPLUS_USE_GDIPLUS_MEM


  95.         }


  96.  


  97.         static void GdiplusFree( void* pVoid )


  98.         {


  99. #ifdef GDIPLUS_USE_GDIPLUS_MEM


  100.             DllExports::GdipFree(pVoid);


  101. #else


  102.             ::operator delete( pVoid );


  103. #endif  // GDIPLUS_USE_GDIPLUS_MEM


  104.         }


  105.  


  106.         void* (operator new)(size_t nSize)


  107.         {


  108.             return GdiplusAlloc( nSize );


  109.         }


  110.         void* (operator new[])(size_t nSize)


  111.         {


  112.             return GdiplusAlloc( nSize );


  113.         }


  114.         void (operator delete)(void* pVoid)


  115.         {


  116.             GdiplusFree( pVoid );


  117.         }


  118.         void (operator delete[])(void* pVoid)


  119.         {


  120.             GdiplusFree( pVoid );


  121.         }


  122. #endif  // _DEBUG


  123.     };


  124. };


  125.  


  126. #include <Gdiplus.h>


  127.  


  128. #endif


  129.  

Parsed in 0.179 seconds

닥터왓슨 사용법

http://serious-code.net/moin.cgi/DrWatson

위의 URL참조
아직은 왓슨에서 생성한 덤프파일 사용법을 잘 모르겠다. -_-;

ps. Dr. Watson 도움말 추가

Dr. Watson 설정

Dr. Watson(Drwtsn32.exe)은 Windows를 설치할 때 시스템 폴더에 설치됩니다. Dr. Watson을 처음 실행하면 기본 옵션이 설정됩니다. 프로그램 오류가 발생하거나 Dr. Watson을 직접 시작하면 Dr. Watson이 처음으로 실행됩니다.

Windows에 프로그램 오류가 발생하면 시스템은 프로그램 오류 처리기를 검색합니다. 프로그램 오류 처리기가 프로그램 실행 시 발생하는 오류를 처리하기 때문입니다. 프로그램 오류 처리기를 찾지 못할 경우 시스템은 현재 프로그램 디버깅이 진행되지 않는다는 것을 확인하고 오류가 처리되지 않은 것으로 간주합니다. 그런 다음 시스템은 레지스트리 편집기에서 프로그램 오류 디버거를 찾아 처리되지 않은 오류를 처리합니다.

시스템은 레지스트리 편집기의 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug 키에서 Debugger 및 Auto라는 항목을 찾습니다. Debugger 항목의 값은 프로그램 오류를 해석하는 데 사용되는 디버거 명령을 지정합니다. Debugger 항목의 값이 발견되면 시스템은 Auto 항목의 값이 0이나 1로 설정되어 있는지 확인합니다.

  • Auto 항목의 값이 0으로 설정되어 있으면 시스템은 프로그램 오류가 발생했다는 사실을 알려주는 메시지 상자를 만듭니다. Debugger 항목의 값이 유효한 디버거 명령을 지정하는 경우 메시지 상자에는 확인취소 단추가 표시됩니다. 확인을 클릭하면 프로그램이 종료되고, 취소를 클릭하면 지정한 디버거가 시작됩니다. 디버거 항목의 값이 비어 있으면 메시지 상자에 확인만 표시되며 디버거가 시작되지 않습니다.
  • Auto 항목의 값이 1로 설정되어 있고 Debugger 항목의 값이 유효한 디버거 명령을 지정하는 경우 시스템이 자동으로 디버거를 시작하고 메시지 상자를 만들지 않습니다.

시스템에 Windows가 설치된 경우 Auto 항목의 값은 기본값 1로 설정되고 Debugger 항목의 값은 Dr. Watson을 시작하는 명령을 지정합니다. 이는 프로그램 오류가 발생했을 때 Windows용 Dr. Watson이 자동으로 오류를 진단하고 해당 진단 정보를 로그에 기록한다는 뜻입니다.

Dr. Watson이 아닌 프로그램을 기본 디버거로 사용하다가 Dr. Watson을 기본 디버거로 사용하려면 명령 프롬프트로 이동한 다음 drwtsn32 -i 명령을 입력하여 Dr. Watson을 시작합니다. -i를 입력하면 레지스트리에 필요한 변경 작업이 수행됩니다.

해당 심볼이 설치되어 있고 심볼 검색 경로가 설정되어 있으면 Dr. Watson이 좀 더 정확한 디버깅 정보를 생성하도록 할 수 있습니다. 이 경우 먼저 Windows CD-ROM에서 컴퓨터의 새 폴더로 심볼을 복사해야 합니다. 그런 다음 시스템 환경 변수를 새로 만들어야 합니다. 먼저 컴퓨터의 systemroot 폴더로 이동한 다음 Symbols라는 새 폴더를 만듭니다. 그런 다음 CD의 \\Support\Debug\i386으로 이동하여 방금 만든 Symbols 폴더에 심볼을 복사합니다. 심볼을 복사한 다음에는 시스템 환경 변수를 새로 만들어야 합니다. 시스템 환경 변수를 새로 만들 때 변수 이름에는 _NT_SYMBOL_PATH를 입력하고 변수 값에는 %systemroot%\Symbols를 입력합니다. 여러 가지 심볼을 세미콜론으로 구분하여 각기 다른 위치에 설치하고 동일한 환경 변수를 사용하여 각 위치를 가리킬 수 있습니다. 이 때 추가한 서비스 팩에 대한 변수를 반드시 포함시켜야 합니다. 예를 들면 다음과 같습니다. _NT_SYMBOL_PATH=%systemroot%\symbol;%systemroot%\hotfixes;%systemroot%\symbolsNt4Sp3

자세한 내용을 보려면 관련 항목을 클릭하십시오.


위의 내용중 닥터왓슨의 기본 디버거 설정을 해제하고 싶다면 레지스트리 편집기의 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug 으로 가서 Auto 항목을 0으로 수정한다.

[펌]유니코드용 dll 사용시 메모리 누수 현상

http://www.debuglab.com/board/board_detail.aspx?id=654&table=qna&pagenum=1

요약하면 유니코드로 빌드된 DLL을 비유니코드 어플리케이션에서 사용시 메모리가 센다.

첫번째로 자료형의 크기 문제
1. general type 중에서 일부
   ============================================
   type name     Other name
   ——————————————–
   __int8        char
   __int16       short
   ——————————————–
  
   (참고로, wchar_t의 정의는
   typedef unsigned short wchar_t;)
  
  
2. Preprocessor에 따른 Generic Text data type mapping 중에서 일부
   ============================================
   Data type    SBCS    MBCS    UNICODE
   ——————————————–
   _TCHAR       char    char    wchar_t
   ——————————————–

가 있고

두번째로
http://support.microsoft.com/default.aspx?scid=kb%3Ben-us%3B167929
버그니 무시해도 된단다.

디버깅을 위한 험난한 여정

미니덤프를 생성한다.
모종의 방법으로 받아와서 디버거에 연결하여 디버깅한다.

디버거에서 디버깅을 잘 하기 위해서 해당 바이너리의 pdb와 소스를 관리해야 한다.
-> 심볼서버를 운영해야 한다.
-> 정확히 배포바이너리의 버젼과 심볼서버의 심볼, 소스서버의 소스를 연결시켜야 한다.
-> 빌드서버를 운영해야 한다.
-> 토할것 같다

여태까지 살펴본 “배포버젼을 디버깅 하는 방법” 에 관한 내용.
배포버젼의 경우 사용자의 운영체제, 핫픽스버젼에 따라 정확한 운영체제 심볼과 사용자가 사용하고 있는 프로그램(즉 우리가 배포한 프로그램)버젼의 정확한 심볼을 관리해서 사용자가 버그가 났을때의 미니덤프를 디버거에서 저 심볼들과 소스들을 잘 매칭시켜서 디버깅 환경을 구축해야 효율적인 디버깅이 가능하다.

어쨋거나 위에서 요구하는 버젼별 바이너리와 심볼관리 를 위해서는 지금 같은 방식의 중구난방식의 배포판 생성을 해서는 안된다.

적어도 빌드서버가 따로 있어야 한다.

예상하는 시나리오는
그러니깐 각각 개발자들이 소스세이프에서 소스를 다운받아서 코딩하고 수정하고 컴파일하고 테스트까지는 자기 컴퓨터에서 하지만
이녀석을 배포한다.
라고 정해지면 해당 소스를 모두 체크인 시키고 빌드서버에서 스크립트에 의한 빌드를 실행한다.
그러면 빌드서버는 소스세이프에서 최신의 버젼의 소스를 얻어오고 빌드넘버를 +1(어쨋거나 프로그램의 버젼을 조정) 시켜 이전의 배포판과 다른 버젼으로 컴파일하고 컴파일후 생성된 바이너리와 심볼파일은 자동으로 심볼서버로 업데이트(물론 해당 버젼에 따라). 그 이후 바이너리 파일을 패키징시켜 배포판을 생성해서 완전히 CD로 구울수 있는 상태등으로 만든다.
(물론 컴파일후 유닛테스트도 들어가면 좋겠다)

위의 일련의 과정이 자동 스크립트화 되지 않으면 관리가 불가능 하다.
흠.. 뭐 위 내용을 자동화 시키는 스크립트가 그리 어렵지는 않을 것같다. 어쨋든 비주얼스튜디오, 소스세이프, 심볼서버등도 모두 커맨드라인을 지원하니…

일단 의문점들
1. 운영체제 심볼구성
필요한 운영체제의 심볼을 얻기 위해서는 개발자 측에서 운영체제를 설치하고 핫픽스, 서버시팩 을 하나하나 적용하면서 운영체제 심볼을 symchk 로 구성하는 수밖에는 없는건가?
(미니덤프에 사용자의 운영체제 버젼이 적혀있고 디버거가 연결할때 필요한 심볼이 없으면 그것만 알아서 다운받게 할수는 없나?)
심볼구성이 은근히 빡세다. 게다가 오랬동안 사용한 머신의 경우 system32 폴더에 갖가지 응용프로그램들의 dll 이나 exe까지 자리를 차지하고 있는 경우가 있어서 시간, 용량의 낭비가 예상된다.

2. 정확한 버젼의 소스
이걸 구성하기 위해서는 각 배포버젼마다 소스를 남겨 두어야 하는건가?
(소스세이스프 같은걸 엘레강스하게 사용할 방법은 없는 것일까?)

3. 릴리즈빌드시의 심볼파일 생성
어쨋거나 릴리즈 빌드를 한 바이너리를 디버깅할수 있게 하는것도 하나의 목적인데 이런 빌드설정이 속도를 얼마나 떨어뜨리느냐가 관건..

4. 빌드시 빌드넘버 or 버젼 넘버링
이거 당췌 어떻게 하는거야?!
컴파일 직전에 스크립트로 리소스 파일을 수정이라도 해야 하나? 그리고 미니덤프에서 프로그램 버젼은 어떻게 매칭하는지 알아야 빌드넘버 넘버링을 하지.

그리고 어쨋거나 추가해야 할것
1. 사용자들에 의한 자동리포팅 시스탬
프로그램이 죽는 경우가 아니더라도 사용자가 “버그리포팅”을 선택하고 현재의 상태와 어떤점이 버그인지 간단히 기술해서 리포팅 시키면 미니덤프+프로젝트파일+데이타파일 을 전송

2. 웹에서의 버그리포팅, 추적시스탬 구축

쩝… 기존에 구축해 봤던 사람이라면 모르겠지만… 리포팅 그런거 다 빼고 심볼서버, 빌드서버 로 미니덤프만 디버깅 하게 하는것도 빡시다.

삽질 – 004 :: 라이브러리의 특정 객체를 사용할때만 링크에러가 날때

라이브러리 전체가 다 안되던지 다 되던지 둘중에 하나일 것이지 특정클래스를 사용할때만 링크 에러가 발생할 경우.

해당 라이브러리 컴파일시 그 클래스만 컴파일을 안했거나 라이브러리로 만들지 않았거나 이 클래스를 사용한다(export명령)는 명령어를 안써줬을때.

당연하잖아… T_T
오늘 이것때문에 도대체 몇시간을 삽질을 한거야!

처음엔 윈도우 였다. 어떤 클래스를 DLL 에 만들고 EXE에서 사용하는데 계속 링크에러가 난다.
원인은? 이 클래스를 EXPORT 한다는 명령어를 class 선언에 안붙여 줬다…… 이걸로 한 한시간??

두번째는 리눅스였다. 역시나 특정 클래스를 사용할때만 링크에러가 난다….. 원인을 모르겠다. 아무리 찾아도 모르겠다.

..
.

씨바 라이브러리파일 만들때 해당 오브젝트 파일을 포함안해줬다.

아 이걸로 한 4시간………………………………………..

어버어버버어버어버ㅓ버ㅓ어버어버ㅓ어버어버업업
ㅣㅇ너린엏ㄴ;ㅇ한ㅇ;헌
ㅎㄴ엏ㄴ엏ㅈ도허
]ㅔㅎ
해ㅓ

해ㅓㄴ한ㅇㅎ

헐헌
[ㅐ5ㅗㅓㅓㅜㅗ
버3ㄱ
ㅐ헝ㄹ