Depuração com símbolos

Este artigo fornece uma visão geral de alto nível sobre a melhor forma de usar símbolos em seu processo de depuração. Ele explica como usar o servidor de símbolos da Microsoft e também como configurar e usar seu próprio servidor de símbolos privado. Essas práticas recomendadas podem ajudar a aumentar sua eficácia e capacidade de depurar problemas, mesmo nos casos em que todos os símbolos e arquivos executáveis relacionados a um problema não estão localizados no seu computador.

Símbolos

Vários tipos diferentes de símbolos estão disponíveis para depuração. Dentre eles estão os símbolos CodeView, COFF, DBG, SYM, PDB e até símbolos de exportação gerados a partir de uma tabela de exportação de arquivos binários. Este white paper discute apenas o VS.NET e os símbolos de formato PDB, pois esse é o formato preferido e mais recente. São gerados por padrão para projetos compilados usando o Visual Studio.

A geração de arquivos PDB para versões executáveis não afeta nenhuma otimização ou altera significativamente o tamanho dos arquivos gerados. Normalmente, a única diferença é o caminho e o nome do arquivo PDB incorporado no executável. Por esse motivo, você deve sempre produzir arquivos PDB, mesmo que não queira enviá-los com o executável.

Os arquivos PDB serão gerados se um projeto é compilado usando a opção de compilador /Zi ou /ZI (produzir informações PDB), juntamente com a opção de vinculador /DEBUG (gerar informações de depuração). Os arquivos PDB gerados pelo compilador são combinados e gravados em um único arquivo PDB, que é colocado no mesmo diretório do executável.

Por padrão, os arquivos PDB contêm as seguintes informações:

  • Símbolos públicos (geralmente todas as funções, variáveis estáticas e globais)
  • Uma lista de arquivos de objeto responsáveis por seções de código no executável
  • Informações de otimização do ponteiro de quadro (FPO)
  • Informações de nome e tipo para variáveis locais e estruturas de dados
  • Informações sobre o arquivo de origem e o número da linha

Se você estiver preocupado com a possibilidade das pessoas usarem as informações do arquivo PDB para fazer engenharia reversa do seu executável, é possível gerar arquivos PDB simplificados, usando a opção do vinculador /PDBSTRIPPED:filename. Se você tiver arquivos PDB dos quais deseja remover informações privadas, poderá usar uma ferramenta chamada pdbcopy, que faz parte das ferramentas de depuração para o Windows.

Por padrão, os arquivos PDB simplificados contêm as seguintes informações:

  • Símbolos públicos (geralmente apenas funções não estáticas e variáveis globais)
  • Uma lista de arquivos de objeto responsáveis por seções de código no executável
  • Informações de otimização do ponteiro de quadro (FPO)

Essas são as informações mínimas necessárias para permitir uma depuração confiável. As informações mínimas também dificultam a obtenção de informações adicionais sobre seu código-fonte original. Como tanto um arquivo PDB reduzido e um arquivo PDB regular são gerados, você pode fornecer a versão reduzida para usuários que podem precisar de capacidades de depuração limitadas, mas manter os PDBs completos confidenciais. Observe que /PDBSTRIPPED gera um segundo arquivo PDB menor, portanto, certifique-se de usar o arquivo PDB correto ao gerar compilações para distribuir amplamente. Para um projeto comum, um PDB regular pode ter alguns megabytes de tamanho, mas uma versão simplificada do PDB pode ter apenas algumas centenas de quilobytes.

Uso de símbolos para depuração

Ao depurar um aplicativo que falhou, o depurador tenta mostrar as funções na pilha que levaram à falha. Sem um arquivo PDB, o depurador não consegue resolver os nomes de função, seus parâmetros ou quaisquer variáveis locais armazenadas na pilha. Se você depurar executáveis de 32 bits, haverá situações em que não poderá sequer obter rastreamentos de pilha confiáveis sem símbolos. Às vezes, é possível analisar os valores brutos na pilha e descobrir quais valores podem ser endereços de retorno, mas eles podem ser facilmente confundidos com referências de função ou dados.

