Truques de depuração do IntelliJ
1. Visão geral
Neste tutorial, examinaremos algunsadvanced IntelliJ debugging facilities.
Presume-se que os fundamentos da depuração já sejam conhecidos (como iniciar a depuração,Step Into, açõesStep Over etc.). Se não, por favor consultethis article para mais detalhes sobre isso.
2. Smart Step Into
Existem situações em que vários métodos são chamados em uma única linha de código-fonte, comodoJob(getArg1(), getArg2()). Se chamarmos a açãoStep Into (F7), o depurador vai para os métodos na ordem usada pela JVM para avaliação:getArg1 -getArg2 -doJob.
No entanto,we might want to skip all intermediate invocations and proceed to the target method directly. A açãoSmart Step Into permite fazer isso.
Ébound to the Shift + F7 by defaulte se parece com isto quando invocado:
Agora podemos escolher o método de destino para continuar. Além disso, observe que o IntelliJ sempre coloca o método mais externo no topo da lista. Isso significa que podemos acessá-lo rapidamente pressionandoShift + F7 | Enter.
3. Drop Frame
Podemos perceber que algum processamento no qual estamos interessados já aconteceu (por exemplo, cálculo do argumento do método atual). Neste caso,it’s possible to drop the current JVM stack frame(s) in order to re-process them.
Considere a seguinte situação:
Suponha que estejamos interessados em depurar o processamento degetArg1, então descartamos o quadro atual (métododoJob):
Agorawe’re in the previous method:
No entanto, os argumentos da chamada já estão calculados neste ponto, então,we need to drop the current frame as well:
Agora podemos executar novamente o processamento chamandoStep Into.
4. Pontos de Interrupção de Campo
Às vezes, campos não privados são modificados por outras classes, não por meio de setters, mas diretamente (esse é o caso com bibliotecas de terceiros, onde não controlamos o código-fonte).
Em tais situações, pode ser difícil entender quando a modificação é feita. O IntelliJ permite criar pontos de interrupção no nível do campo para rastrear isso.
Eles são definidos como de costume - clique com o botão esquerdo do mouse na calha esquerda do editor na linha de campo. Depois disso, é possívelopen breakpoint properties (right-click on the breakpoint mark) and configure if we’re interested in the field’s reads, writes, or both:
5. Logging Breakpoints
Às vezes, sabemos que há uma condição de corrida no aplicativo, mas não sabemos onde exatamente está. Pode ser um desafio, especialmente ao trabalhar com um novo código.
Podemos adicionar instruções de depuração às fontes do nosso programa. No entanto, não existe essa capacidade para bibliotecas de terceiros.
O IDE pode ajudar aqui -it allows setting breakpoints that don’t block execution once hit, but produce logging statements instead.
Considere o seguinte exemplo:
public static void main(String[] args) {
ThreadLocalRandom random = ThreadLocalRandom.current();
int count = 0;
for (int i = 0; i < 5; i++) {
if (isInterested(random.nextInt(10))) {
count++;
}
}
System.out.printf("Found %d interested values%n", count);
}
private static boolean isInterested(int i) {
return i % 2 == 0;
}
Suponha que estejamos interessados em registrar os parâmetros reais da chamadaisInterested.
Vamos criar um ponto de interrupção sem bloqueio no método de destino (Shift + clique com o botão esquerdo do mouse na calha do editor à esquerda). Depois disso, vamos abrir suas propriedades (clique com o botão direito no ponto de interrupção) edefine the target expression to log:
Ao executar o aplicativo (observe que ainda é necessário usar o modo de depuração), veremos o resultado:
isInterested(1)
isInterested(4)
isInterested(3)
isInterested(1)
isInterested(6)
Found 2 interested values
6. Pontos de interrupção condicionais
Podemos ter uma situação em que um método específico é chamado de vários threads simultaneamente e precisamos depurar o processamento apenas para um argumento específico.
IntelliJ permitecreating breakpoints that pause the execution only if a user-defined condition is satisfied.
Aqui está um exemplo que usa o código-fonte acima:
Agora, o depurador irá parar no ponto de interrupção apenas se o argumento fornecido for maior que 3.
7. Marcas de objeto
Esse é o recurso IntelliJ mais poderoso e menos conhecido. É bastante simples em sua essência -we can attach custom labels to JVM objects.
Vamos dar uma olhada em um aplicativo que usaremos para demonstrá-los:
public class Test {
public static void main(String[] args) {
Collection tasks = Arrays.asList(new Task(), new Task());
tasks.forEach(task -> new Thread(task).start());
}
private static void mayBeAdd(Collection holder) {
int i = ThreadLocalRandom.current().nextInt(10);
if (i % 3 == 0) {
holder.add(i);
}
}
private static class Task implements Runnable {
private final Collection holder = new ArrayList<>();
@Override
public void run() {
for (int i = 0; i < 20; i++) {
mayBeAdd(holder);
}
}
}
}
7.1. Criação de marcas
Um objeto pode ser marcado quando um aplicativo é interrompido em um ponto de interrupção e o destino é acessível a partir de frames de pilha.
Selecione-o, pressioneF11 (açãoMark Object) e defina o nome do alvo:
7.2. Ver Marcas
Agora podemos ver nossos rótulos de objetos personalizados, mesmo em outras partes do aplicativo:
O legal é queeven if a marked object is not reachable from stack frames at the moment, we can still see its state - abre uma caixa de diálogoEvaluate Expression ou adicione um novo relógio e comece a digitar o nome da marca.
IntelliJ se oferece para completá-lo com o sufixo_DebugLabel:
Quando o avaliamos, o estado do objeto alvo é mostrado:
8. Conclusão
Verificamos várias técnicas que aumentam muito a produtividade durante a depuração de um aplicativo multithread.
Essa geralmente é uma tarefa desafiadora e não podemos subestimar a importância da ajuda do ferramental aqui.