Contents
1. 앞으로 추가 정리할 내용 [Bottom] [Top]
Visual Studio의 디버깅 - http://msdn2.microsoft.com/ko-kr/library/sc65sadd(VS.80).aspx
디버거 로드맵 - http://msdn2.microsoft.com/ko-kr/library/k0k771bt(VS.80).aspx
네이티브 코드 디버깅 - http://msdn2.microsoft.com/ko-kr/library/k70yt3e2(VS.80).aspx
CRT 디버깅 기술 - http://msdn2.microsoft.com/ko-kr/library/zh712wwf(VS.80).aspx
Debug Routines - http://msdn2.microsoft.com/ko-kr/library/1666sb98(VS.80).aspx
원격 디버깅 설치 - http://msdn2.microsoft.com/ko-kr/library/y7f5zaaa(VS.80).aspx
2. Assert 함수 사용하기 [Bottom] [Top]
Visual C++ 에서는 다음과 같이 4가지 형태의 Assert 함수군를 제공한다.
함수군
헤더 파일
함수
설명
CRT ASSERT 함수
<crtdbg.h>
_ASSERT( expr )
기본적인 Assert 동작과 파일 이름과 줄번호 출력
_ASSERTE( expr )
_ASSERT() 기능에 expr 문자열 출력
ANSI assert 함수
<assert.h>
assert( expr )
기본적인 Assert 동작
ATL ASSERT 함수
ATLASSERT( expr )
기본적인 Assert 동작
MFC ASSERT 함수
ASSERT( expr )
기본적인 Assert 동작
VERIFY( expr )
ASSERT() 기능에 Release 모드에서 expr 만 수행
2.1. CRT ASSERT 함수 활용 [Bottom] [Top]
- 힙 손상 확인
1 _ASSERTE( _CrtCheckMemory() ); - 포인터 유효성 확인
- 지정한 메모리 범위가 읽기나 쓰기에 적합한지 확인한다.
1 _ASSERTE( _CrtIsValidPointer( address, size, TRUE ) );- 로컬 힙에서 포인터가 메모리를 가리키는지 확인한다.
- 로컬 힙이란 이러한 C 런타임 라이브러리의 인스턴스가 만들고 관리하는 힙, 즉 DLL 에는 라이브러리의 자체 인스턴스가 있으므로 응용 프로그램 힙 외부에 있는 자체 힙을 의미한다. 이 ASSERT 는 NULL 이나 범위를 벗어나는 주소 뿐만 아니라 정적 변수, 스택 변수 및 다른 모든 비 로컬 메모리에 대한 포인터를 찾아준다.
1 _ASSERTE( _CrtIsValidHeapPointer( myData ) ); - 메모리 블록 확인
- 메모리 블록이 로컬 힙에 있고 블록 형식이 유효한지 확인한다.
1 _ASSERTE( _CrtIsMemoryBlock( myData, size, &requestNumber, &filename, &linenumber ) );
2.2. Report 매크로 [Bottom] [Top]
매크로
설명
_RPT0, _RPT1, _RPT2, _RPT3, _RPT4
메시지 문자열과 0을 네 개의 인수로 출력.
_RPT1 ~ _RPT4 에서는 메시지 문자열이 인수에 대해 printf 스타일의 형식 지정 문자열로 사용._RPTF0, _RPTF1, _RPTF2, _RPTF4
_RPTn 과 같지만 파일 이름과 줄 번호를 출력.
C 런타임 라이브러리와 함께 제공된 매크로가 없지만 디버그 보고가 필요할 경우, 필요에 맞게 특별히 디자인된 매크로를 작성할 수 있다. 예를 들어, 헤더 파일 중 하나에서 다음과 같은 코드를 포함하여 ALERT_IF2 매크로를 정의할 수 있다.
1 #ifndef _DEBUG 2 /* For RELEASE builds */ 3 #define ALERT_IF2(expr, msg, arg1, arg2) do {} while (0) 4 #else 5 /* For DEBUG builds */ 6 #define ALERT_IF2(expr, msg, arg1, arg2) \ 7 do { \ 8 if((expr) && \ 9 (1 == _CrtDbgReport(_CRT_ERROR, \ 10 __FILE__, __LINE__, msg, arg1, arg2))) \ 11 _CrtDbgBreak( ); \ 12 } while (0) 13 #endif
ALERT_IF2를 한 번만 호출하면 이 항목의 처음에서 printf 코드의 모든 기능을 실행할 수 있다.
1 ALERT_IF2( someVar > MAX_SOMEVAR, "OVERFLOW! In NameOfThisFunc( ), 2 someVar=%d, otherVar=%d.\n", someVar, otherVar );
3. 사용자 디버깅 중단점 사용하기 [Bottom] [Top]
Visual Studio (디버거) 에서 디버깅 중단점을 삽입하지 않고 DebugBreak() 와 _ _debugbreak() 함수를 이용하여 코드 상에서 설정 가능하다.
디버깅 중단점 종류
헤더 파일
함수
Win32 디버깅 중단점
<Windows.h>
DebugBreak()
CRT 디버깅 중단점
<intrin.h>
_ _debugbreak()
<crtdbg.h>
_CrtDbgBreak()
_ _debugbreak() 함수는
1 main() { 2 __debugbreak(); 3 }
x86 계열 컴퓨터에서는 다음과 같다.
1 main() { 2 __asm { 3 int 3 4 } 5 }
4. 메모리 누수 탐지 [Bottom] [Top]
메모리 누수를 탐지하는 데 사용하는 기본 도구는 디버거와 CRT(C 런타임 라이브러리) 디버그 힙 함수. 디버그 힙 함수를 사용하려면 다음과 같이 헤더 파일을 선언한다.
1 #define _CRTDBG_MAP_ALLOC // STEP 0 - 생략 가능 2 #include <stdlib.h> // STEP 1 3 #include <crtdbg.h> // STEP 2
주의> #include 문은 순서가 중요. 순서를 변경하면 제대로 작동하지 않을 수 있다.
4.1. 메모리 누수 정보 덤프 [Bottom] [Top]
_CrtDumpMemoryLeaks() 함수 사용
- 디버거의 출력 창에 메모리 누수 정보를 표시한다.
- 메모리 누수 정보
파일 이름과 줄 번호 (_CRTDBG_MAP_ALLOC 가 정의된 경우만 표시)
- 메모리 할당 번호(중괄호 안에 표시)
- 블록 형식(표준, 클라이언트 또는 CRT)
- 16진수로 표기한 메모리 위치
- 바이트로 표기한 블록 크기
- 16진수로 표기한 처음 16바이트의 내용
4.2. 메모리 누수 정보 자동 덤프 [Bottom] [Top]
_CrtSetDbgFlag() 함수 사용
프로그램이 여러 위치에서 종료될 수 있는 경우에는 _CrtDumpMemoryLeaks() 함수 보다는 _CrtSetDbgFlag() 함수를 사용한다.
- 일반적인 사용되는 예제 코드
1 _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
4.3. CRT 보고서 모드 설정 [Bottom] [Top]
_CrtSetReportMode() 함수 사용
- 기본적으로 출력 창의 디버그 창으로 덤프되는 메모리 누수 정보를 다른 위치로 출력하도록 설정
_CrtSetReportFile() 함수 와 함께 사용하여 파일로 출력 가능
- 예제 코드 1
1 HANDLE hLogFile = CreateFile( 2 "c:\\log.txt", 3 GENERIC_WRITE, 4 FILE_SHARE_WRITE, 5 NULL, 6 CREATE_ALWAYS, 7 FILE_ATTRIBUTE_NORMAL, 8 NULL); 9 10 _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE ); 11 _CrtSetReportFile( _CRT_WARN, hLogFile ); 12 13 _RPT0( _CRT_WARN, "file message\n" ); 14 15 CloseHandle( hLogFile );
- 예제 코드 2
1 freopen( "c:\\log2.txt", "w", stderr); 2 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); 3 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); 4 5 _RPT0(_CRT_ERROR,"1st message\n");
5. 쓰레드 정보 표시 [Bottom] [Top]
네이티브 코드를 디버깅하는 경우 조사식 창이나 간략한 조사식 대화 상자에 @TIB 를 입력하면 스레드 정보 블록의 내용이 표시된다.
네이티브 코드를 디버깅하는 경우 조사식 창이나 간략한 조사식 대화 상자에 @Err 을 입력하면 현재 스레드의 마지막 오류 코드가 표시된다.
6. 삽입된 코드 디버깅 [Bottom] [Top]
COM 이나 OLE DB 소비자 특성과 같이 Visual C++ 에서 지원하는 특성(Attributes)을 사용하면 프로그래밍을 단순화할 수 있다. 하지만 디버깅 시 실제 생성된 코드를 디버깅할 수가 없어서 불편할 경우 다음의 두가지 방법으로 삽입된 코드를 볼 수 있다.
6.1. 디버깅 중일 경우 [Bottom] [Top]
디스어셈블리 창에서 확인 가능하다.
디스어셈블리 창에 소스 코드와 특성에 의해 삽입된 코드에 대한 어셈블리어 명령이 표시되고 소스 코드 주석이 표시될 수도 있다.
6.2. 삽입된 코드가 포함된 소스 파일 생성 [Bottom] [Top]
C++ 컴파일러에 /Fx 옵션을 사용하여 삽입된 코드를 포함한 소스파일의 복사본을 생성한다. (파일명: 원본파일명.mrg.확장자)
- 특성에 의하여 삽입된 코드 형식 (.mrg 파일)
//+++ Start Injected Code // 삽입된 코드 시작 ... //--- End Injected Code // 삽입된 코드 끝
- .mrg 소스 파일은 컴파일러에서 삽입한 소스 코드를 표시한다. 단, 매크로는 확장되지 않는다.
주의> .mrg 파일은 원래 소스 파일과 완전히 똑같이 컴파일되거나 실행되지 않을 수도 있다.
Visual Studio 에서 /Fx 옵션 설정 방법
- [프로젝트 속성 창] 을 열고 [구성 속성] - [C/C++] 을 선택한다.
[출력 파일] - [특성 사용 소스 확장] 에서 [예(/Fx)] 으로 설정한다.
- [프로젝트 속성 창] 을 닫고 다시 컴파일하면 *.mrg.* 파일이 생성된다.
7. 자동으로 디버거 시작 [Bottom] [Top]
디버거가 자동으로 실행되도록 응용 프로그램을 설정하려면
- 레지스트리 편집기(regedit) 를 시작합니다.
- 레지스트리 편집기에서 HKEY_LOCAL_MACHINE 폴더를 엽니다.
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options 로 이동합니다.
Image File Execution Options 폴더에서 디버깅할 응용 프로그램의 이름 (예: myapp.exe) 을 찾습니다. 디버깅할 응용 프로그램을 찾을 수 없으면 다음을 수행하십시오.
Image File Execution Options 폴더를 마우스 오른쪽 단추로 클릭하고 바로 가기 메뉴에서 새 키를 클릭합니다.
- 새 키를 마우스 오른쪽 단추로 클릭하고 바로 가기 메뉴에서 이름 바꾸기를 클릭합니다.
- 응용 프로그램 이름 (이 예제의 경우 myapp.exe) 과 같아지도록 키 이름을 편집합니다.
myapp.exe 폴더를 마우스 오른쪽 단추로 클릭하고 바로 가기 메뉴에서 새 문자열 값을 클릭합니다.
- 새 문자열 값을 마우스 오른쪽 단추로 클릭하고 바로 가기 메뉴에서 이름 바꾸기를 클릭합니다.
이름을 debugger 로 바꿉니다.
새 문자열 값을 마우스 오른쪽 단추로 클릭하고 바로 가기 메뉴에서 수정을 클릭합니다.
문자열 편집 대화 상자가 나타납니다.값 데이터 상자에 vsjitdebugger.exe 를 입력합니다.
- 확인을 클릭합니다.
- 레지스트리 메뉴에서 끝내기를 클릭합니다.
- vsjitdebugger.exe 가 들어 있는 디렉터리가 시스템 경로에 있어야 합니다. 이 디렉터리를 시스템 경로에 추가하려면 다음 단계를 따릅니다.
- 제어판을 클래식 보기로 열고 시스템을 두 번 클릭합니다.
- 시스템 등록 정보에서 고급 탭을 클릭합니다.
- 고급 탭에서 환경 변수를 클릭합니다.
- 환경 변수 대화 상자의 시스템 변수 아래에서 Path 를 선택한 다음 편집 단추를 클릭합니다.
- 시스템 변수 편집 대화 상자의 변수 값에 디렉터리를 추가합니다. 목록의 각 항목을 구분하는 데는 세미콜론을 사용합니다.
- 확인을 클릭하여 시스템 변수 편집 대화 상자를 닫습니다.
- 확인을 클릭하여 환경 변수 대화 상자를 닫습니다.
- 확인을 클릭하여 시스템 등록 정보 대화 상자를 닫습니다.
이제, 응용 프로그램을 시작하면 Visual Studio 가 시작되고 응용 프로그램이 로드된다.
8. 디버깅 팁 [Bottom] [Top]
8.1. Just-In-Time 디버깅 팁 [Bottom] [Top]
서버에 Visual Studio 를 설치한 다음 처리되지 않은 예외가 발생하면 기본적으로 예외 대화 상자가 열린다. 사용자는 이 대화 상자에서 Just-In-Time 디버깅을 시작하거나 예외를 무시하도록 지정해야 한다. 무인 작업을 진행하려는 경우에는 이러한 작동 방식이 적합하지 않을 수 있다. Visual Studio 를 설치하기 전의 기본 동작처럼 처리되지 않은 예외가 발생해도 대화 상자가 열리지 않도록 서버를 구성하려면 레지스트리 편집기를 사용하여 다음 레지스트리 키를 삭제한다.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug\Debugger HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\DbgManagedDebugger
64비트 운영 체제에서는 다음 레지스트리 키도 삭제한다.
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug\Debugger HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\DbgManagedDebugger
8.2. Win32 오류 코드 찾기 [Bottom] [Top]
기본 시스템 설치의 include 디렉터리에 있는 Winerror.h 에는 Win32 API 함수에 대한 오류 코드 정의가 포함되어 있다. 조사식 창 이나 간략한 조사식 대화 상자 에 코드를 입력하면 오류 코드를 찾을 수 있다. 예를 들면 다음과 같다.
0x80000004,hr
8.3. 출력창의 바로가기 활용하기 [Bottom] [Top]
Visual Studio 의 출력창에 다음과 같은 메세지 형식으로 출력될 경우 출력창의 메세지를 더블클릭하면 소스 파일로 이동하거나 해당 파일을 열 수 있다.
- 메시지 형식1 - 소스 파일의 특정 줄번호로 이동
{파일의 전체경로} (줄번호) : 메시지 형식2 - 해당 파일 열기 (<Ctrl> 키를 누른 상태에서 클릭한다.)
"file://{파일의 전체경로}"- 사용 예)
OutputDebugString( "C:\\Test.cpp(10) :\n" ); // C:\Test.cpp 파일의 10번째 라인으로 이동 OutputDebugString( "\"file://D:\\Data.txt\"\n" ); // D:\Data.txt 파일 열기
[출력 결과] ------------------------------ C:\Test.cpp(10) : "file://D:\Data.txt"
8.4. Visual Studio 에서 실행 중인 확인하기 [Bottom] [Top]
Visual Studio 에서 실행 중인지 알아야 할 경우가 있다. 특히 Visual Studio 에서 실행될 때는 디버깅 메시지를 출력하다가 그냥 실행할 경우에는 오류로 처리해야 한다면 꼭 필요하다.
BOOL WINAPI IsDebuggerPresent(void);
IsDebuggerPresent() 함수의 반환값은 다음과 같다.
TRUE 이면 Visual Studio 에서 실행 중
FALSE 이면 그냥 실행 중
