Análise de despejo de memória

Nem todos os bugs podem ser encontrados antes do lançamento, o que significa que nem todos os bugs que geram exceções podem ser encontrados antes do lançamento. Felizmente, a Microsoft incluiu no SDK da Plataforma uma função para ajudar os desenvolvedores a coletar informações sobre exceções descobertas pelos usuários. A função MiniDumpWriteDump grava as informações de despejo de memória necessárias em um arquivo sem salvar todo o espaço do processo. Esse arquivo de informações de despejo de memória é chamado de minidump. Este artigo técnico fornece informações sobre como escrever e usar um minidump.

Escrevendo um minidump

As opções básicas para escrever um minidump são as seguintes:

  • Não fazer nada. O Windows gera automaticamente um minidump sempre que um programa gera uma exceção sem tratamento. A geração automática de um minidump está disponível desde o Windows XP. Se o usuário permitir, o minidump será enviado para a Microsoft e não para o desenvolvedor, por meio Relatório de Erros do Windows (WER). Os desenvolvedores podem obter acesso a esses minidumps por meio do Programa de Aplicativos da Área de Trabalho do Windows.

    O uso de WER requer:

    • Desenvolvedores para assinar seus aplicativos usando o Authenticode
    • Os aplicativos têm um recurso VERSIONINFO válido em cada executável e DLL

    Se você implementar uma rotina personalizada para exceções sem tratamento, será fortemente instado a usar a função ReportFault no manipulador de exceção para também enviar um minidump automatizado para WER. A função ReportFault lida com todos os problemas de conexão e envio do minidump para WER. Não enviar minidumps para WER viola os requisitos de Jogos para Windows.

    Para obter mais informações sobre como o WER funciona, consulte Como Relatório de Erros do Windows funciona. Para obter uma explicação dos detalhes do registro, consulte Introdução Relatório de Erros do Windows na Zona ISV do MSDN.

  • Use um produto do Sistema de Equipe do Microsoft Visual Studio. No menu Depurar , clique em Salvar Despejo como para salvar uma cópia de um despejo. O uso de um despejo salvo localmente é apenas uma opção para teste interno e depuração.

  • Adicione código ao seu projeto. Adicione a função MiniDumpWriteDump e o código de tratamento de exceção apropriado para salvar e enviar um minidump diretamente para o desenvolvedor. Este artigo demonstra como implementar essa opção. No entanto, observe que MiniDumpWriteDump atualmente não funciona com código gerenciado e só está disponível no Windows XP, Windows Vista, Windows 7.

Acesso thread-safe

MiniDumpWriteDump faz parte da biblioteca DBGHELP. Essa biblioteca não é thread-safe, portanto, qualquer programa que usa MiniDumpWriteDump deve sincronizar todos os threads antes de tentar chamar MiniDumpWriteDump.

Escrevendo um minidump com código

A implementação real é simples. Veja a seguir um exemplo simples de como usar MiniDumpWriteDump.

#include <dbghelp.h>
#include <shellapi.h>
#include <shlobj.h>

int GenerateDump(EXCEPTION_POINTERS* pExceptionPointers)
{
    BOOL bMiniDumpSuccessful;
    WCHAR szPath[MAX_PATH]; 
    WCHAR szFileName[MAX_PATH]; 
    WCHAR* szAppName = L"AppName";
    WCHAR* szVersion = L"v1.0";
    DWORD dwBufferSize = MAX_PATH;
    HANDLE hDumpFile;
    SYSTEMTIME stLocalTime;
    MINIDUMP_EXCEPTION_INFORMATION ExpParam;

    GetLocalTime( &stLocalTime );
    GetTempPath( dwBufferSize, szPath );

    StringCchPrintf( szFileName, MAX_PATH, L"%s%s", szPath, szAppName );
    CreateDirectory( szFileName, NULL );

    StringCchPrintf( szFileName, MAX_PATH, L"%s%s\\%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp", 
               szPath, szAppName, szVersion, 
               stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay, 
               stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond, 
               GetCurrentProcessId(), GetCurrentThreadId());
    hDumpFile = CreateFile(szFileName, GENERIC_READ|GENERIC_WRITE, 
                FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);

    ExpParam.ThreadId = GetCurrentThreadId();
    ExpParam.ExceptionPointers = pExceptionPointers;
    ExpParam.ClientPointers = TRUE;

    bMiniDumpSuccessful = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), 
                    hDumpFile, MiniDumpWithDataSegs, &ExpParam, NULL, NULL);

    return EXCEPTION_EXECUTE_HANDLER;
}


void SomeFunction()
{
    __try
    {
        int *pBadPtr = NULL;
        *pBadPtr = 0;
    }
    __except(GenerateDump(GetExceptionInformation()))
    {
    }
}

Este exemplo demonstra o uso básico de MiniDumpWriteDump e as informações mínimas necessárias para chamá-lo. O nome do arquivo de despejo cabe ao desenvolvedor; no entanto, para evitar colisões de nome de arquivo, é aconselhável gerar o nome do arquivo a partir do nome do aplicativo e do número de versão, do processo e das IDs do thread e da data e hora. Isso também ajudará a manter os minidumps agrupados por aplicativo e versão. Cabe ao desenvolvedor decidir quantas informações são usadas para diferenciar nomes de arquivos de minidump.

Deve-se observar que o nome do caminho no exemplo anterior foi gerado chamando a função GetTempPath para recuperar o caminho do diretório designado para arquivos temporários. O uso desse diretório funciona mesmo com contas de usuário com privilégios mínimos e também impede que o minidump ocupar espaço no disco rígido depois que ele não for mais necessário.

