Получение stacktrace'а в elixir'е
Предисловие
Этот пост написан скорее для меня самого, чем для случайно забредших сюда.
Был однажды случай в моей жизни, когда 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. Забавно выглядит, на самом деле.