Se as funções na pilha atual foram compiladas usando a otimização Omitir Ponteiros de Quadro (/Oy) e, se os símbolos não estiverem presentes, o depurador não poderá determinar de forma confiável qual função chamou a função atual. Isso ocorre porque, sem as informações de Otimização do Ponteiro de Quadro (FPO) que os PDBs contêm, o depurador não pode contar com o registro de ponteiro de quadro (EBP) para apontar para o ponteiro de quadro anterior salvo e no endereço de retorno da função pai. Em vez disso, ele faz uma suposição. Às vezes acerta. No entanto, muitas vezes erra, o que pode induzir ao erro. Se você vir um aviso sobre símbolos ausentes ou nenhum símbolo carregado, como no exemplo a seguir, não confie na pilha desse ponto para baixo.

SWPerfTest.exe!TextFunction(... ...)    Line 59    C++
d3dx9d.dll!008829b5()
[Frames below may be incorrect and/or missing, no symbols loaded for d3dx9d.dll]
SWPerfTest.exe!main(int argc=, const char * * argv=)  Line 328 + 0x12 bytes     C++
SWPerfTest.exe!__mainCRTStartup() Line 716 + 0x17 bytes    C
kernel32.dll!@BaseThreadInitThunk@12() + 0x12 bytes
ntdll.dll!__RtlUserThreadStart@8() + 0x27 bytes

Em muitos casos, é possível continuar depurando sem símbolos, pois o problema está em um local que tem símbolos precisos e você não precisa analisar as funções mais abaixo na pilha de chamadas. Mesmo que uma biblioteca que esteja na sua pilha de chamadas não tenha PDBs disponíveis, contanto que tenha sido compilada com ponteiros de quadro, o depurador deve ser capaz de adivinhar corretamente as funções pai. A partir do Windows XP Service Pack 2, todos os arquivos executáveis e DLL do Windows são compilados com a FPO desabilitada, pois torna a depuração mais precisa. Desabilitar a FPO também permite que os perfis de amostragem percorram a pilha durante o tempo de execução, com impacto mínimo no desempenho. Em versões do Windows anteriores ao Windows XP SP2, todos os binários do sistema operacional exigem arquivos de símbolo correspondentes que contêm informações de FPO, para permitir depuração e criação de perfil precisas.

Se você depurar executáveis nativos de 64 bits, não precisará de arquivos de símbolo para produzir rastreamentos de pilha válidos, pois os compiladores e sistemas operacionais x64 foram projetados para não precisar deles. No entanto, você ainda precisa de arquivos de símbolo para recuperar os nomes das funções, parâmetros de chamada e variáveis locais.

No entanto, alguns casos são particularmente difíceis de depurar sem símbolos. Por exemplo, se você estiver depurando um programa para o qual compilou um arquivo PDB e se ocorrer uma falha em um retorno de chamada de uma função em uma DLL para a qual não você não possui símbolos, não poderá ver qual função causou o retorno de chamada, pois você não poderá decodificar a pilha. Isso acontece com frequência em bibliotecas de terceiros, se os PDBs não forem fornecidos ou em componentes antigos do sistema operacional, se os PDBs não estiverem disponíveis. Os retornos de chamada geralmente ocorrem durante a passagem de mensagens, enumeração, alocação de memória ou tratamento de exceções. Depurar essas funções sem uma pilha precisa pode ser frustrante.

Para depurar de forma confiável os minidespejos gerados em um computador diferente ou que falharam no código que você não possui, é importante poder acessar todos os símbolos e binários dos executáveis referenciados no minidespejo. Se os símbolos e binários estiverem disponíveis em um servidor de símbolos, eles serão obtidos automaticamente pelo depurador. Para mais informações sobre minidespejos, confira o white paper chamado Análise de despejo de memória.

Obtenção dos símbolos necessários

