Fork me on GitHub

Отладка

Это перевод актуальной версии оригинального урока.

Ошибки – неотъемлемая часть любого проекта, поэтому отладка очень важна. В этом уроке мы узнаем об отладке Elixir-кода и инструментах статического анализа, позволяющих находить потенциальные проблемы.

Содержание

Dialyxir и Dialyzer

DialyzerDIscrepancy AnalYZer for ERlang programs – инструмент статического анализа кода. Его задача – не выполняя сам программный код, проанализировать его на наличие ошибок, лишних выражений, недостижимых участков и т.д.

Dialyxir – это mix-команда, облегчающая использование Dialyzer в Elixir.

Спецификации позволяют таким инструментам как Dialyzer лучше понимать код. В отличие от документации, которая может быть прочитана только живыми людьми (если она, конечно, существует и хорошо написана), @spec использует более формальный, понятный компьютеру синтаксис.

Давайте добавим Dialixyr в наш проект. Проще всего сделать это, добавив зависимость в mix.exs:

defp deps do
  [{:dialyxir, "~> 0.4", only: [:dev]}]
end

Теперь выполним:

$ mix deps.get
...
$ mix deps.compile

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

Последним шагом мы запустим Dialyxir, чтобы он построил PLT (Персистентную Таблицу Поиска). Это придется делать после установки каждой новой версии Erlang или Elixir. К счастью, Dialyzer не будет пытаться анализировать стандартную библиотеку при каждом использовании. На загрузку потребуется несколько минут.

$ mix dialyzer --plt
Starting PLT Core Build ... this will take awhile
dialyzer --build_plt --output_plt /.dialyxir_core_18_1.3.2.plt --apps erts kernel stdlib crypto public_key -r /Elixir/lib/elixir/../eex/ebin /Elixir/lib/elixir/../elixir/ebin /Elixir/lib/elixir/../ex_unit/ebin /Elixir/lib/elixir/../iex/ebin /Elixir/lib/elixir/../logger/ebin /Elixir/lib/elixir/../mix/ebin
  Creating PLT /.dialyxir_core_18_1.3.2.plt ...
...
 done in 5m14.67s
done (warnings were emitted)

Статический анализ кода

Все готово к использованию Dialyxir:

$ mix dialyzer
...
examples.ex:3: Invalid type specification for function 'Elixir.Examples':sum_times/1. The success typing is (_) -> number()
...

Сообщение от Dialyzer понятно: тип возвращаемого значения нашей функции sum_times/1 отличается от объявленного. Причина в том, что Enum.sum/1 возвращает число типа number, а не integer, но при этом заявлено, что sum_times/1 возвращает integer.

Поскольку number это не integer мы получаем ошибку. Как её исправить? Нужно использовать функцию round/1, чтобы превратить число типа number в integer:

@spec sum_times(integer) :: integer
def sum_times(a) do
    [1, 2, 3]
    |> Enum.map(fn el -> el * a end)
    |> Enum.sum
    |> round
end

И теперь:

$ mix dialyzer
...
  Proceeding with analysis... done in 0m0.95s
done (passed successfully)

Использование спецификаций вместе с инструментами статического анализа позволяет писать код, тестирующий сам себя и содержащий меньше ошибок.

Отладка

Иногда статического анализа кода не достаточно. Часто, чтобы найти ошибки, нужно понять логику выполнения программы. Самый простой способ – это добавить в наш код выражения вывода, такие как IO.puts/2, чтобы отследить значения переменных и ход выполнения. Однако он примитивен и обладает рядом ограничений. К счастью для нас, мы можем использовать отладчик Erlang для работы с Elixir-кодом.

Взглянем на простейший модуль:

defmodule Example do

  def cpu_burns(a, b, c) do
    x = a * 2
    y = b * 3
    z = c * 5

    x + y + z
  end

end

Теперь запустим iex:

$ iex -S mix

И отладчик:

iex > :debugger.start()
{:ok, #PID<0.307.0>}

Модуль :debugger отвечает за доступ к отладчику. Функция start/1 позволяет настраивать его:

Следующим шагом подключим наш модуль к отладчику:

iex > :int.ni(Example)
{:module, Example}

Модуль :int – это интерпретатор, дающий возможность ставить точки останова и пошагово выполнять код программы.

Когда вы запустите отладчик вы увидите примерно такое окно:

Скриншот отладчика 1

После того, как мы подключили наш модуль к отладчику, он появится в меню слева:

Скриншот отладчика 2

Точки останова

Точка останова – это точка в коде, где выполнение программы будет остановлено. Есть два способа поставить точку останова:

Попробуем поставить точку останова в IEx:

iex > :int.break(Example, 8)
:ok

Точка установлена в строке 8 модуля Example. Теперь, когда мы вызовем нашу функцию:

iex > Example.cpu_burns(1, 1, 1)

Выполнение поставится на паузу и окно отладчика будет выглядеть следующим образом:

Скриншот отладчика 3

Также появится дополнительное окно с нашим исходным кодом:

Скриншот отладчика 4

В этом окне можно просмотреть значения переменных, выполнить следующую строку кода или произвольное выражение. Функция :int.disable_break/2 позволяет деактивировать точку останова:

iex > :int.disable_break(Example, 8)
:ok

Реактивировать точку останова можно вызовом функции :int.enable_break/2. Удалить точку совсем можно так:

iex > :int.delete_break(Example, 8)
:ok

Все те же операции доступны в окне отладчика. В верхнем меню Break при помощи Line Break можно поставить точку останова. Если мы выберем строку, не содержащую кода, точка останова будет проигнорирована интерпретатором, но отобразится в окне отладчика. Есть три типа точек останова:

Вот и всё! Счастливой отладки!


Contributors

loading...



Поделиться