Статьи

Post-hoc трассировка с использованием отладчика

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

Давайте рассмотрим эту очень тривиальную и неэффективную реализацию функции для возврата n-го числа в последовательности Фибоначчи.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public class Fib {
 
    public long fib(long number) {
        return number < 1 ? 0 :    // Breakpoint here
            number < 2 ? 1 : fib(number - 2) + fib(number - 1);
    }
 
    public static void main(String[] args) {
 
        Fib fib = new Fib();
        System.out.println(fib.fib(10L));
 
    }
 
}

Теперь мы добавляем простую точку останова, я собираюсь немного ее изменить, чтобы она не остановилась; но он будет записывать простое выражение, которое дает нам текущее значение number

Снимок экрана 2014-01-07 в 15.41.37

Это дает следующий вывод для нашего довольно неэффективного кода:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
ebugger connected to local process.
Source breakpoint: Fib.java:11, evaluate(number)=10 (long)
Source breakpoint: Fib.java:11, evaluate(number)=8 (long)
Source breakpoint: Fib.java:11, evaluate(number)=6 (long)
Source breakpoint: Fib.java:11, evaluate(number)=4 (long)
Source breakpoint: Fib.java:11, evaluate(number)=2 (long)
Source breakpoint: Fib.java:11, evaluate(number)=0 (long)
Source breakpoint: Fib.java:11, evaluate(number)=1 (long)
Source breakpoint: Fib.java:11, evaluate(number)=3 (long)
Source breakpoint: Fib.java:11, evaluate(number)=1 (long)
Source breakpoint: Fib.java:11, evaluate(number)=2 (long)
... sometime later
Source breakpoint: Fib.java:11, evaluate(number)=1 (long)
Source breakpoint: Fib.java:11, evaluate(number)=2 (long)
Source breakpoint: Fib.java:11, evaluate(number)=0 (long)
Source breakpoint: Fib.java:11, evaluate(number)=1 (long)
55

Итак, что мы сделали здесь, так это добавили трассировку post-hoc, вы можете даже не иметь доступа к источнику и все же иметь возможность получить полезную информацию о том, что делает код.

Теперь очевидный вывод из приведенного выше примера состоит в том, что мы вычисляем одно и то же значение снова и снова. Итак, вот еще одна версия кода, которая вместо этого использует карту для хранения ранее вычисленных значений в последовательности. Заметьте также, что добавление трассировки в этот код труднее, чем в предыдущем случае, не делая Lambda менее привлекательной.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.HashMap;
import java.util.Map;
 
public class Fib {
 
    Map<Long, Long> data = new HashMap<>();
    {
        data.put(1L, 1L);
    }
 
    public long fib(long number) {
        return data.computeIfAbsent(number,
                      n -> n < 1 ? 0 : fib(n - 2) + fib(n - 1)); // Breakpoint here
    }
 
    public static void main(String[] args) {
 
        Fib fib = new Fib();
        System.out.println(fib.fib(10L));
 
    }
 
}

Здесь стоит отметить две вещи: во-первых, ваше выражение журнала точек останова должно оценивать значение лямбда-параметра n а во-вторых, вы всегда должны помещать свое выражение в новую строку, чтобы будущий разработчик мог легко его установить. (Неважно, теперь намного меньше / красивее / умнее, это выглядело бы, если бы вы поместили код в один).

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

01
02
03
04
05
06
07
08
09
10
11
12
Debugger connected to local process.
Source breakpoint: Fib.java:17, evaluate(n)=10
Source breakpoint: Fib.java:17, evaluate(n)=8
Source breakpoint: Fib.java:17, evaluate(n)=6
Source breakpoint: Fib.java:17, evaluate(n)=4
Source breakpoint: Fib.java:17, evaluate(n)=2
Source breakpoint: Fib.java:17, evaluate(n)=0
Source breakpoint: Fib.java:17, evaluate(n)=3
Source breakpoint: Fib.java:17, evaluate(n)=5
Source breakpoint: Fib.java:17, evaluate(n)=7
Source breakpoint: Fib.java:17, evaluate(n)=9
55

Снимки экрана в этом блоге от Jdeveloper; но аналогичные функции доступны в Intelij , Netbeans, и если вы немного хитры , вы можете получить нечто подобное в Eclipse .