O Visual Studio e outros depuradores da Microsoft, como o WinDbg, geralmente são configurados para funcionar apenas se você estiver compilando um aplicativo e depurando-o no seu próprio computador. Se você precisar fornecer seu executável para outra pessoa, se você tiver várias versões de uma DLL ou um arquivo .exe em seu computador ou se quiser depurar com precisão um aplicativo que usa bibliotecas do Windows ou outras bibliotecas, como o DirectX, você precisará entender como os depuradores encontram e carregam símbolos. O depurador usa o caminho de pesquisa de símbolos especificado pelo usuário, que é encontrado em Options\Debugging\Symbols no Visual Studio, ou a variável de ambiente _NT_SYMBOL_PATH. Normalmente, o depurador procura PDBs correspondentes nos seguintes locais:

  • No local especificado dentro da DLL ou do arquivo executável.

    Se você tiver compilou uma DLL ou um arquivo executável em seu computador, por padrão, o vinculador colocará o caminho completo e o nome do arquivo PDB associado dentro da DLL ou do arquivo executável. Quando você depura, o depurador primeiro verifica se o arquivo de símbolos existe no local especificado dentro da DLL ou do arquivo executável. Isso é útil, pois você sempre terá símbolos disponíveis para o código que compilou no seu computador.

  • PDBs que podem estar presentes na mesma pasta que a DLL ou o arquivo executável.

  • Em algumas pastas de cache de símbolo locais.

  • Todos os servidores de símbolos de compartilhamento de arquivos em uma rede local.

  • Todos os servidores de símbolos da Internet, como o servidor de símbolos da Microsoft.

Para garantir que você tenha todos os PDBs necessários para uma depuração precisa, instale as ferramentas de depuração para o Windows. As versões de 32 e 64 bits podem ser encontradas nas Ferramentas de depuração para o Windows.

Uma ferramenta útil instalada com esse pacote é a symchk.exe. Ela pode ajudar a identificar símbolos ausentes ou incorretos. Essa ferramenta tem um grande número de potenciais opções de linha de comando. Aqui estão duas das mais úteis e comumente usadas.

Verificar se um determinado arquivo DLL ou .exe e PDB na mesma pasta correspondem

"c:\Program Files\Debugging Tools for Windows\symchk" testing.dll /s .

SYMCHK: FAILED files = 0
SYMCHK: PASSED + IGNORED files = 1

A opção/s . informa ao symchk para procurar símbolos apenas na pasta atual e não procurar em nenhum servidor de símbolos.

Verificar se todas as DLLs e arquivos executáveis em um conjunto de pastas têm PDBs correspondentes

"c:\Program Files\Debugging Tools for Windows\symchk" *.* /r

A opção/r configura osymchk para percorrer recursivamente as pastas, para verificar se todos os arquivos executáveis têm PDBs correspondentes. Sem a opção /s, o symchk usa ou _NT_SYMBOL_PATH atual para procurar símbolos em qualquer servidor privado ou local ou nos servidores de símbolos da Microsoft. A ferramenta symchk procura apenas por símbolos de arquivos executáveis (.exe, .dll e similar). Você não pode usar pesquisas com caracteres curingas em busca de símbolos de arquivos não executáveis.

Como funciona o symchk

Quando o vinculador gera arquivos .dll, executáveis e PDB, ele armazena GUIDs idênticos em cada arquivo. O GUID é usado por ferramentas para determinar se um determinado um dado arquivo PDB corresponde a uma DLL ou a um arquivo executável. Se você alterar uma DLL ou um arquivo executável (usando um editor de recursos ou uma codificação de proteção contra cópia, ou alterando as informações de versão), o GUID será atualizado e o depurador não poderá carregar o arquivo PDB. Por esse motivo, é muito importante evitar a manipulação da DLL ou do arquivo executável depois de criados pelo vinculador.

Você também pode usar o programa utilitário DUMPBIN que vem com o VS.NET para mostrar os caminhos de símbolo que são pesquisados e verificar se foram encontrados arquivos de símbolos que correspondam a uma determinada DLL ou arquivo executável. Por exemplo:

DUMPBIN /PDBPATH:VERBOSE filename.exe

Servidores de símbolos

Um servidor de símbolos é um repositório para várias versões de arquivos executáveis e de símbolos. Ele contém os próprios arquivos de símbolos ou ponteiros para os arquivos de símbolos associados. Os depuradores entendem como usar servidores de símbolos e podem empregá-los para procurar por símbolos ausentes ou desconhecidos.

