Elixir - Debugging

  1. https://elixir-lang.org/getting-started/debugging.html

using IEx.pry (= binding.pry in Ruby)

  1. http://blog.plataformatec.com.br/2016/04/debugging-techniques-in-elixir-lang/

using IEx.pry in tests

  1. Elixir - Testing

using IEx.pry in deps

see the next section.

debugging dependencies

sometimes it might be necessary to debug external dependencies stored in deps/ directory.

information about endpoint

  1. https://elixirforum.com/t/how-can-i-see-what-port-a-phoenix-app-in-production-is-actually-trying-to-use/5160/5
$ bin/billing remote_console
iex(billing@> :sys.get_state BillingWeb.Endpoint.Server
iex(billing@> :sys.get_state BillingWeb.Endpoint

memory consumption

IO.puts("before foo: " <> inspect(:erlang.memory(:total)))
IO.puts("after foo: " <> inspect(:erlang.memory(:total)))

remote debugging

  1. http://blog.plataformatec.com.br/2016/05/tracing-and-observing-your-remote-node/
  2. https://gist.github.com/pnc/9e957e17d4f9c6c81294
  3. http://erlang.org/doc/apps/observer/observer_ug.html

find out epmd and node ports on remote host

$ ssh foo_host 'epmd -names'
epmd: up and running on port 4369 with data:
name foo at port 99999

here 4369 is epmd port and 99999 is a node port.

forward ports

configure forwarding of corresponding local ports:

$ ssh -N -L 4369:localhost:4369 99999:localhost:99999 foo_host

here all connections are forwarded, say, from localhost:4369 to foo_host:4369 (whenever connection is made to locahost:4369, it’s forwarded to foo_host:4369).

start a hidden node running observer

\iex --name 'debug@localhost' --cookie 'bar' --hidden -e ':observer.start'


make sure magic cookie matches the one from foo/var/vm.args (not the one from automatically generated ~/.erlang.cookie)! otherwise you won’t be able to connect to foo next.

connect to remote node

observer menu: NodesConnect Node (type foo@


make sure node name matches the one from foo/var/vm.args exactly (foo@localhost != foo@

(how to) debug process inside BEAM

  1. https://www.youtube.com/watch?v=pO4_Wlq8JeI&feature=youtu.be&t=853

the higher number of reductions is, the more process loads CPU:

iex> Process.list
  |> Enum.map(& {Process.info(&1, :reductions), &1})
  |> Enum.sort(&>=/2)
  |> Enum.take(5)
  |> Enum.each(&IO.inspect)
iex> pid = pid(0,4225,0)
iex> Process.info(pid, :current_stacktrace)

debug that process with dbg (for some reason Sasa Juric doesn’t recommend it for production):

iex> :dbg.tracer()
iex> :dbg.p(pid, [:call])
iex> :dbg.tpl(:_, []); :timer.sleep(1000); :dbg.stop()

debugging deployment

  1. https://github.com/edeliver/edeliver#help

use --debug option to run in shell debug mode, say:

$ mix edeliver start production --debug

about stacktrace


You should not rely on the ability to get the stacktrace outside of a rescue / catch , it might get removed from the BEAM with any major release.

stacktrace is only guaranteed to have information about the place where error was raised - if stacktrace is obtained manually via Process.info/2, it might not contain information about all calling modules up in the stack (that is it might include some Erlang modules but not user modules).

if, for example, operation returns error tuple instead of raising error and this error tuple is handled somewhere later, this operation might be missing in stacktrace obtained in helper module.

=> if you absolutely need information about the place where error occurred - raise error, you cannot rely on stacktrace obtained manually somewhere later.