Facilitando a depuração de uma imagem no .NET
Observação
Este artigo é específico para aplicativos .NET Framework. Não se aplica a implementações mais recentes do .NET, incluindo o .NET 6 e versões posteriores.
Ao compilar código não gerenciado, você pode configurar uma imagem executável para depuração, definindo opções de IDE ou opções de linha de comando. Por exemplo, você pode usar a opção de linha de comando /Zi no Visual C++ para solicitar a ele que emita arquivos de símbolo de depuração (extensão de arquivo .pdb). De modo similar, a opção de linha de comando /Od informa ao compilador para desabilitar a otimização. O código resultante é executado mais lentamente, mas é mais fácil depurar, se isso for necessário.
Ao compilar o código gerenciado do .NET Framework, os compiladores, como Visual C++, Visual Basic e C#, compilam seu programa de origem na linguagem intermediária comum (CIL). Em seguida, a CIL é compilada com JIT, pouco antes da execução, no código do computador nativo. Assim como ocorre com código não gerenciado, você pode configurar uma imagem executável para depuração, definindo opções de IDE ou opções de linha de comando. Você também pode configurar a compilação JIT para depuração praticamente da mesma forma.
Essa configuração de JIT tem dois aspectos:
Você pode solicitar que o compilador JIT gere informações de acompanhamento. Isso permite que o depurador combine uma cadeia de CIL com sua contraparte de código de máquina e rastreie onde as variáveis locais e os argumentos de função estão armazenados. No .NET Framework versão 2,0 e posteriores, o compilador JIT sempre gera informações de rastreamento, portanto, não há necessidade de solicitá-la.
Você pode solicitar que o compilador JIT não otimize o código de máquina resultante.
Normalmente, o compilador que gera a CIL define essas opções do compilador JIT adequadamente com base nas opções IDE ou opções de linha de comando especificadas por você, por exemplo, /Od.
Em alguns casos, talvez você queira alterar o comportamento do compilador JIT para que o código de máquina gerado por ele seja mais fácil de depurar. Por exemplo, talvez você queira gerar informações de acompanhamento JIT de um build de varejo ou controlar a otimização. Você pode fazer isso com um arquivo de inicialização (.ini).
Por exemplo, se o assembly que você deseja depurar é chamado MyApp.exe, você pode criar, na mesma pasta dele, um arquivo de texto chamado MyApp.ini contendo estas três linhas:
[.NET Framework Debugging Control]
GenerateTrackingInfo=1
AllowOptimize=0
Você pode definir o valor de cada opção como 0 ou 1 e qualquer opção ausente assume 0 por padrão. Configurar GenerateTrackingInfo
como 1 e AllowOptimize
como 0 proporciona a depuração mais fácil.
Do .NET Framework 2.0 em diante, o compilador JIT sempre gera informações de rastreamento, independentemente do valor de GenerateTrackingInfo
; no entanto, o valor de AllowOptimize
ainda tem um efeito. Ao usar o Ngen.exe (Gerador de Imagens Nativas) para pré-compilar a imagem nativa sem otimização, o arquivo .ini deve estar presente na pasta de destino com AllowOptimize=0
quando Ngen.exe for executado. Se você tiver pré-compilado um assembly sem otimização, você deverá remover o código pré-compilado usando a opção /uninstall do NGen.exe antes de executar novamente o Ngen.exe para pré-compilar o código como otimizado. Se o arquivo .ini não estiver presente na pasta, Ngen.exe pré-compilará o código por padrão como otimizado.
O System.Diagnostics.DebuggableAttribute controla as configurações de um assembly. O DebuggableAttribute inclui dois campos que controlam se o compilador JIT deve otimizar e/ou gerar informações de rastreamento. Em .NET Framework 2.0 e versões posteriores, o compilador JIT sempre gera informações de acompanhamento.
Para uma compilação de varejo, os compiladores não definem o DebuggableAttribute. Por padrão, o compilador JIT gera o código de computador com maior desempenho e mais difícil de depurar. Habilitar o acompanhamento JIT reduz um pouco o desempenho, enquanto desabilitar a otimização reduz muito o desempenho.
O DebuggableAttribute aplica-se a um assembly inteiro por vez e não a módulos individuais dentro do assembly. Ferramentas de desenvolvimento, portanto, deverão anexar atributos personalizados ao token de metadados de assembly se um assembly já tiver sido criado ou então anexá-los à classe chamada System.Runtime.CompilerServices.AssemblyAttributesGoHere. A ferramenta ALink promoverá então esses atributos DebuggableAttribute de cada módulo para o assembly do qual eles se tornarão parte. Se houver um conflito, a operação ALink falhará.
Observação
Na versão 1.0 do .NET Framework, o compilador do Microsoft Visual C++ adiciona o DebuggableAttribute quando as opções do compilador /clr e /Zi são especificadas. Na versão 1.1 do .NET Framework, você deverá adicionar o DebugabbleAttribute manualmente ao seu código ou usar a opção de vinculador /ASSEMBLYDEBUG.