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 출력합니다 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)

즉, 후크 함수는 할당의 크기를 나타내는 형식 값과 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입니다. re기본ing 인수에는 할당 크기, 블록 형식, 순차 요청 번호 및 파일 이름에 대한 포인터가 포함됩니다. 사용 가능한 경우 인수는 할당이 이루어진 줄 번호도 포함합니다. 후크 함수는 작성자가 원하는 모든 분석 및 기타 작업을 수행한 후 할당 작업이 계속될 수 있음을 나타내거나 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되지 않은 결과 등 힙 할당 함수의 특수 디버그 버전에 대해 설명합니다.

  • CRT 디버그 힙 정보

    메모리 관리 및 디버그 힙, 디버그 힙의 블록 유형, 힙 상태 보고 함수 및 디버그 힙을 사용하여 할당 요청을 추적하는 방법을 설명합니다.

  • CRT 라이브러리를 사용하여 메모리 누수 찾기

    디버거 및 C 런타임 라이브러리를 사용하여 메모리 누수를 탐지하고 격리하는 기술에 대해 설명합니다.

참고 항목