Arquivos DLL e executáveis também estão disponíveis no servidor de símbolos da Microsoft. Isso possibilita a depuração de falhas e a análise de código para arquivos do sistema operacional que talvez não existam em sua máquina. Se um depurador encontrar um arquivo executável ou uma DLL que não exista no sistema que você está usando para depuração, ele solicitará automaticamente os símbolos e uma cópia do arquivo binário dos servidores de símbolos da Microsoft. Isso será útil ao depurar um componente que tenha muitas versões, por exemplo, msvcrt.dll, e você precisar examinar o código de uma versão que não está presente no seu computador. Isso também ajuda a depurar minidespejos gerados em um sistema operacional diferente do sistema que você está usando para depuração.

A Microsoft publica todos os arquivos PDB para todos os sistemas operacionais e outros componentes redistribuídos, como o SDK do DirectX, em seu servidor de símbolos acessível externamente. Isso facilita a depuração de um aplicativo que usa esses arquivos DLL ou executáveis. Você pode usar o servidor de símbolos da Microsoft para resolver símbolos, juntamente com todos os símbolos locais para componentes criados no seu computador.

Você pode configurar seu computador para usar o servidor de símbolos da Microsoft, proporcionando acesso a todos os arquivos de símbolos da Microsoft. Além disso, você também pode configurar um servidor de símbolos privado para sua empresa, equipe ou rede, que pode ser usado para armazenar várias versões antigas de um projeto em que você está trabalhando ou para fornecer um cache local para os símbolos usados no servidor de símbolos da Microsoft.

Para usar um servidor de símbolos, especifique o caminho de pesquisa em uma variável de ambiente chamada _NT_SYMBOL_PATH. Depuradores e ferramentas modernas, como WinDbg, NTSD ou Visual Studio, usam automaticamente esse caminho para procurar símbolos.

Quando um depurador procura símbolos, ele primeiro pesquisa localmente. Em seguida, procura nos servidores de símbolos. Ao encontrar um símbolo correspondente, ele transfere o arquivo de símbolo para seu cache local. Os símbolos para uma DLL típica ou um arquivo executável comum variam de 1 a 100 MB de tamanho. Portanto, ao depurar um processo que inclui muitas DLLs, poderá levar algum tempo para resolver todos os símbolos e transferi-los para um cache local.

Uso do servidor de símbolos da Microsoft

O servidor de símbolos da Microsoft permite obter todos os símbolos mais recentes, incluindo símbolos para arquivos corrigidos ou atualizados. O servidor de símbolos da Microsoft está disponível em https://msdl.microsoft.com/download/symbols.

Você pode acessar o servidor de símbolos de várias maneiras:

  • Insira o endereço do servidor diretamente. No Visual Studio, no menu Ferramentas, escolha Opções, Depuração e, em seguida, escolha Símbolos.

  • Use a variável de ambiente _NT_SYMBOL_PATH. Este método é recomendável.

    Ela é usada por todas as ferramentas de depuração. Também é usado pelo Visual Studio e é lida e decodificada quando o Visual Studio é aberto. Portanto, se você alterá-la, será necessário reiniciar o Visual Studio.

    Essa variável de ambiente permite especificar vários servidores de símbolos, por exemplo, um servidor de símbolos privado interno. Também permite especificar um diretório de cache local para armazenar PDBs de todos os símbolos procurados em servidores de símbolos, tanto internamente quanto pela Internet.

A sintaxe da variável _NT_SYMBOL_PATH é:

srv*[local cache]*[private symbol server]*https://msdl.microsoft.com/download/symbols

Substitua [cache local] pelo nome de um diretório no computador onde você deseja armazenar um cache de todos os símbolos usados, por exemplo, %SYSTEMROOT%\Symbols ou c:\symbols.

O [servidor de símbolos privado] é opcional. Ele pode apontar para um servidor de símbolos que está localizado em sua rede ou apontar para um servidor de símbolos compartilhado por sua equipe, grupo de produtos ou empresa.

