If there’s one programming language that changed my life, that’s Erlang. After using Erlang for couple of years, I “moved” to Elixir, which is based on Erlang’s VM.

One the most important aspects of Erlang’s VM is that it’s a “real” VM, there’s a kernel, processes, messaging facilities and many more.

Lately I’ve been debugging a huge Erlang application whose architecture I was not very familiar with and I needed to find a way to see what kind of messages are being sent and received, which Modules and Functions are being called and what are they returning.

So I wanted to write a small How-To for me and you, in case we need it again in the future.

Okay, for this example I’ll be using Elixir TCP Server, a simple TCP server that gets data and sends it back to its origin.

First, let’s clone the repo.

antranigv@zvartnots:prj $ git clone https://github.com/SonaTigranyan/ElixirTcpServer

Okay, now let’s run the server

antranigv@zvartnots:ElixirTcpServer $ iex -S mix
Erlang/OTP 23 [erts-11.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

Compiling 3 files (.ex)
Generated tcp_server app
Interactive Elixir (1.10.3) - press Ctrl+C to exit (type h() ENTER for help)

Good! By default, the TCP server listens on port 9000, as specified in the Application Tree.

Okay, we can send data now :)

antranigv@zvartnots:ElixirTcpServer $ echo test | nc localhost 9000

Or in an interactive way!

antranigv@zvartnots:ElixirTcpServer $ nc localhost 9000
First mesage!
First mesage!
Good TCP server!
Good TCP server!
antranigv@zvartnots:ElixirTcpServer $

Good! As you can see the connection is closed when the server gets bye.

Okay, say we want to trace the do_send function, observe what does it get and return.

iex(2)> :dbg.start()
{:ok, #PID<0.191.0>}
iex(3)> :dbg.tracer()
{:ok, #PID<0.191.0>}
iex(4)> :dbg.tpl(TcpServer, :do_send, [{:_, [], [{:return_trace}]}])
{:ok, [{:matched, :nonode@nohost, 1}, {:saved, 1}]}
iex(5)> :dbg.p(:new_processes, :c)
{:ok, [{:matched, :nonode@nohost, 0}]}
(<0.198.0>) call 'Elixir.TcpServer':do_send(#Port<0.545>,"Message from client!\n")

Okay, first we start the dbg facility, and then we start a tracing server on the local node.

After that, we use function tpl to specify which local calls we want to trace.

And in the end we use the p function to start tracing the calls (c) of all new_processes :)

Now, when the do_send function is called, we see what it gets.

And when we send bye, we see the following:

(<0.198.0>) returned from 'Elixir.TcpServer':do_send/2 -> ok

And all of this is happening when the software system is running. In production, we can do the same, by either attaching to the node or connecting to it!

That’s all folks! :)