Depuração para completos iniciantes
Sem falhas, o código que escrevemos como desenvolvedores de software nem sempre faz o que esperávamos que fizesse. Às vezes faz algo completamente diferente! Quando o inesperado acontece, a próxima tarefa é descobrir o porquê e, embora possamos ficar tentados a ficar olhando para nosso código por horas, é mais fácil e eficiente usar uma ferramenta de depuração ou depurador.
Um depurador, infelizmente, não é algo que pode revelar magicamente todos os problemas ou "bugs" em nosso código. Depuração significa executar seu código passo a passo em uma ferramenta de depuração como o Visual Studio, para encontrar o ponto exato onde você cometeu um erro de programação. Em seguida, você entende quais correções precisa fazer em seu código e as ferramentas de depuração geralmente permitem que você faça alterações temporárias para que você possa continuar executando o programa.
Usar um depurador de forma eficaz também é uma habilidade que leva tempo e prática para aprender, mas é, em última análise, uma tarefa fundamental para todo desenvolvedor de software. Neste artigo, apresentamos os princípios fundamentais da depuração e fornecemos dicas para você começar.
Esclareça o problema fazendo-se as perguntas certas
Isso ajuda a esclarecer o problema que você encontrou antes de tentar corrigi-lo. Esperamos que já tenhas encontrado um problema no teu código, caso contrário, não estarias aqui a tentar descobrir como depurá-lo! Portanto, antes de começar a depuração, certifique-se de ter identificado o problema que pretende resolver:
O que você esperava que seu código fizesse?
O que aconteceu em vez disso?
Se você encontrar um erro (exceção) ao executar seu aplicativo, pode ser uma coisa boa! Uma exceção é um evento inesperado encontrado ao executar código, normalmente um erro de algum tipo. Uma ferramenta de depuração pode levá-lo ao local exato em seu código onde a exceção ocorreu e pode ajudá-lo a investigar possíveis correções.
Se algo mais aconteceu, qual é o sintoma do problema? Você já suspeita onde esse problema ocorreu no seu código? Por exemplo, se o código exibir algum texto, mas o texto estiver incorreto, você saberá que seus dados estão ruins ou que o código que define o texto de exibição tem algum tipo de bug. Ao percorrer o código em um depurador, você pode examinar cada alteração em suas variáveis para descobrir exatamente quando e como valores incorretos são atribuídos.
Examine suas suposições
Antes de investigar um bug ou um erro, pense nas suposições que fizeram você esperar um determinado resultado. Suposições ocultas ou desconhecidas podem atrapalhar a identificação de um problema, mesmo quando você está olhando diretamente para a causa do problema em um depurador. Você pode ter uma longa lista de suposições possíveis! Aqui estão algumas perguntas para se fazer para desafiar suas suposições.
Você está usando a API certa (ou seja, o objeto, função, método ou propriedade certo)? Uma API que você está usando pode não fazer o que você acha que faz. (Depois de examinar a chamada de API no depurador, corrigi-la pode exigir uma consulta à documentação para localizar a API correta.)
Você está usando uma API corretamente? Talvez você tenha usado a API certa, mas não a usou da maneira certa.
O seu código contém algum erro de digitação? Alguns erros de digitação, como um simples erro ortográfico de um nome de variável, podem ser difíceis de ver, especialmente ao trabalhar com idiomas que não exigem que as variáveis sejam declaradas antes de serem usadas.
Você fez uma alteração no seu código e assumiu que ele não está relacionado ao problema que você está vendo?
Você esperava que um objeto ou variável contivesse um determinado valor (ou um certo tipo de valor) diferente do que realmente aconteceu?
Você sabe a intenção do código? Muitas vezes, é mais difícil depurar o código de outra pessoa. Se não for o seu código, é possível que você precise gastar tempo aprendendo exatamente o que o código faz antes de poder depurá-lo efetivamente.
Dica
Ao escrever código, comece pequeno e comece com o código que funciona! (Um bom exemplo de código é útil aqui.) Às vezes, é mais fácil corrigir um conjunto grande ou complicado de código começando com um pequeno pedaço de código que demonstra a tarefa principal que você está tentando alcançar. Em seguida, você pode modificar ou adicionar código incrementalmente, testando em cada ponto a existência de erros.
Ao questionar suas suposições, você pode reduzir o tempo necessário para encontrar um problema em seu código. Você também pode reduzir o tempo necessário para corrigir um problema.
Percorra o código no modo de depuração para encontrar onde o problema ocorreu
Quando você normalmente executa um aplicativo, você vê erros e resultados incorretos somente depois que o código é executado. Um programa também pode ser encerrado inesperadamente sem dizer o porquê.
Quando você executa um aplicativo dentro de um depurador, também chamado de modo de depuração , o depurador monitora ativamente tudo o que está acontecendo enquanto o programa é executado. Isso também permite que o utilizador pause a aplicação a qualquer momento para examinar o seu estado e, em seguida, percorrer o seu código linha por linha para observar cada detalhe à medida que acontece.
No Visual Studio, você entra no modo de depuração usando F5 (ou o comando do menu Depurar>Iniciar Depuração ou o botão Iniciar Depuração na Barra de Ferramentas Depurar). Se ocorrer alguma exceção, o Auxiliar de Exceção do Visual Studio levará você ao ponto exato onde a exceção ocorreu e fornecerá outras informações úteis. Para obter mais informações sobre como lidar com exceções em seu código, consulte Técnicas e ferramentas de depuração.
Se você não obteve uma exceção, provavelmente tem uma boa ideia de onde procurar o problema em seu código. Quando usas os pontos de interrupção com o depurador, tens a oportunidade de examinar o teu código mais cuidadosamente. Os pontos de interrupção são a funcionalidade mais básica e essencial da depuração fiável. Um ponto de interrupção indica onde o Visual Studio deve pausar seu código em execução para que você possa examinar os valores das variáveis, ou o comportamento da memória, a sequência na qual o código é executado.
No Visual Studio, você pode definir rapidamente um ponto de interrupção clicando na margem esquerda ao lado de uma linha de código. Ou coloque o cursor sobre uma linha e pressione F9.
Para ajudar a ilustrar esses conceitos, levamos você através de alguns exemplos de código que já tem vários bugs. Estamos usando C#, mas os recursos de depuração se aplicam ao Visual Basic, C++, JavaScript, Python e outras linguagens suportadas. Código de exemplo para Visual Basic também é fornecido, mas capturas de tela estão em C#.
Criar um aplicativo de exemplo (com alguns bugs)
Em seguida, você cria um aplicativo que tem alguns bugs.
Você deve ter o Visual Studio instalado e o .NET desktop development workload instalado.
Se você ainda não instalou o Visual Studio, vá para a página de downloads do Visual Studio para instalá-lo gratuitamente.
Se você precisar instalar a carga de trabalho, mas já tiver o Visual Studio, selecione Ferramentas >Obter Ferramentas e Recursos. O instalador do Visual Studio é iniciado. Escolha a carga de trabalho de desenvolvimento de desktop .NET e, em seguida, escolha Modificar.
Abra o Visual Studio.
Na janela Iniciar, escolha Criar um novo projeto. Digite console na caixa de pesquisa, selecione C# ou Visual Basic como a linguagem e escolha Aplicação de Console para .NET. Escolha Próximo. Digite ConsoleApp_FirstApp como o nome do projeto e selecione Avançar.
Se você usar um nome de projeto diferente, precisará modificar o valor do namespace para corresponder ao nome do projeto ao copiar o código de exemplo.
Escolha a estrutura de destino recomendada ou .NET 8, depois selecione Criar.
Se você não vir o modelo de projeto do Console App para .NET, vá para Ferramentas >Obter Ferramentas e Recursos, que abre o Visual Studio Installer. Escolha o de trabalho de desenvolvimento da área de trabalho .NET e, em seguida, escolha Modificar.
O Visual Studio cria o projeto de console, que aparece em Gerenciador de Soluções no painel direito.
No Program.cs (ou Program.vb), substitua todo o código padrão pelo código a seguir. Primeiro, selecione o separador do idioma correto, C# ou Visual Basic.
using System; using System.Collections.Generic; namespace ConsoleApp_FirstApp { class Program { static void Main(string[] args) { Console.WriteLine("Welcome to Galaxy News!"); IterateThroughList(); Console.ReadKey(); } private static void IterateThroughList() { var theGalaxies = new List<Galaxy> { new Galaxy() { Name="Tadpole", MegaLightYears=400, GalaxyType=new GType('S')}, new Galaxy() { Name="Pinwheel", MegaLightYears=25, GalaxyType=new GType('S')}, new Galaxy() { Name="Cartwheel", MegaLightYears=500, GalaxyType=new GType('L')}, new Galaxy() { Name="Small Magellanic Cloud", MegaLightYears=.2, GalaxyType=new GType('I')}, new Galaxy() { Name="Andromeda", MegaLightYears=3, GalaxyType=new GType('S')}, new Galaxy() { Name="Maffei 1", MegaLightYears=11, GalaxyType=new GType('E')} }; foreach (Galaxy theGalaxy in theGalaxies) { Console.WriteLine(theGalaxy.Name + " " + theGalaxy.MegaLightYears + ", " + theGalaxy.GalaxyType); } // Expected Output: // Tadpole 400, Spiral // Pinwheel 25, Spiral // Cartwheel, 500, Lenticular // Small Magellanic Cloud .2, Irregular // Andromeda 3, Spiral // Maffei 1, 11, Elliptical } } public class Galaxy { public string Name { get; set; } public double MegaLightYears { get; set; } public object GalaxyType { get; set; } } public class GType { public GType(char type) { switch(type) { case 'S': MyGType = Type.Spiral; break; case 'E': MyGType = Type.Elliptical; break; case 'l': MyGType = Type.Irregular; break; case 'L': MyGType = Type.Lenticular; break; default: break; } } public object MyGType { get; set; } private enum Type { Spiral, Elliptical, Irregular, Lenticular} } }
Nossa intenção para este código é exibir o nome da galáxia, a distância até a galáxia e o tipo de galáxia em uma lista. Para depurar, é importante entender a intenção do código. Aqui está o formato de uma linha da lista que queremos mostrar na saída:
nome de galáxia, distância, tipo de galáxia.
Executar o aplicativo
Pressione F5 ou o botão Iniciar Depuração na Barra de Ferramentas de Depuração, localizada acima do editor de código.
A aplicação inicia e não há exceções mostradas pelo depurador. No entanto, a saída que você vê na janela do console não é o esperado. Aqui está a saída esperada:
Tadpole 400, Spiral
Pinwheel 25, Spiral
Cartwheel, 500, Lenticular
Small Magellanic Cloud .2, Irregular
Andromeda 3, Spiral
Maffei 1, Elliptical
Mas, em vez, vês este resultado:
Tadpole 400, ConsoleApp_FirstApp.GType
Pinwheel 25, ConsoleApp_FirstApp.GType
Cartwheel, 500, ConsoleApp_FirstApp.GType
Small Magellanic Cloud .2, ConsoleApp_FirstApp.GType
Andromeda 3, ConsoleApp_FirstApp.GType
Maffei 1, 11, ConsoleApp_FirstApp.GType
Olhando para a saída e para o nosso código, sabemos que GType
é o nome da classe que armazena o tipo de galáxia. Estamos tentando mostrar o tipo de galáxia real (como "Espiral"), não o nome da classe!
Depurar o aplicativo
Com o aplicativo ainda em execução, insira um ponto de interrupção.
No loop de
foreach
, clique com o botão direito do mouse ao lado do métodoConsole.WriteLine
para abrir o menu de contexto e selecione Ponto de interrupção>Inserir Ponto de Interrupção no menu flutuante.foreach (Galaxy theGalaxy in theGalaxies) { Console.WriteLine(theGalaxy.Name + " " + theGalaxy.MegaLightYears + ", " + theGalaxy.GalaxyType); }
Quando você define o ponto de interrupção, um ponto vermelho aparece na margem esquerda.
À medida que você vê um problema na saída, você começa a depuração observando o código anterior que define a saída no depurador.
Selecione o ícone Reiniciar botão na barra de ferramentas de depuração (Ctrl + Shift + F5).
O aplicativo pausa no ponto de interrupção que você definiu. O realce amarelo indica onde o depurador está pausado (a linha amarela do código ainda não foi executada).
Passe o cursor sobre a variável
GalaxyType
, à direita, e, em seguida, à esquerda do ícone da chave inglesa, expandatheGalaxy.GalaxyType
. Você vê queGalaxyType
contém uma propriedadeMyGType
e o valor da propriedade está definido comoSpiral
."Espiral" é realmente o valor correto que você esperava imprimir no console! Portanto, é um bom começo que você possa acessar o valor nesse código enquanto executa o aplicativo. Nesse cenário, estamos usando a API incorreta. Vamos ver se você pode corrigir isso enquanto executa o código no depurador.
No mesmo código, enquanto ainda estás a depurar, coloca o cursor no final do
theGalaxy.GalaxyType
e altera-o paratheGalaxy.GalaxyType.MyGType
. Embora você possa fazer a edição, o editor de código mostra um erro (linha ondulada vermelha). (No Visual Basic, o erro não é mostrado e esta seção de código funciona.)Pressione F11 (Debug>Step Into ou o botão Step Into na Barra de Ferramentas de Depuração) para executar a linha de código atual.
F11 avança o depurador (e executa o código) uma instrução de cada vez. F10 (Step Over) é um comando semelhante, e ambos são úteis ao aprender a usar o depurador.
Quando se tenta avançar o depurador, a janela de diálogo do Hot Reload aparece, indicando que as edições não podem ser compiladas.
A caixa de diálogo Editar e Continuar é exibida, indicando que as edições não podem ser compiladas.
Observação
Para depurar o código de exemplo do Visual Basic, ignore as próximas etapas até ser instruído a clicar no ícone Reiniciar botão.
Selecione Editar na Hot Reload ou Editar e Continuar na caixa de mensagem. Você vê uma mensagem de erro agora na janela Lista de Erros. O erro indica que o
'object'
não contém uma definição paraMyGType
.Apesar de definirmos cada galáxia com um objeto do tipo
GType
(que tem a propriedadeMyGType
), o depurador não reconhece o objetotheGalaxy
como um objeto do tipoGType
. O que é que se passa? Você quer examinar qualquer código que define o tipo de galáxia. Quando fazes isso, vês que a classeGType
definitivamente tem uma propriedade deMyGType
, mas algo não está certo. A mensagem de erro sobreobject
acaba por ser a pista; Para o intérprete de linguagem, o tipo parece ser um objeto do tipoobject
em vez de um objeto do tipoGType
.Olhando através do seu código relacionado com a definição do tipo de galáxia, você encontra a propriedade
GalaxyType
da classeGalaxy
é especificada comoobject
em vez deGType
.public object GalaxyType { get; set; }
Altere o código anterior da seguinte maneira:
public GType GalaxyType { get; set; }
Selecione o ícone Reiniciar botão na barra de ferramentas de depuração (Ctrl + Shift + F5) para recompilar o código e reiniciar.
Agora, quando o depurador faz uma pausa no
Console.WriteLine
, você pode passar o mouse sobretheGalaxy.GalaxyType.MyGType
e ver se o valor está definido corretamente.Remova o ponto de interrupção clicando no círculo do ponto de interrupção na margem esquerda (ou clique com o botão direito do mouse e escolha Ponto de interrupção>Excluir ponto de interrupção) e, em seguida, pressione F5 para continuar.
O aplicativo é executado e exibe a saída. Está parecendo bom, mas você percebe uma coisa. Você esperava que a galáxia Pequena Nuvem de Magalhães aparecesse como uma galáxia irregular na saída do console, mas ela não mostra nenhum tipo de galáxia.
Tadpole 400, Spiral Pinwheel 25, Spiral Cartwheel, 500, Lenticular Small Magellanic Cloud .2, Andromeda 3, Spiral Maffei 1, Elliptical
Defina um ponto de interrupção nessa linha de código antes da instrução
switch
(antes da instruçãoSelect
no Visual Basic).public GType(char type)
Este código é onde o tipo de galáxia está definido, por isso queremos dar uma olhada mais de perto.
Selecione o ícone Reiniciar botão na barra de ferramentas de depuração (Ctrl + Shift + F5) para reiniciar.
O depurador pausa na linha de código onde você define o ponto de interrupção.
Passe o cursor sobre a variável
type
. Você verá um valor deS
(seguindo o código do caractere). Está interessado num valor deI
, já que sabe que esse é um tipo de galáxia irregular.Pressione F5 e passe o mouse sobre a variável
type
novamente. Repita esta etapa até ver um valor deI
na variáveltype
.Agora, pressione F11 (Debug>Step Into).
Pressione F11 até parar na linha de código na instrução
switch
para obter um valor de 'I' (instruçãoSelect
para Visual Basic). Aqui, você vê um problema claro resultante de um erro de digitação. Você esperava que o código avançasse para onde ele defineMyGType
como um tipo de galáxia irregular, mas o depurador ignora esse código completamente e pausa na seçãodefault
da instruçãoswitch
(instruçãoElse
no Visual Basic).Olhando para o código, você vê um erro de digitação na instrução
case 'l'
. Deve sercase 'I'
.Selecione o código
case 'l'
e substitua-o porcase 'I'
.Remova o ponto de interrupção e, em seguida, selecione o botão Reiniciar para reiniciar a aplicação.
Os bugs foram corrigidos agora e você vê a saída que você espera!
Pressione qualquer tecla para concluir o aplicativo.
Resumo
Quando vir um problema, use o depurador e os comandos de etapa como F10 e F11 para localizar a região do código com o problema.
Observação
Se for difícil identificar a região do código onde o problema ocorre, defina um ponto de interrupção no código que é executado antes do problema e, em seguida, utilize comandos de execução passo a passo até que o problema se manifeste. Você também pode usar
Quando você encontrar a região de código com o problema, use o depurador para investigar. Para encontrar a causa de um problema, inspecione o código do problema enquanto executa seu aplicativo no depurador:
Inspecione as variáveis e verifique se elas contêm o tipo de valores que devem conter. Se você encontrar um valor incorreto, descubra onde o valor incorreto foi definido (para encontrar onde o valor foi definido, talvez seja necessário reiniciar o depurador, examinar a pilha de chamadas ou ambos).
Verifique se seu aplicativo está executando o código esperado. (Por exemplo, no aplicativo de exemplo, esperávamos que o código da instrução
switch
definisse o tipo de galáxia como Irregular, mas o aplicativo ignorou o código devido ao erro de digitação.)
Dica
Você usa um depurador para ajudá-lo a encontrar erros. Uma ferramenta de depuração pode encontrar bugs para você apenas se souber a intenção do seu código. Uma ferramenta só pode saber a intenção do seu código se você, o desenvolvedor, expressar essa intenção. Escrever testes de unidade é como você faz isso.
Próximos passos
Neste artigo, aprendeste alguns conceitos gerais de depuração. Em seguida, você pode começar a aprender mais sobre o depurador.