Para usar apenas o servidor de símbolos da Microsoft junto com um cache local de símbolos, para acelerar o acesso pela Internet, use a seguinte configuração para _NT_SYMBOL_PATH:

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

Você pode encontrar outras opções para o _NT_SYMBOL_PATH no arquivo de ajuda que é instalado com o pacote Ferramentas de Depuração da Microsoft para o Windows.

Os executáveis sem símbolos podem aumentar o tempo necessário para iniciar um depurador se você usar um servidor de símbolos. Isso acontece porque o depurador consulta o servidor de símbolos sempre que tenta carregar o executável. Por esse motivo, sempre é melhor solicitar símbolos para todos os componentes.

Talvez não seja possível solicitar símbolos para todos os componentes. Por exemplo, drivers de vídeo podem ter DLLs em seu espaço de processo e os arquivos PDB necessários estão disponíveis no servidor de símbolos da Microsoft. Nesse caso, há um pequeno atraso ao iniciar uma sessão de depuração.

Para evitar até mesmo esse pequeno atraso, execute o depurador uma vez para armazenar em cache todos os símbolos do servidor de símbolos da Microsoft localmente. Em seguida, modifique o _NT_SYMBOL_PATH para remover o servidor de símbolos da Microsoft. A menos que os arquivos executáveis sejam alterados, as verificações de arquivos executáveis que não têm símbolos não exigirão uma consulta pela Internet, porque você tem cópias locais em cache de todos os símbolos necessários do servidor de símbolos da Microsoft.

Obtenção de símbolos de forma manual

Se você configurou o depurador corretamente, ele carrega automaticamente todos os símbolos necessários do seu cache local ou de um servidor de símbolos. Se você deseja obter os símbolos apenas para um único executável ou para uma pasta de executáveis, poderá usar symchk. Por exemplo, se você quiser baixar os símbolos do arquivo d3dx9_30.dll na pasta System do Windows para o diretório atual, poderá usar o seguinte comando:

"c:\Program Files\Debugging Tools for Windows\symchk" c:\Windows\System32\d3dx9_30.dll /oc \.

A ferramenta symchk tem muitos outros usos. Para mais detalhes, confira symchk /?, ou confira a documentação das Ferramentas de Depuração da Microsoft para o Windows.

Configuração de um servidor de símbolos

Configurar de um servidor de símbolos é muito simples. Ele é útil pelos seguintes motivos:

  • Para economizar largura de banda ou acelerar a resolução de símbolos para sua empresa, equipe ou produto. Um servidor de símbolos interno em um compartilhamento de arquivos local em sua rede armazena em cache todas as referências a servidores de símbolos externos, como o servidor de símbolos da Microsoft. Um servidor de símbolos local ou interno pode ser acessado rapidamente por muitas pessoas ao mesmo tempo. Dessa forma, ele economiza a largura de banda e a latência que as solicitações duplicadas de símbolos podem criar.
  • Para armazenar símbolos de compilações, versões ou versões externas antigas do seu aplicativo. Armazenando os símbolos dessas compilações em um servidor de símbolos ao qual você pode acessar facilmente, é possível depurar falhas e problemas nessas compilações em qualquer computador que tenha um depurador e uma conexão com o servidor de símbolos local. Isso é bastante útil se você depurar minidespejos gerados por executáveis que você não compilou, ou seja, compilações que foram geradas por outro programador ou por um computador de compilação. Se os símbolos dessas compilações forem armazenados no servidor de símbolos, você terá uma depuração confiável e precisa.
  • Para manter os símbolos atualizados. Quando os componentes são atualizados, como componentes do sistema operacional modificados pelo Windows Update ou pelo SDK do DirectX, ainda é possível depurar usando todos os símbolos mais recentes.

Configurar um servidor de símbolos em sua própria rede local é tão simples quanto criar um compartilhamento de arquivos em um servidor e conceder permissões totais aos usuários para acessar o compartilhamento, criar arquivos e pastas. Esse compartilhamento deve ser criado em um sistema operacional de servidor, como o Windows Server 2003, para evitar limitações no número de pessoas que podem acessá-la simultaneamente.

Por exemplo, se você configurar um compartilhamento de arquivos em \\mainserver\symbols, os membros da sua equipe podem definir o _NT_SYMBOL_PATH da seguinte maneira:

