Técnicas de depuração do CRT

Quando você depura 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 do CRT

A biblioteca de tempo de execução C (CRT) fornece amplo suporte à 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 de CRT são criadas com informações de depuração (/Z7, /Zd, /Zi, /ZI (formato das informações de depuração)) e sem otimização. Algumas funções contêm asserções para verificar os parâmetros passados a elas, e o código-fonte será fornecido. Com esse código-fonte, você pode acessar funções de CRT para confirmar se as funções estão funcionando conforme o esperado e verificar se há parâmetros incorretos ou estados de memória. (Algumas tecnologias CRT são proprietárias e não fornecem código-fonte para manipulação de exceções, ponto flutuante e algumas outras rotinas.)

Para obter mais informações sobre as várias bibliotecas em tempo de execução que você pode usar, confira Bibliotecas em tempo de execução C.

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 diretivas, porque eles desaparecem automaticamente em #ifdef sua compilação de versão quando _DEBUG não está definido.

Macro Descrição
_RPT0, _RPT1, _RPT2, _RPT3, _RPT4 Gera uma cadeia de caracteres de mensagem e zero a quatro argumentos. Para _RPT1 através _RPT4, a cadeia de caracteres de mensagem serve como uma cadeia de caracteres de formatação estilo printf para os argumentos.
_RPTF0, _RPTF1, _RPTF2, _RPTF3, _RPTF4 O mesmo que _RPTn, mas essas macros também geram 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 para reportar os mesmos valores e, além disso, o nome de arquivo e o número de 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. Nesses casos, você pode escrever uma macro projetada especificamente para atender aos seus próprios requisitos. Em um dos 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 alterar facilmente uma macro personalizada para relatar mais ou menos informações para destinos diferentes. Essa abordagem é útil à medida que seus requisitos de depuração evoluem.

Gravação 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 do bloco de cliente

Se você quiser validar ou reportar o conteúdo dos dados armazenados em blocos _CLIENT_BLOCK, poderá 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 ponteiro para o início do bloco de alocação, juntamente com um voidsize_t valor de tipo indicando o tamanho da alocação, e retornar void. Caso contrário, seu conteúdo depende de você.

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

O ponteiro para a função que você passa é _CrtSetDumpClient 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 muitas finalidades diferentes. Use-o para testar como um aplicativo trata 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 seguinte exemplo:

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

O ponteiro que você passa para _CrtSetAllocHook é 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_REALLOCou _HOOK_FREE). Em uma liberação ou em uma realocação, pvData tem um ponteiro para o artigo de usuário do bloco que está para ser liberado. No entanto, no caso de uma alocação, esse ponteiro é nulo, porque a alocação ainda 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ível, os argumentos também incluem o número de linha no 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 TRUE, indicando 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 então experimentaria o tipo de erros de alocação que normalmente ocorreriam somente quando a memória disponível fosse baixa. Ganchos mais complexos podem controlar os padrões de alocação, analisar o uso de memória ou reportar quando as situações específicas surgem.

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 _CRT_BLOCK explicitamente os blocos. Esses blocos serão as alocações de memória feitas internamente por funções da biblioteca de runtime C) se fizerem chamadas às funções da biblioteca de runtime C que alocam a memória interna. Você pode ignorar os blocos _CRT_BLOCK 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 blocos _CRT_BLOCK, qualquer função da biblioteca de runtime C chamada em seu gancho poderá interceptar 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 transborde. Se você precisar reportar operações de alocação _CRT_BLOCK, uma maneira de evitar 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 runtime C, elas não interceptarão seu gancho de alocação em um loop infinito.

Se você examinar os arquivos de origem da biblioteca de tempo de execução, verá que a função de gancho de alocação padrão, (que simplesmente retorna ), está localizada em um arquivo separado próprio, _CrtDefaultAllocHookdebug_heap_hook.cpp.TRUE Se você quiser que seu gancho de alocação seja chamado mesmo para as alocações feitas pelo código de inicialização em tempo de execução que é executado antes da função do main aplicativo, você pode substituir essa função padrão por uma de sua própria, 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á-la, entre outras coisas, para filtrar relatórios com foco em tipos de alocações específicos. 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 que você passa para _CrtSetReportHook é 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 de gancho, o argumento contém a categoria do relatório (_CRT_WARN, ou ), 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 após gerar o relatório ou _CRT_ASSERTiniciar o nRptType_CRT_ERRORdepurador. (Um retVal valor de zero continua a execução, um valor de 1 inicia o depurador.)

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

Nesta seção

  • Versões de depuração das funções de alocação de heap

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

  • Detalhes de heap de depuração do 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.

  • Localizar vazamentos de memória usando a biblioteca CRT

    Aborda técnicas para detectar e isolar vazamentos de memória usando o depurador e a biblioteca em tempo de execução C.

Confira também