Предисловие

Этот пост написан скорее для меня самого, чем для случайно забредших сюда.

Был однажды случай в моей жизни, когда beam-нода грохалась при выполнении определённого набора действий. При этом понять, в какой точке происходило падение было невозможно, потому что грохалось где-то в глубинах даже не elixir’ового кода, а какой-то erlang-библиотеки. То есть отдебажить “комментированием кода” точно не получилось бы.

Предупреждение

В эрланге я разбираюсь также хорошо, как ES6-писатели в жабаскрипте. То есть примерно никак. Поэтому предостерегаю: в этом посте может содержаться полнейший бред.

Получение трейса

strace

Сначала, конечно, был просмотрен strace на наличие подозрительных операций. Но, как вы понимаете, strace для кода с сильным параллелизмом не выдаст ничего понятного. Куча строк про всякие мьютексы, туда-сюда, треды, вот это вот всё. Ну и я не настолько strace-мастер, чтобы его разбирать руками за доли секунд.

А проблема была при отправке писем, что характерно.

Crashdump viewer

Есть в эрланге Crashdump viewer. Но, цитирую: “The Crashdump Viewer is a WxWidgets based tool”. Ага. Конечно. В шифте, который анально огорожен. Я смогу стартануть WxWidgets-приложение (или выкинуть наружу tcp). Ну-ну.

Эликсировый трейс

Было решено найти способ вытащить трейс вызовов beam-машины (то есть erlang’а).

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

Пришлось искать средства вывода трейса в момент выполнения.

К счастью, в erlang’е для этого средства есть (хоть и весьма своеобразные).

Огромное спасибо автору вот этого гиста. Он спас мне очень много времени копания в документации.

В результате код, который печатает трейс во время выполнения в файл получился примерно такой (elixir, но старался юзать эрланговые функции):

print_trace = fn(print_trace, fd) ->
  receive do
    msg ->
      :io.format(fd, "call: (tracer pid: ~p)~n ~p~n", [self(), msg])
      print_trace.(print_trace, fd)
  end
end

{ok, fd} = :file.open("/tmp/trace.out", [:write])
custom_tracer = :proc_lib.spawn(fn -> print_trace.(print_trace, fd) end)

:erlang.trace(self(), true, [:all, {:tracer, custom_tracer}])

Пояснять код не буду — разбирайтесь сами.

Заключение

Вывод трейса на самом деле ничем не помог — пришлось выпиливать отправку файлов из письма.

Но зато я насмотрелся на то, как со стороны эрланга выглядит elixir. Забавно выглядит, на самом деле.