Srv*c:\symbols*\\mainserver\symbols*https://msdl.microsoft.com/download/symbols

À medida que os símbolos são recuperados, arquivos e pastas aparecem no diretório compartilhado \\mainserver\symbols, bem como em caches individuais, no diretório c:\symbols.

Geralmente, isso é tudo o que está envolvido com a configuração e o uso do seu próprio servidor de símbolos ou do servidor de símbolos da Microsoft.

Adição de símbolos a um servidor de símbolos

Para adicionar, excluir ou editar arquivos em um compartilhamento de servidor de símbolos, use a ferramenta symstore.exe. Essaferramenta faz parte do pacote Ferramentas de Depuração da Microsoft para Windows. A documentação completa sobre servidores de símbolos, a ferramenta symstore e a indexação de símbolos está incluída no pacote Ferramentas de Depuração para Windows.

Talvez você queira adicionar símbolos diretamente ao seu próprio servidor de símbolos, como parte de um processo de compilação, ou disponibilizar símbolos para toda a sua equipe em bibliotecas ou ferramentas de terceiros. O processo de adicionar um símbolo a um compartilhamento de arquivos do servidor de símbolos é chamado de indexação de símbolos. Existem duas maneiras comuns de indexar símbolos. Um arquivode símbolo pode ser copiado para o servidor de símbolos. Ou um copiar um ponteiro para o local do símbolo no servidor de símbolos. Se você tiver uma pasta de arquivos que contenha suas compilações antigas, pode ser útil indexar ponteiros para os arquivos PDB que já estão no compartilhamento, em vez de duplicar símbolos. Dado que os símbolos podem ter dezenas de megabytes de tamanho, é uma boa ideia planejar antecipadamente quanto espaço você precisará para arquivar todas as compilações do seu projeto durante todo o desenvolvimento. Se você indexar apenas ponteiros para símbolos, poderá ter problemas se remover compilações antigas ou alterar o nome de um compartilhamento de arquivos.

Por exemplo, para indexar recursivamente todos os símbolos em c:\dxsym\Extras\Symbols obtidos do SDK do DirectX de outubro de 2006 em um compartilhamento de arquivos de servidor de símbolos chamado \\mainserver\symbols, você pode usar o seguinte comando:

"c:\Program Files\Debugging Tools for Windows\symstore" add /f "C:\dxsym\Extras\Symbols\*.pdb"
/s \\mainserver\symbols /t "October 2006 DirectX SDK " /r

O parâmetro /t "comment" é usado para adicionar uma descrição à transação que adicionou os símbolos. Isso pode ser útil ao realizar tarefas administrativas nos símbolos.

Práticas Recomendadas

  • Configure seu próprio compartilhamento de arquivos de servidor de símbolos para sua equipe, empresa ou produto.
  • Configure o _NT_SYMBOL_PATH para apontar para um cache local, um servidor de símbolos privado e para o servidor de símbolos da Microsoft.
  • Se um depurador não puder carregar símbolos para um componente que você está depurando, entre em contato com o proprietário do componente para solicitar símbolos, pelo menos um PDB simplificado.
  • Configure um sistema de compilação automatizado para indexar símbolos em seu servidor de símbolos privado para cada compilação produzida. Verifique se as compilações que você distribui são as compilações geradas por esse processo. Isso garante que os símbolos estejam sempre disponíveis para depurar problemas.
  • Configure um servidor de símbolos para permitir que os depuradores acessem o código-fonte de um módulo específico diretamente de um sistema de controle de código-fonte baseado no Visual Source Safe ou Perforce. Se as informações do arquivo de origem e os símbolos de uma versão lançada de um jogo forem indexados, os desenvolvedores que tiverem acesso ao servidor de símbolos poderão realizar a depuração completa do nível da origem dos problemas relatados, sem manter ambientes de compilação ou versões antigas de arquivos de origem em seus computadores de desenvolvimento. Para configurar o servidor de símbolos a fim de permitir a indexação de informações do arquivo de origem, confira a documentação sobre o servidor de origem.