CRT 디버깅 기술
C 런타임 라이브러리를 사용하는 프로그램을 디버그하는 경우 이러한 디버깅 기술이 유용할 수 있습니다.
CRT 디버그 라이브러리 사용
CRT(C 런타임) 라이브러리는 광범위한 디버깅 지원을 제공합니다. CRT 디버그 라이브러리 중 하나를 사용하려면 연결 /DEBUG
하고 , /MTd
또는 /LDd
.로 /MDd
컴파일해야 합니다.
CRT 디버깅에 대한 기본 정의 및 매크로는 헤더 파일에서<crtdbg.h>
찾을 수 있습니다.
CRT 디버그 라이브러리의 함수는 최적화 없이 디버그 정보(/Z7, /Zd, /Zi, /ZI(디버깅 정보 형식))를 사용하여 컴파일됩니다. 일부 함수는 함수에 전달된 매개 변수를 확인하기 위해 어설션을 사용하며 소스 코드를 제공합니다. 이 소스 코드를 사용하면 CRT 함수를 단계적으로 실행하여 원하는 대로 함수가 작동하고 있는지 확인하고 잘못된 매개 변수나 메모리 상태를 검사할 수 있습니다. (일부 CRT 기술은 독점적이며 예외 처리, 부동 소수점 및 기타 몇 가지 루틴에 대한 소스 코드를 제공하지 않습니다.)
사용할 수 있는 런타임 라이브러리에 대한 자세한 내용은 C 런타임 라이브러리를 참조하세요.
보고서 매크로
디버깅의 경우 정의된<crtdbg.h>
매크로와 _RPTFn
매크로를 사용하여 _RPTn
문 사용을 printf
바꿀 수 있습니다. 정의되지 않은 경우 릴리스 빌드 _DEBUG
에서 #ifdef
자동으로 사라지기 때문에 지시문으로 묶을 필요가 없습니다.
매크로 | 설명 |
---|---|
_RPT0 , _RPT1 , _RPT2 , _RPT3 _RPT4 |
메시지 문자열과 0을 네 개의 인수로 출력합니다. _RPT4 메시지 _RPT1 문자열은 인수에 대한 printf 스타일 서식 지정 문자열로 사용됩니다. |
_RPTF0 , _RPTF1 , _RPTF2 , _RPTF3 _RPTF4 |
와 동일 _RPTn 하지만 이러한 매크로는 매크로가 있는 파일 이름 및 줄 번호도 출력합니다. |
다음 예시를 참조하세요.
#ifdef _DEBUG
if ( someVar > MAX_SOMEVAR )
printf( "OVERFLOW! In NameOfThisFunc( ),
someVar=%d, otherVar=%d.\n",
someVar, otherVar );
#endif
이 코드는 값을 someVar
출력합니다 otherVar
stdout
. 다음 _RPTF2
호출을 사용하여 동일한 값과 파일 이름, 줄 번호를 보고할 수 있습니다.
if (someVar > MAX_SOMEVAR) _RPTF2(_CRT_WARN, "In NameOfThisFunc( ), someVar= %d, otherVar= %d\n", someVar, otherVar );
일부 애플리케이션에서는 C 런타임 라이브러리와 함께 제공된 매크로가 제공하지 않는다는 디버그 보고가 필요할 수 있습니다. 이 경우 요구 사항에 맞게 특별히 디자인된 매크로를 작성할 수 있습니다. 예를 들어 헤더 파일 중 하나에서 다음과 같은 코드를 포함하여 다음과 ALERT_IF2
같은 매크로를 정의할 수 있습니다.
#ifndef _DEBUG /* For RELEASE builds */
#define ALERT_IF2(expr, msg, arg1, arg2) do {} while (0)
#else /* For DEBUG builds */
#define ALERT_IF2(expr, msg, arg1, arg2) \
do { \
if ((expr) && \
(1 == _CrtDbgReport(_CRT_ERROR, \
__FILE__, __LINE__, msg, arg1, arg2))) \
_CrtDbgBreak( ); \
} while (0)
#endif
한 번의 호출로 ALERT_IF2
코드의 printf
모든 함수를 수행할 수 있습니다.
ALERT_IF2(someVar > MAX_SOMEVAR, "OVERFLOW! In NameOfThisFunc( ),
someVar=%d, otherVar=%d.\n", someVar, otherVar );
대상에 따라 더 많거나 더 적은 정보를 보고하도록 사용자 지정 매크로를 쉽게 변경할 수 있습니다. 이 방법은 디버깅 요구 사항이 진화함에 따라 유용합니다.
디버그 후크 함수 작성
디버거의 일반 처리 내에서 미리 정의된 일부 지점에 코드를 삽입할 수 있는 여러 종류의 사용자 지정 디버그 후크 함수를 작성할 수 있습니다.
클라이언트 블록 후크 함수
적절한 함수를 작성하여 _CLIENT_BLOCK
블록에 저장되는 데이터 내용을 보고하거나 그 유효성을 검사할 수 있습니다. 작성하는 함수에는 다음에 정의된 대로 다음과 유사한 프로토타입이 있어야 합니다.<crtdbg.h>
void YourClientDump(void *, size_t)
즉, 후크 함수는 할당의 크기를 나타내는 형식 값과 size_t
함께 할당 블록의 시작 부분에 대한 포인터를 수락 void
하고 반환void
해야 합니다. 그렇지 않으면 콘텐츠는 사용자에게 달려 있습니다.
_CrtSetDumpClient 사용하여 후크 함수를 설치하면 블록이 덤프될 때마다 _CLIENT_BLOCK
호출됩니다. 그런 다음, _CrtReportBlockType을 사용하여 덤프한 블록의 형식이나 하위 형식에 대한 정보를 얻을 수 있습니다.
전달하는 _CrtSetDumpClient
함수에 대한 포인터는 다음과 같이 형식_CRT_DUMP_CLIENT
입니다.<crtdbg.h>
typedef void (__cdecl *_CRT_DUMP_CLIENT)
(void *, size_t);
할당 후크 함수
메모리가 할당, 재할당 또는 해제될 때마다 할당 후크 함수를 사용하여 _CrtSetAllocHook
설치됩니다. 이 형식의 후크는 다양한 용도로 사용할 수 있습니다. 예를 들어 애플리케이션에서 할당 패턴을 검사하거나 추후 분석 목적으로 할당 정보를 로그하기 위해 메모리 부족 상황을 어떻게 처리하는지 테스트하는 데 이 후크를 사용합니다.
참고 항목
할당 후크 및 crt 메모리 할당에 설명된 할당 후크 함수에서 C 런타임 라이브러리 함수를 사용하는 것에 대한 제한 사항에 유의하세요.
할당 후크 함수에는 다음 예제와 같은 프로토타입이 있어야 합니다.
int YourAllocHook(int nAllocType, void *pvData,
size_t nSize, int nBlockUse, long lRequest,
const unsigned char * szFileName, int nLine )
전달 _CrtSetAllocHook
한 포인터는 다음과 같이 형식_CRT_ALLOC_HOOK
입니다.<crtdbg.h>
typedef int (__cdecl * _CRT_ALLOC_HOOK)
(int, void *, size_t, int, long, const unsigned char *, int);
런타임 라이브러리가 후크를 nAllocType
호출할 때 인수는 수행할 할당 작업(_HOOK_ALLOC
_HOOK_REALLOC
또는_HOOK_FREE
)을 나타냅니다. 해제하거나 다시 할당할 때 pvData
에는 해제할 블록의 사용자 아티클에 대한 포인터가 있습니다. 그러나 할당의 경우에는 할당이 발생하지 않았기 때문에 이 포인터는 null입니다. 나머지 인수에는 할당 크기, 블록 형식, 순차 요청 번호 및 파일 이름에 대한 포인터가 포함됩니다. 사용 가능한 경우 인수는 할당이 이루어진 줄 번호도 포함합니다. 후크 함수는 작성자가 원하는 모든 분석 및 기타 작업을 수행한 후 할당 작업이 계속될 수 있음을 나타내거나 FALSE
작업이 실패해야 함을 나타내는 중 하나를 TRUE
반환해야 합니다. 이 형식의 간단한 후크는 지금까지 할당된 메모리 양을 확인하고 해당 양이 작은 제한을 초과하면 반환 FALSE
할 수 있습니다. 그러면 애플리케이션에서 일반적으로 사용 가능한 메모리가 부족할 때만 발생하는 할당 오류의 종류가 발생합니다. 좀 더 복잡한 후크는 할당 패턴을 추적하거나 메모리 사용을 분석하거나 특정 상황이 발생하는 때를 보고할 수 있습니다.
할당 후크 및 CRT 메모리 할당
할당 후크 함수에 대한 중요한 제한 사항은 블록을 명시적으로 무시 _CRT_BLOCK
해야 한다는 것입니다. 이러한 블록은 할당 후크 함수가 내부 메모리를 할당하는 C 런타임 라이브러리 함수를 호출하는 경우, C 런타임 라이브러리 함수를 통해 내부적으로 만든 메모리 할당입니다. 할당 후크 함수의 처음 부분에 다음과 같은 코드를 포함하여 _CRT_BLOCK
블록을 무시할 수 있습니다.
if ( nBlockUse == _CRT_BLOCK )
return( TRUE );
할당 후크가 _CRT_BLOCK
블록을 무시하지 않으면 후크에서 호출한 C 런타임 라이브러리 함수는 무한 루프에서 프로그램을 트래핑할 수 있습니다. 예를 들어, printf
는 내부 할당을 만듭니다. 후크 코드가 호출 printf
되면 결과 할당으로 인해 후크가 다시 호출되고 스택이 오버플로될 때까지 다시 호출 printf
됩니다. _CRT_BLOCK
할당 작업을 보고해야 할 경우, 이러한 제한을 피하려면 형식 지정과 출력에 C 런타임 함수 대신 Windows API 함수를 사용해야 합니다. Windows API는 C 런타임 라이브러리 힙을 사용하지 않기 때문에 무한 루프에서 할당 후크를 트래핑하지 않습니다.
런타임 라이브러리 원본 파일을 검사하면 기본 할당 후크 함수 _CrtDefaultAllocHook
(단순히 반환 TRUE
)가 자체 debug_heap_hook.cpp
파일의 별도 파일에 있는 것을 볼 수 있습니다. 애플리케이션 main
의 함수 이전에 실행되는 런타임 시작 코드에서 수행한 할당에 대해서도 할당 후크를 호출하려면 이 기본 함수를 사용하는 _CrtSetAllocHook
대신 사용자 고유의 함수로 바꿀 수 있습니다.
보고서 후크 함수
사용하여 설치된 _CrtSetReportHook
보고서 후크 함수는 디버그 보고서를 생성할 때마다 _CrtDbgReport
호출됩니다. 보고서 후크 함수를 사용하여 특정한 할당 형식에 맞게 보고서를 필터링할 수 있습니다. 보고서 후크 함수에는 다음 예제와 같은 프로토타입이 있어야 합니다.
int AppReportHook(int nRptType, char *szMsg, int *retVal);
전달 _CrtSetReportHook
한 포인터는 다음과 같이 형식_CRT_REPORT_HOOK
입니다.<crtdbg.h>
typedef int (__cdecl *_CRT_REPORT_HOOK)(int, char *, int *);
런타임 라이브러리가 후크 함수 nRptType
를 호출할 때 인수에는 완전히 어셈블된 보고서 메시지 문자열에 대한 포인터가 포함된 보고서(_CRT_ERROR
_CRT_WARN
또는_CRT_ASSERT
) szMsg
의 범주가 포함되며 retVal
보고서를 생성한 후 정상적인 실행을 계속할지 또는 디버거를 시작할지 여부를 _CrtDbgReport
지정합니다. (값이 0이 retVal
면 실행이 계속됩니다. 값이 1이면 디버거가 시작됩니다.)
후크가 문제의 메시지를 완전히 처리하여 더 이상 보고할 필요가 없도록 하려면 해당 메시지를 반환 TRUE
해야 합니다. 반환 FALSE
_CrtDbgReport
되는 경우 메시지를 정상적으로 보고합니다.
이 섹션의 내용
-
CRT가 호출을 매핑하는 방법, 명시적으로 호출할 때의 이점, 변환을 방지하는 방법, 클라이언트 블록에서 별도의 할당 유형 추적, 정의
_DEBUG
되지 않은 결과 등 힙 할당 함수의 특수 디버그 버전에 대해 설명합니다. -
메모리 관리 및 디버그 힙, 디버그 힙의 블록 유형, 힙 상태 보고 함수 및 디버그 힙을 사용하여 할당 요청을 추적하는 방법을 설명합니다.
-
디버거 및 C 런타임 라이브러리를 사용하여 메모리 누수를 탐지하고 격리하는 기술에 대해 설명합니다.