A Arte de Depurar (Parte II)

Publicado em: 22 de fevereiro de 2007

Clique aqui para fazer o download do código-fonte deste artigo.

Objetivos:

Este é o segundo artigo da série “A Arte de Depurar”, composta por pequenos tutoriais com nível de dificuldade crescente para explorar o universo da depuração na Plataforma .NET e no Visual Studio .NET. No primeiro artigo (http://www.microsoft.com/brasil/msdn/tecnologias/vs2005/ArteDeDepurar01.mspx), entendemos os fundamentos do processo de depuração, justificando porque, o que e quando depurar, como evitar a má prática do “code-and-fix” e, por fim, desenvolvemos uma aplicação calculadora simples para ilustrar o processo. Neste segundo artigo, será apresentada uma introdução a breakpoints e comandos para navegação passo-a-passo no fluxo de execução de código-fonte que o Visual Studio .NET (VS.NET) oferece para desenvolvedores durante o processo de depuração.

*
Nesta página
Breakpoints, o fundamento básico da depuraçãoBreakpoints, o fundamento básico da depuração
Navegando passo-a-passo pelo fluxo de execução no modo depuraçãoNavegando passo-a-passo pelo fluxo de execução no modo depuração
Inspecionando rapidamente o valor de variáveis na depuraçãoInspecionando rapidamente o valor de variáveis na depuração
Liberando e adentrando no fluxo de execução de códigoLiberando e adentrando no fluxo de execução de código
Comandos de navegação adicionaisComandos de navegação adicionais
Desacoplando o depurador do VS.NET a um programa já em execuçãoDesacoplando o depurador do VS.NET a um programa já em execução
ConclusãoConclusão

Breakpoints, o fundamento básico da depuração

Apenas relembrando, a aplicação calculadora desenvolvida no primeiro artigo é composta por dois projetos:

Uma Class Library (biblioteca de classes, cujo output é uma dll) chamada FuncoesMatematicas, implementada na linguagem VB.NET e contendo apenas uma única classe, chamada OperacoesBasicas. Essa classe, por sua vez, contém um único método, intitulado Dividir, que recebe dois inteiros a serem divididos e retorna um outro inteiro contendo o resultado da divisão.

Um projeto Windows Application, implementado em C# e contendo apenas um único formulário (FrmPrincipal), exibido na Figura 1. Este projeto consome a class library FuncoesMatematicas após processar a entrada do usuário.

Figura 1. Interface principal do projeto Calculadora

 

No artigo passado, você observou que, ao iniciar a aplicação no modo de depuração e entrar com o valor “0” (zero) para o divisor (campo “Número 2”), clicando em seguida no botão “dividir ->”, o VS.NET entrou em foco novamente e exibiu automaticamente a linha que ocasionou o erro, oferecendo alguns links de ajuda, conforme mostra a Figura 2.

Figura 2. Depurador do VS.NET informando fonte do erro

 

Observe a seta amarela à esquerda do código, na barra vertical cinza. Ela indica que o fluxo de execução do código está temporariamente suspenso, exatamente naquela linha. Entretanto, não é necessário esperar um erro acontecer para ter o fluxo de execução do código suspenso. Na verdade, a essência da depuração consiste em definir, você mesmo, quando e onde o fluxo de execução deve ser interrompido. Para isso, é necessário definir breakpoints (ou, literalmente, “pontos de quebra”) no código-fonte.

Um breakpoint sinaliza ao depurador do VS.NET que você deseja suspender o fluxo de execução quando o mesmo atingir uma certa linha do código-fonte. Para adicionar um breakpoint com o VS.NET, basta pressionar a tecla F9 quando o cursor do teclado estiver posicionado na linha desejada, ou então clicar com o mouse na barra vertical cinza à esquerda do código, exatamente na altura correspondendo à linha de código desejada. Uma bolinha vermelha surgirá na barra vertical cinza, como exibido na Figura 3. Para remover um breakpoint, realize exatamente o mesmo procedimento da inserção. Experimente você mesmo adicionar e remover alguns breakpoints no código da classe FrmPrincipal.

Figura 3. Inserindo um breakpoint à classe FrmPrincipal

 

Duas observações interessantes nesse momento: em primeiro lugar, não são todas as linhas de código que permitem a adição de um breakpoint. Por exemplo, se você tentar adicionar um breakpoint na linha em branco logo acima do método btnDividir_Click, o VS.NET exibirá o seguinte erro em sua barra de status (parte inferior da janela da ferramenta): “A breakpoint could not be inserted at this location” (“Um breakpoint não pôde ser inserido nesse local”). O mesmo acontece, por exemplo, nas linhas que possuem apenas comentários. Observe, entretanto, que linhas de código contendo declaração de variáveis ou até mesmo apenas o fechamento de chaves/parênteses estão sujeitas à inserção de breakpoints.

Outra observação diz respeito ao fato de que, quando um breakpoint é inserido em um arquivo, o mesmo não sofre nenhuma alteração física. Por exemplo, insira e remova breakpoints à vontade na classe FrmPrincipal e observe que o arquivo que a contém, FrmPrincipal.cs, não aparece com um asterisco (*) ao seu lado na barra de arquivos abertos do VS.NET, indicando portanto que o arquivo não sofreu alterações físicas. Se você está curioso para saber onde os breakpoints são salvos, a resposta é o arquivo .suo (Solution User Options) que é salvo ocultamente pelo VS.NET no mesmo diretório de sua solução (arquivo .sln).

Embora seja uma constatação simples, esse fato é muito importante para usuários mais avançados, pois arquivos .suo não são submetidos ao controle de versão de código-fonte, permitindo que, em desenvolvimento em times, cada membro da equipe possua seu próprio arquivo .suo e, consequentemente, suas próprias configurações de depuração. Se você não ainda sabe o que é controle de versão de código-fonte, não se preocupe com essa informação adicional e sigamos em frente.

Uma vez inserido o breakpoint, inicie a aplicação no modo depuração (F5 ou menu Debug > Start Debugging). A aplicação será iniciada normalmente, mas quando você clicar no botão de divisão, o fluxo de execução será suspenso como previsto e o VS.NET voltará ao foco, mostrando a seta amarela na barra vertical cinza da esquerda, agora dentro do breakpoint, como exibido na Figura 4. Dizemos então que o breakpoint daquela linha foi “atingido” (“the breakpoint was hit”). Toda a linha de código em que o fluxo de execução está suspenso agora possui um fundo amarelo. Experimente tal processo digitando, por exemplo “8” e “4” nos campos Número 1 e Número 2 no formulário principal da aplicação calculadora (Figura 1), clicando em seguida no botão de divisão.

Figura 4. Breakpoint “atingido” pelo fluxo de execução do código

 

Início da páginaInício da página

Navegando passo-a-passo pelo fluxo de execução no modo depuração

Você agora já sabe como entrar em uma sessão de depuração a partir de breakpoints. Mas o que de interessante podemos fazer aqui? Se você deseja apenas verificar por que caminhos entrará o fluxo de execução do código da aplicação, então o primeiro comando que precisa aprender é o “Step Over” (pressione F10 ou então acesse o menu Debug > Step Over). Este comando permite que você avance, linha por linha, o fluxo de execução da aplicação.

Execute o Step Over repetidas vezes e observe as linhas subseqüentes receberem o foco em amarelo, indicando por onde anda a execução da aplicação. Observe, entretanto, que o corpo da cláusula if do método btnDividir_Click não é executado, pois o valor do divisor não é zero. Pelo contrário, o corpo da cláusula else é executado, como mostra a Figura 5. Execute o comando Step Over até a execução de sua aplicação chegar na linha de código mostrada pela figura.

Figura 5. Executando comandos Step Over

 

Início da páginaInício da página

Inspecionando rapidamente o valor de variáveis na depuração

Coloque o mouse em cima da variável resultado. Nesse momento, o VS.NET exibirá um tooltip (popup informativo) apresentando o valor dessa variável. Observe, como exibido na Figura 6, que o valor de resultado ainda é 0 (zero), enquanto se você realizar o mesmo procedimento para as variáveis n1 e n2 irá observar valores não-default (8 e 2). Isso nos revela, portanto, que a linha de código em amarelo no processo de depuração, na verdade, nos mostra o código que está na iminência de ser executado, porém que ainda não executou. Dessa forma, a variável resultado, embora tenha sido declarada, ainda está com o valor padrão original (default) atribuído inicialmente a variáveis do tipo int: zero.

Figura 6. Disparando tooltips via mouse para inspecionar o valor de variáveis

 

Execute o comando Step Over mais uma vez e inspecione novamente o valor de resultado. Dessa vez, a variável já assumiu o valor esperado resultante da divisão (2), como mostra a Figura 7.

Figura 7. Variável com valor atualizado

 

Um recurso bastante interessante do processo de depuração do VS.NET consiste no fato de que elementos de código mais complexos, como objetos, podem ter os valores de seus atributos e propriedades inspecionados de maneira similar, através de tooltips que podem ser expandidos ou contraídos. Por exemplo, coloque o cursor do mouse logo acima do objeto txtResultado. Observe o sinal de “+” que aparece logo antes do seu nome na janela de tooltip. Clique e observe, conforme a Figura 8, que as propriedades desse objeto também estão disponíveis para rápida visualização. Observe, por fim, que o valor da propriedade Text de txtResultado ainda é a string vazia (“”), pois o código que atribui um valor a essa propriedade ainda não foi executado (embora esteja na iminência de execução, como indicado pelo processo de depuração através do fundo amarelo nessa linha de código).

Figura 8. Tooltips expansíveis

 

Início da páginaInício da página

Liberando e adentrando no fluxo de execução de código

Se você continuar executando Step Over, em um dado momento o fluxo de execução sairá do método btnDividir_Click e o controle retornará à aplicação calculadora, que voltará a receber o foco. Entretanto, você não precisa executar o Step Over contínuas vezes se deseja liberar o fluxo de execução do código de sua aplicação. Para isso, basta executar o comando Continue (tecla F5 ou através do menu Debug > Continue). Uma vez executado o Continue, o fluxo de execução voltará a correr livremente, parando em um próximo breakpoint ou devolvendo o controle à aplicação caso nenhum breakpoint seja encontrado pelo caminho.

É importante observar, entretanto, que mesmo tendo o controle do fluxo de execução voltado à aplicação, o processo de depuração ainda não acabou. Afinal, a qualquer momento o fluxo de execução pode voltar a cair em um novo breakpoint, ou até mesmo em um breakpoint em que já tenha caído anteriormente. Por exemplo, clique novamente no botão de divisão da aplicação calculadora e observe o mesmo processo de depuração da Figura 4 ser iniciado novamente.

Pressionando F10 (Step Over), volte a fazer o fluxo de execução parar na linha indicada na Figura 5, em que o método Dividir da classe OperacoesBasicas (da class library FuncoesMatematicas) é invocado. Dessa vez, não invoque novamente o Step Over. Pressione a tecla F11 para invocar o comando Step Into. Esse comando faz com que o fluxo de execução adentre a chamada de método (ou propriedade) da linha corrente. No nosso caso, o Step Into irá abrir o arquivo OperacoesBasicas.vb e posicionar o cursor exatamente no início da função Dividir, como mostra a Figura 9. Observe que, sem problema algum, o depurador do VS.NET conseguiu atingir e identificar código implementado em VB.NET a partir de código C#. Em outras palavras, a interoperabilidade de linguagens da Plataforma .NET é suportada também pelo processo de depuração.

Figura 9. Código VB.NET acessado via código C# no processo de depuração

 

O sistema de tipos comum da Plataforma .NET também viabiliza que os parâmetros passados em C# consigam ser entendidos claramente em VB.NET. Coloque o cursor do mouse nas variáveis n1 e n2 do código VB.NET para verificar que seus valores (do tipo Integer) correspondem aos valores passados em C# (do tipo int). De fato, o tipo int do C# e o Integer do VB.NET são na verdade o mesmo tipo, intitulado Int32 (inteiro de 32 bits) pelo sistema de tipos comum do .NET.

Observe por fim que nem todo código é passível de Step Into. Por exemplo, a linha de código da Figura 9 que realiza a divisão de n1 por n2 não invoca nenhum código acessível pelo processo de depuração em mais alto-nível (por exemplo, nenhum um método ou propriedade é chamado). Nesses casos, o VS.NET tratará o Step Into como um Step Over, “pulando” para a linha de código seguinte.

Início da páginaInício da página

Comandos de navegação adicionais

Se durante o processo de depuração você houver executado o comando Step Into e, em algum momento, deseja voltar à chamada original responsável por adentrar o fluxo de execução, basta invocar o comando Step Out (SHIFT + F11). No exemplo acima, o comando Step Out fará o depurador abandonar o código do método Dividir (VB.NET) retornando ao código C# que o chamou (Figura 5), justamente na iminência (logo antes) da atribuição de um valor à variável resultado.

Uma outra opção interessante de controle de execução é clicar em qualquer linha de código com o botão direito do mouse e escolher a opção Run To Cursor. Isso fará com que a aplicação continue a ser executada até atingir a linha de código em questão.

Finalmente, outro comando de depuração um pouco mais avançado e menos utilizado é o Set Next Statement. Ao clicar com o botão direito do mouse em uma certa linha de código e escolher essa opção, a execução pulará para a linha selecionada. Entretanto, as linhas compreendidas entre a linha de execução corrente e a linha selecionada para o Set Next Statement não são executadas, o que pode causar efeitos indesejáveis à sua aplicação (como a não-atribuição de um valor a uma variável, por exemplo).

Com um breakpoint na linha que declara a variável n1, exatamente como mostra a Figura 5, experimente esses dois últimos comandos (Run to Cursor e Set Next Statement) e perceba a diferença. Quando o breakpoint for atingido, clique com o botão direito do mouse na linha que atribui um valor à propriedade Text da variável resultado (resultado.Text) e depois clique em Run to Cursor. Em seguida, libere o fluxo de execução (F5) e observe que o campo de texto do resultado no formulário aparece com o valor esperado (o resultado da divisão).

Repita o procedimento, dessa vez clicando não em Run to Cursor mas em Set Next Statement. Nesse caso, a variável resultado receberá o valor zero, pois embora ela tenha sido declarada (o que é feito automaticamente pela execução do código mesmo que a linha de declaração seja ignorada) seu valor default (zero) não é alterado. Isso acontece porque a linha que invoca o método Dividir e atribui seu retorno à variável resultado é ignorada (“pulada”) pelo processo de depuração, como conseqüência do comando Set Next Statement.

Início da páginaInício da página

Desacoplando o depurador do VS.NET a um programa já em execução

Uma vez que você iniciou a execução de sua aplicação no modo depuração, ainda assim é possível interromper o modo de depuração sem interromper a aplicação. Em outras palavras, o depurador do VS.NET pode ser “desacoplado” (detached) da aplicação, permitindo sua normal execução sem mais interferências. Para isso, uma vez no modo depuração, execute o comando do menu Debug > Detach All. O comando voltará à aplicação e breakpoints não serão mais atingidos. O processo de depuração está encerrado.

Da mesma maneira, é possível acoplar o depurador a uma aplicação que não esteja executando no modo de depuração. Para isso, execute o comando Debug > Attach to Process. Uma lista de processos da sua máquina será exibida, como mostra a Figura 10.

Figura 10. Escolhendo um processo a ser acoplado ao depurador do VS.NET

 

Escolha o processo correspondente à aplicação desejada e clique no botão Attach. O modo de depuração será ativado e os breakpoints no VS.NET agora poderão ser atingidos novamente. Caso você tente acoplar o depurador a um processo que não seja suportado, seus breakpoints ficarão marcados como “inatingíveis”, como mostra a Figura 11.

Figura 11. Processo de acoplamento mal-sucedido: breakpoints inatingíveis

 

O uso do acoplamento e desacoplamento dinâmico pode ser interessante em um cenário em que você, por algum motivo, deseja executar o processo de depuração apenas sob demanda. Suponha, por exemplo, que você observou que o processo de depuração deixa lenta a execução da aplicação. Você pode então iniciar a execução de sua aplicação normalmente, sem o modo de depuração, para ganhar em performance. Mais tarde, será possível acoplar o depurador dinamicamente, apenas quando sua interação com a aplicação atingir um momento que realmente demande o processo de depuração.

Início da páginaInício da página

Conclusão

Neste artigo, entendemos o conceito de breakpoint e investigamos algumas funcionalidades do VS.NET para navegar pelo código em depuração, incluindo a inspeção rápida do valor de variáveis e objetos em geral. Adicionalmente, entendemos como acoplar e desacoplar o depurador a uma aplicação em execução. Ao aprender tais recursos, você já está apto a ir um pouco além do processo básico de depuração, aumentando suas chances de inspecionar de maneira mais produtiva seu código-fonte e de identificar erros mais facilmente.

Sobre o autor: André Furtado é engenheiro de software pelo Centro de Inovação Microsoft de Recife, bacharel e mestre em Ciência da Computação pela UFPE, Microsoft Student Partner, Certified MSF Practitioner, MCP, Certified IBM-DB2 Specialist, SCJP 1.4, campeão mundial / nacional da competição Imagine Cup 2005 e um dos líderes do grupo de usuários Sharp Shooters .NET.


Início da páginaInício da página