«1. Обзор

В этом руководстве мы рассмотрим некоторые расширенные средства отладки IntelliJ.

Предполагается, что основы отладки уже известны (как начать отладку, действия Step Into, Step Over и т. д.). Если нет, пожалуйста, обратитесь к этой статье для получения более подробной информации об этом.

2. Умный шаг в

Бывают ситуации, когда в одной строке исходного кода вызывается несколько методов, например doJob(getArg1(), getArg2()). Если мы вызываем Step Into action (F7), отладчик переходит к методам в порядке, используемом JVM для оценки: getArg1 — getArg2 — doJob.

Однако мы можем захотеть пропустить все промежуточные вызовы и сразу перейти к целевому методу. Smart Step In action позволяет это сделать.

По умолчанию он привязан к Shift + F7 и при вызове выглядит так:

Теперь мы можем выбрать целевой метод для продолжения. Также обратите внимание, что IntelliJ всегда помещает самый внешний метод в начало списка. Это означает, что мы можем быстро перейти к нему, нажав Shift + F7 | Входить.

3. Удаление кадров

Мы можем понять, что некоторая интересующая нас обработка уже произошла (например, вычисление аргумента текущего метода). В этом случае можно удалить текущие кадры стека JVM, чтобы повторно обработать их.

Рассмотрим следующую ситуацию:

Предположим, что мы заинтересованы в отладке обработки getArg1, поэтому мы отбрасываем текущий кадр (метод doJob):

Теперь мы находимся в предыдущем методе:

Однако Аргументы вызова к этому моменту уже вычислены, поэтому нам также нужно отбросить текущий кадр:

Теперь мы можем перезапустить обработку, вызвав Step Into.

4. Точки останова полей

Иногда не приватные поля модифицируются другими классами не через сеттеры, а напрямую (как в случае со сторонними библиотеками, где мы не контролируем исходный код).

В таких ситуациях может быть трудно понять, когда модификация завершена. IntelliJ позволяет создавать точки останова на уровне полей, чтобы отслеживать это.

Устанавливаются как обычно — левый клик по левой канавке редактора на строке поля. После этого можно открыть свойства точки останова (щелкнуть правой кнопкой мыши по отметке точки останова) и настроить, интересуют ли нас операции чтения, записи или и того, и другого:

5. Регистрация точек останова

Иногда мы знаем, что есть является состоянием гонки в приложении, но не знаю, где именно оно находится. Это может быть проблемой, особенно при работе с новым кодом.

Мы можем добавить операторы отладки в исходники нашей программы. Однако для сторонних библиотек такой возможности нет.

Здесь может помочь IDE — она позволяет устанавливать точки останова, которые не блокируют выполнение после попадания, а вместо этого создают операторы регистрации.

Рассмотрим следующий пример:

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;
}

Предположим, что мы заинтересованы в регистрации фактических параметров вызова isInterested.

Давайте создадим неблокирующую точку останова в целевом методе (Shift + щелчок левой кнопкой мыши по левой полосе редактора). После этого давайте откроем его свойства (щелкните правой кнопкой мыши точку останова) и определим целевое выражение для логирования:

При запуске приложения (обратите внимание, что все еще необходимо использовать режим отладки) мы увидим вывод: ~~ ~

isInterested(1)
isInterested(4)
isInterested(3)
isInterested(1)
isInterested(6)
Found 2 interested values

6. Условные точки останова

У нас может возникнуть ситуация, когда конкретный метод вызывается из нескольких потоков одновременно, и нам нужно отладить обработку только для определенного аргумента.

IntelliJ позволяет создавать точки останова, которые приостанавливают выполнение, только если выполняется определенное пользователем условие.

Вот пример, в котором используется приведенный выше исходный код:

Теперь отладчик остановится на точке останова, только если заданный аргумент больше 3.

7. Метки объектов

Это самый мощный и наименее известная функция IntelliJ. По сути, это довольно просто — мы можем прикреплять собственные метки к объектам JVM.

Давайте посмотрим на приложение, которое мы будем использовать для их демонстрации:

public class Test {

    public static void main(String[] args) {
        Collection<Task> tasks = Arrays.asList(new Task(), new Task());
        tasks.forEach(task -> new Thread(task).start());
    }

    private static void mayBeAdd(Collection<Integer> holder) {
        int i = ThreadLocalRandom.current().nextInt(10);
        if (i % 3 == 0) {
            holder.add(i);
        }
    }

    private static class Task implements Runnable {

        private final Collection<Integer> holder = new ArrayList<>();

        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                mayBeAdd(holder);
            }
        }
    }
}

7.1. Создание меток

«Объект может быть помечен, когда приложение остановлено в точке останова и цель доступна из кадров стека.

Выберите его, нажмите F11 (действие «Отметить объект») и укажите имя цели:

7.2. Просмотр меток

Теперь мы можем видеть метки наших пользовательских объектов даже в других частях приложения:

Круто то, что даже если помеченный объект недоступен из фреймов стека в данный момент, мы все равно можем видеть его состояние — откройте диалоговое окно Evaluate Expression или добавьте новые часы и начните вводить название метки.

IntelliJ предлагает дополнить его суффиксом _DebugLabel:

Когда мы оцениваем его, отображается состояние целевого объекта:

7.3. Метки как условия

Также можно использовать метки в условиях точки останова:

8. Заключение

Мы проверили ряд приемов, значительно повышающих производительность при отладке многопоточного приложения.

Обычно это сложная задача, и мы не можем недооценивать важность помощи инструментов здесь.