Eclipse Debug: Conheça os recursos de Debug do Eclipse – Parte 2

Veja nesse artigo como funciona o Debugging avançado de aplicações na IDE Eclipse. Será mostrado alguns recursos avançados dessa poderosa ferramenta de Debug.

Artigo no estilo: Curso

Fique por dentro
Esse artigo demonstra, de maneira avançada, como utilizar as ferramentas que estão disponíveis no IDE Eclipse para auxiliar desenvolvedores a encontrar erros de código em qualquer programa, sendo, portanto, bastante útil para quem quiser aumentar seus conhecimentos em debugging.

Na primeira parte deste artigo tivemos um contato inicial com debugging de programas no Eclipse.

Introduzimos o método de identificação, isolamento e correção de erros, mostramos como utilizar as funcionalidades padrão de depuração do projeto JDT, incluído no Eclipse, como alterar para a perspectiva de debug, adicionar breakpoints, controlar a execução passo a passo e alterar o valor de variáveis, sempre acompanhados de exemplos práticos para cada caso.

Nesta segunda parte do artigo, vamos nos aprofundar um pouco mais sobre este tema, aprendendo a utilizar novas técnicas e funcionalidades de debugging, mais complexas e poderosas, permitindo acelerar o processo de identificação e remoção de bugs de programas.

Vamos falar sobre breakpoints condicionais, watchpoints, e threads, incluindo um exemplo de debugging de uma aplicação com problema de deadlock.

Breakpoints condicionais

Quando encontramos um erro em nosso programa, vamos querer saber o que o programa está fazendo pouco antes de gerar esse erro. Uma maneira de fazer isso é passar por todas as declarações do código, uma de cada vez, utilizando o debugger, até chegar ao ponto específico onde o erro acontece.

No entanto, se pensarmos, por exemplo, que podemos estar no meio de um ciclo de mais de mil, ou um milhão de execuções, então esta não será, definitivamente, uma boa ideia. Queremos chegar imediatamente na zona do erro, no momento em que ele está prestes a acontecer.

Como vimos no artigo anterior, podemos saber em que parte do código ele se encontra olhando para o stack trace no log ou janela de output, mas ainda temos que descobrir em que momento ele ocorreu, ou seja, que variáveis o programa estava tratando quando se deu o erro.

No caso de ciclos de execução, para chegar no local pretendido do código e no momento correto, é possível alterar os valores das variáveis de controle, como vimos na primeira parte deste artigo.

Mas nem sempre estaremos dentro de um ciclo, ou nem sempre saberemos qual o valor exato de uma iteração em que ocorre o erro.

Nestes casos, podemos utilizar outra opção: declarar pontos de interrupção (breakpoints) condicionais, que são acionados sempre que o valor de uma expressão mudar.

Para demonstrar este recurso, foi construído um programa, baseado nos tutorias de Java da Oracle, que faz com que dois “amigos” se cumprimentem.

Vamos tentar simplificar o máximo possível este exemplo para podermos nos focar nos ensinamentos de debugging e não no código em si.

Neste programa, existe uma classe Friend, que cria um objeto que tem apenas o campo name, com o nome do amigo.

Esta mesma classe possui dois métodos: o bow(), para cumprimentar um amigo, e o bowBack(), para cumprimentar um amigo de volta. Possui também o método main(), que constrói um array de amigos e cria uma thread para cada par de amigos se cumprimentar. Chamar os métodos em threads distintas vai ser importante para aprendermos mais detalhes interessantes sobre o debugger. O código do nosso exemplo é demonstrado na Listagem 1.

Listagem 1. Código do exemplo Deadlock. public class Deadlock { static class Friend { private static int instanceCounter = 0; private int instanceNumber = 0; private final String name; public Friend(String name) { this.name = name; instanceNumber = ++instanceCounter; } public String getName() { return this.name; } public synchronized void bow(Friend bower) { System.out.format("%d: %s cumprimentou %s!%n", instanceNumber, this.name, bower.getName()); bower.bowBack(this); } public synchronized void bowBack(Friend bower) { System.out.format("%d: %s cumprimentou %s de volta!%n", instanceNumber, this.name, bower.getName()); } } public static void main(String[] args) { final Friend[] friends = { new Friend("Alberto"), new Friend("Roberto"), new Friend("Chico"), new Friend("Anysio"), new Friend("Didi"), new Friend("Dedé"), new Friend("João"), new Friend("Maria"), new Friend("Eduardo"), new Friend("Mônica"), new Friend("Leo"), new Friend("Bia") }; for (int i = 1; i <= friends.length; i += 2) { final Friend firstFriend = friends[i - 1]; final Friend secondFriend = friends[i]; new Thread(new Runnable() { public void run() { firstFriend.bow(secondFriend); } }).start(); } } }"

[...] continue lendo...

Artigos relacionados