Se você arquivar o produto durante o processo de build diário, inclua também símbolos para o build para que você possa depurar uma versão antiga do produto, se necessário. Você também precisa tomar medidas para manter otimizações completas do compilador ao gerar símbolos. Isso pode ser feito abrindo as propriedades do projeto no ambiente de desenvolvimento e, para a configuração de versão, fazendo o seguinte:

  1. No lado esquerdo da página de propriedades do projeto, clique em C/C++. Por padrão, isso exibe as configurações gerais . No lado direito da página de propriedades do projeto, defina Formato de Informações de Depuração como Banco de Dados de Programas (/Zi).
  2. No lado esquerdo da página de propriedades, expanda Vinculador e clique em Depuração. No lado direito da página de propriedades, defina Gerar Informações de Depuração como Sim (/DEBUG).
  3. Clique em Otimização e defina Referências como Eliminar dados não referenciados (/OPT:REF).
  4. Defina Habilitar dobramento COMDAT para remover COMDATs redundantes (/OPT:ICF).

O MSDN tem informações mais detalhadas sobre a estrutura MINIDUMP_EXCEPTION_INFORMATION e a função MiniDumpWriteDump .

Usando Dumpchk.exe

Dumpchk.exe é um utilitário de linha de comando que pode ser usado para verificar se um arquivo de despejo foi criado corretamente. Se Dumpchk.exe gerar um erro, o arquivo de despejo estará corrompido e não poderá ser analisado. Para obter informações sobre como usar Dumpchk.exe, consulte Como usar Dumpchk.exe para verificar um arquivo de despejo de memória.

Dumpchk.exe está incluído no CD do produto Windows XP e pode ser instalado no System Drive\Program Files\Support Tools\ executando Setup.exe na pasta Support\Tools\ no CD do produto Windows XP. Você também pode obter a versão mais recente do Dumpchk.exe baixando e instalando as ferramentas de depuração disponíveis nas Ferramentas de Depuração do Windows no Windows Hardware Developer Central.

Analisando um minidump

Abrir um minidump para análise é tão fácil quanto criar um.

Para analisar um minidump

  1. Abra o Visual Studio.
  2. No menu Arquivo , clique em Abrir Projeto.
  3. Defina Arquivos do tipo como Arquivos de Despejo, navegue até o arquivo de despejo, selecione-o e clique em Abrir.
  4. Execute o depurador.

O depurador criará um processo simulado. O processo simulado será interrompido na instrução que causou a falha.

Usando o Servidor de Símbolos Públicos da Microsoft

Para obter a pilha de falhas no nível do driver ou do sistema, talvez seja necessário configurar o Visual Studio para apontar para o servidor de símbolos públicos da Microsoft.

Para definir um caminho para o servidor de símbolos da Microsoft

  1. No menu Depurar , clique em Opções.
  2. Na caixa de diálogo Opções , abra o nó Depuração e clique em Símbolos.
  3. Verifique se Pesquisar os locais acima somente quando os símbolos forem carregados manualmente não estiver selecionado, a menos que você deseje carregar símbolos manualmente ao depurar.
  4. Se você estiver usando símbolos em um servidor de símbolo remoto, poderá melhorar o desempenho especificando um diretório local para o qual os símbolos podem ser copiados. Para fazer isso, insira um caminho para símbolos de cache do servidor de símbolos para esse diretório. Para se conectar ao servidor de símbolos públicos da Microsoft, você precisa habilitar essa configuração. Observe que, se você estiver depurando um programa em um computador remoto, o diretório de cache se referirá a um diretório no computador remoto.
  5. Clique em OK.
  6. Como você está usando o servidor de símbolos públicos da Microsoft, uma caixa de diálogo Contrato de Licença de Usuário Final é exibida. Clique em Sim para aceitar o contrato e baixar símbolos no cache local.

Depurando um minidump com WinDbg

Você também pode usar o WinDbg, um depurador que faz parte das Ferramentas de Depuração do Windows, para depurar um minidump. O WinDbg permite que você depure sem precisar usar o Visual Studio. Para baixar as Ferramentas de Depuração do Windows, confira Ferramentas de Depuração do Windows no Windows Hardware Developer Central.

Depois de instalar as Ferramentas de Depuração do Windows, você deve inserir o caminho do símbolo no WinDbg.

Para inserir um caminho de símbolo no WinDbg

  1. No menu Arquivo , clique em Caminho do Símbolo.

  2. Na janela Caminho de Pesquisa de Símbolos , insira o seguinte:

    "srv\*c:\\cache\*https://msdl.microsoft.com/download/symbols;"

Usando ferramentas de Copy-Protection com minidumps

Os desenvolvedores também precisam estar cientes de como seu esquema de proteção de cópia pode afetar o minidump. A maioria dos esquemas de proteção de cópia tem suas próprias ferramentas descramble e cabe ao desenvolvedor aprender a usar essas ferramentas com MiniDumpWriteDump.

Resumo

A função MiniDumpWriteDump pode ser uma ferramenta extremamente útil na coleta e resolução de bugs após o lançamento do produto. Escrever um manipulador de exceção personalizado que usa MiniDumpWriteDump permite que o desenvolvedor personalize a coleção de informações e melhore o processo de depuração. A função é flexível o suficiente para ser usada em qualquer projeto baseado em C++e deve ser considerada parte do processo de estabilidade de qualquer projeto.