C 런타임 라이브러리를 사용하는 프로그램을 디버그하는 경우 이러한 디버깅 기술이 유용할 수 있습니다.
CRT 디버그 라이브러리 사용
CRT(C 런타임) 라이브러리는 광범위한 디버깅 지원을 제공합니다. CRT 디버그 라이브러리 중 하나를 사용하려면 연결 /DEBUG 하고 , /MDd또는 /MTd/LDd컴파일해야 합니다.
CRT 디버깅에 대한 기본 정의 및 매크로는 헤더 파일에서 <crtdbg.h> 찾을 수 있습니다.
CRT 디버그 라이브러리의 함수는 최적화 없이 디버그 정보(/Z7, /Zd, /Zi, /ZI(디버깅 정보 형식))를 사용하여 컴파일됩니다. 일부 함수는 함수에 전달된 매개 변수를 확인하기 위해 어설션을 사용하며 소스 코드를 제공합니다. 이 소스 코드를 사용하면 CRT 함수를 단계적으로 실행하여 원하는 대로 함수가 작동하고 있는지 확인하고 잘못된 매개 변수나 메모리 상태를 검사할 수 있습니다. (일부 CRT 기술은 독점적이며 예외 처리, 부동 소수점 및 기타 몇 가지 루틴에 대한 소스 코드를 제공하지 않습니다.)
사용할 수 있는 런타임 라이브러리에 대한 자세한 내용은 C 런타임 라이브러리를 참조하세요.
보고서 매크로
디버깅의 경우 문 사용을 바꾸기 위해 정의된 _RPTn매크로 및 _RPTFn 매크로를 사용할 <crtdbg.h>printf 수 있습니다. 정의되지 않은 경우 릴리스 빌드 #ifdef 에서 _DEBUG 자동으로 사라지기 때문에 지시문으로 묶을 필요가 없습니다.
| 매크로 | 설명 |
|---|---|
_RPT0, _RPT1, _RPT2, _RPT3_RPT4 |
메시지 문자열과 0을 네 개의 인수로 출력합니다.
_RPT1메시지 _RPT4 문자열은 인수에 대한 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 출력합니다 otherVarstdout. 다음 _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)
즉, 후크 함수는 할당의 크기를 나타내는 형식 값과 void 함께 할당 블록의 시작 부분에 대한 포인터를 수락 size_t 하고 반환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입니다. 나머지 인수에는 할당 크기, 블록 형식, 순차 요청 번호 및 파일 이름에 대한 포인터가 포함됩니다. 사용 가능한 경우 인수는 할당이 이루어진 줄 번호도 포함합니다. 후크 함수는 작성자가 원하는 모든 분석 및 기타 작업을 수행한 후 할당 작업이 계속될 수 있음을 나타내거나 TRUE작업이 실패해야 함을 나타내는 중 하나를 FALSE반환해야 합니다. 이 형식의 간단한 후크는 지금까지 할당된 메모리 양을 확인하고 해당 양이 작은 제한을 초과하면 반환 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_WARN_CRT_ERROR또는_CRT_ASSERT) szMsg 의 범주가 포함되며 retVal 보고서를 생성한 후 정상적인 실행을 계속할지 또는 디버거를 시작할지 여부를 _CrtDbgReport 지정합니다. (값이 0이 retVal 면 실행이 계속됩니다. 값이 1이면 디버거가 시작됩니다.)
후크가 문제의 메시지를 완전히 처리하여 더 이상 보고할 필요가 없도록 하려면 해당 메시지를 반환 TRUE해야 합니다. 반환 FALSE_CrtDbgReport 되는 경우 메시지를 정상적으로 보고합니다.
이 섹션의 내용
-
CRT가 호출을 매핑하는 방법, 명시적으로 호출할 때의 이점, 변환을 방지하는 방법, 클라이언트 블록에서 별도의 할당 유형 추적, 정의
_DEBUG되지 않은 결과 등 힙 할당 함수의 특수 디버그 버전에 대해 설명합니다. -
메모리 관리 및 디버그 힙, 디버그 힙의 블록 유형, 힙 상태 보고 함수 및 디버그 힙을 사용하여 할당 요청을 추적하는 방법을 설명합니다.
-
디버거 및 C 런타임 라이브러리를 사용하여 메모리 누수를 탐지하고 격리하는 기술에 대해 설명합니다.