Partilhar via


Técnicas de depuração CRT

Quando você depurar um programa que usa a biblioteca de tempo de execução C, essas técnicas de depuração podem ser úteis.

Uso da biblioteca de depuração CRT

A biblioteca C runtime (CRT) fornece suporte extensivo à depuração. Para usar uma das bibliotecas de depuração CRT, você deve vincular e /DEBUG compilar com /MDd, /MTdou /LDd.

As principais definições e macros para depuração CRT podem ser encontradas no <crtdbg.h> arquivo de cabeçalho.

As funções nas bibliotecas de depuração CRT são compiladas com informações de depuração (/Z7, /Zd, /Zi, /ZI (Debug Information Format)) e sem otimização. Algumas funções contêm asserções para verificar parâmetros que são passados para elas, e o código-fonte é fornecido. Com esse código-fonte, você pode entrar em funções CRT para confirmar se as funções estão funcionando como você espera e verificar se há parâmetros ou estados de memória defeituosos. (Algumas tecnologias CRT são proprietárias e não fornecem código-fonte para tratamento de exceções, ponto flutuante e algumas outras rotinas.)

Para obter mais informações sobre as várias bibliotecas de tempo de execução que você pode usar, consulte C Run-Time Libraries.

Macros para relatórios

Para depuração, você pode usar as _RPTn macros e _RPTFn , definidas em <crtdbg.h>, para substituir o uso de printf instruções. Você não precisa incluí-los em #ifdef diretivas, porque eles desaparecem automaticamente na compilação da versão quando _DEBUG não estão definidos.

Macro Descrição
_RPT0, _RPT1, _RPT2, _RPT3, _RPT4 Gera uma cadeia de caracteres de mensagem e zero a quatro argumentos. Para _RPT1 through _RPT4, a cadeia de caracteres de mensagem serve como uma cadeia de formatação no estilo printf para os argumentos.
_RPTF0, _RPTF1, _RPTF2, _RPTF3, _RPTF4 O mesmo que _RPTn, mas essas macros também produzem o nome do arquivo e o número da linha onde a macro está localizada.

Considere o seguinte exemplo:

#ifdef _DEBUG
    if ( someVar > MAX_SOMEVAR )
        printf( "OVERFLOW! In NameOfThisFunc( ),
               someVar=%d, otherVar=%d.\n",
               someVar, otherVar );
#endif

Esse código gera os valores de someVar e otherVar para stdout. Você pode usar a seguinte chamada para _RPTF2 relatar esses mesmos valores e, adicionalmente, o nome do arquivo e o número da linha:

if (someVar > MAX_SOMEVAR) _RPTF2(_CRT_WARN, "In NameOfThisFunc( ), someVar= %d, otherVar= %d\n", someVar, otherVar );

Alguns aplicativos podem precisar de relatórios de depuração que as macros fornecidas com a biblioteca de tempo de execução C não fornecem. Para esses casos, você pode escrever uma macro projetada especificamente para atender às suas próprias necessidades. Em um de seus arquivos de cabeçalho, por exemplo, você pode incluir um código como o seguinte para definir uma macro chamada 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

Uma chamada para ALERT_IF2 poderia fazer todas as funções do printf código:

ALERT_IF2(someVar > MAX_SOMEVAR, "OVERFLOW! In NameOfThisFunc( ),
someVar=%d, otherVar=%d.\n", someVar, otherVar );

Você pode facilmente alterar uma macro personalizada para relatar mais ou menos informações para destinos diferentes. Essa abordagem é útil à medida que seus requisitos de depuração evoluem.

Escrita da função de gancho de depuração

Você pode escrever vários tipos de funções de gancho de depuração personalizadas que permitem inserir seu código em alguns pontos predefinidos dentro do processamento normal do depurador.

Funções de gancho de bloco do cliente

Se você quiser validar ou relatar o conteúdo dos dados armazenados em _CLIENT_BLOCK blocos, você pode escrever uma função especificamente para essa finalidade. A função que você escreve deve ter um protótipo semelhante ao seguinte, conforme definido em <crtdbg.h>:

void YourClientDump(void *, size_t)

Em outras palavras, sua função de gancho deve aceitar um void ponteiro para o início do bloco de alocação, juntamente com um size_t valor de tipo indicando o tamanho da alocação, e retornar void. Caso contrário, o seu conteúdo depende de si.

Depois de instalar sua função de gancho usando _CrtSetDumpClient, ela será chamada toda vez que um _CLIENT_BLOCK bloco for despejado. Em seguida, você pode usar _CrtReportBlockType para obter informações sobre o tipo ou subtipo de blocos despejados.

O ponteiro para a função para a _CrtSetDumpClient qual você passa é do tipo _CRT_DUMP_CLIENT, conforme definido em <crtdbg.h>:

typedef void (__cdecl *_CRT_DUMP_CLIENT)
   (void *, size_t);

Funções de gancho de alocação

Uma função de gancho de alocação, instalada usando _CrtSetAllocHook, é chamada toda vez que a memória é alocada, realocada ou liberada. Você pode usar esse tipo de gancho para muitos fins diferentes. Use-o para testar como um aplicativo lida com situações de memória insuficiente, como examinar padrões de alocação ou registrar informações de alocação para análise posterior.

Observação

Esteja ciente da restrição sobre o uso de funções de biblioteca de tempo de execução C em uma função de gancho de alocação, descrita em Ganchos de alocação e alocações de memória crt.

Uma função de gancho de alocação deve ter um protótipo como o exemplo a seguir:

int YourAllocHook(int nAllocType, void *pvData,
        size_t nSize, int nBlockUse, long lRequest,
        const unsigned char * szFileName, int nLine )

O ponteiro para _CrtSetAllocHook o qual você passa é do tipo _CRT_ALLOC_HOOK, conforme definido em <crtdbg.h>:

typedef int (__cdecl * _CRT_ALLOC_HOOK)
    (int, void *, size_t, int, long, const unsigned char *, int);

Quando a biblioteca de tempo de execução chama seu gancho, o nAllocType argumento indica qual operação de alocação está prestes a ser feita (_HOOK_ALLOC, _HOOK_REALLOC, ou _HOOK_FREE). Em um livre ou em uma realocação, pvData tem um ponteiro para o artigo do usuário do bloco prestes a ser liberado. No entanto, para uma alocação, esse ponteiro é nulo, porque a alocação não ocorreu. Os argumentos restantes contêm o tamanho da alocação, seu tipo de bloco, um número de solicitação sequencial e um ponteiro para o nome do arquivo. Se disponíveis, os argumentos também incluem o número da linha na qual a alocação foi feita. Depois que a função de gancho executa qualquer análise e outras tarefas que seu autor deseja, ela deve retornar , TRUEindicando que a operação de alocação pode continuar, ou FALSE, indicando que a operação deve falhar. Um gancho simples desse tipo pode verificar a quantidade de memória alocada até agora e retornar FALSE se essa quantidade exceder um pequeno limite. O aplicativo experimentaria então o tipo de erros de alocação que normalmente ocorreriam apenas quando a memória disponível estivesse baixa. Ganchos mais complexos podem acompanhar os padrões de alocação, analisar o uso da memória ou relatar quando situações específicas ocorrem.

Ganchos de alocação e alocações de memória CRT

Uma restrição importante nas funções de gancho de alocação é que elas devem ignorar explicitamente os _CRT_BLOCK blocos. Esses blocos são as alocações de memória feitas internamente pelas funções da biblioteca de tempo de execução C se elas fizerem chamadas para funções de biblioteca de tempo de execução C que alocam memória interna. Você pode ignorar _CRT_BLOCK blocos incluindo o seguinte código no início da função de gancho de alocação:

if ( nBlockUse == _CRT_BLOCK )
    return( TRUE );

Se o gancho de alocação não ignorar _CRT_BLOCK blocos, qualquer função de biblioteca de tempo de execução C chamada em seu gancho pode prender o programa em um loop infinito. Por exemplo, printf faz uma alocação interna. Se o código do gancho chamar printf, a alocação resultante fará com que o gancho seja chamado novamente, o que chamará printf novamente, e assim por diante, até que a pilha estoure. Se você precisar relatar _CRT_BLOCK operações de alocação, uma maneira de contornar essa restrição é usar funções de API do Windows, em vez de funções de tempo de execução C, para formatação e saída. Como as APIs do Windows não usam o heap da biblioteca de tempo de execução C, elas não intercetarão seu gancho de alocação em um loop infinito.

Se você examinar os arquivos de origem da biblioteca em tempo de execução, verá que a função _CrtDefaultAllocHook de gancho de alocação padrão (que simplesmente retorna TRUE), está localizada em um arquivo separado próprio, debug_heap_hook.cpp. Se você quiser que seu gancho de alocação seja chamado até mesmo para as alocações feitas pelo código de inicialização em tempo de execução que é executado antes da main função do seu aplicativo, você pode substituir essa função padrão por uma de suas próprias, em vez de usar _CrtSetAllocHook.

Funções de gancho de relatório

Uma função de gancho de relatório, instalada usando _CrtSetReportHook, é chamada toda vez _CrtDbgReport que gera um relatório de depuração. Você pode usá-lo, entre outras coisas, para filtrar relatórios para se concentrar em tipos específicos de alocações. Uma função de gancho de relatório deve ter um protótipo como este exemplo:

int AppReportHook(int nRptType, char *szMsg, int *retVal);

O ponteiro para _CrtSetReportHook o qual você passa é do tipo _CRT_REPORT_HOOK, conforme definido em <crtdbg.h>:

typedef int (__cdecl *_CRT_REPORT_HOOK)(int, char *, int *);

Quando a biblioteca de tempo de execução chama sua função hook, o nRptType argumento contém a categoria do relatório (_CRT_WARN, _CRT_ERROR, ou _CRT_ASSERT), szMsg contém um ponteiro para uma cadeia de caracteres de mensagem de relatório totalmente montada e retVal especifica se _CrtDbgReport deve continuar a execução normal depois de gerar o relatório ou iniciar o depurador. (Um retVal valor de zero continua a execução, um valor de 1 inicia o depurador.)

Se o gancho lidar com a mensagem em questão completamente, para que nenhum relatório adicional seja necessário, ele deve retornar TRUE. Se ele retornar FALSE, _CrtDbgReport irá relatar a mensagem normalmente.

Nesta secção

  • Depurar versões de funções de alocação de pilha

    Discute as versões especiais de depuração das funções de alocação de heap, incluindo: como o CRT mapeia chamadas, os benefícios de chamá-las explicitamente, como evitar a conversão, rastreando os tipos separados de alocações em blocos de cliente e os resultados de não definir _DEBUG.

  • Detalhes do heap de depuração CRT

    Descreve o gerenciamento de memória e o heap de depuração, os tipos de blocos no heap de depuração, as funções de relatório de estado de heap e como usar o heap de depuração para rastrear solicitações de alocação.

  • Encontre vazamentos de memória usando a biblioteca CRT

    Aborda técnicas para detetar e isolar vazamentos de memória usando o depurador e a Biblioteca Run-Time C.

Ver também