∴ikura

Farewell, io:format

Well, it’s a dark day indeed. I’m saying goodbye to
Erlang’s family of io:format/n routines. We’ve had
a good long run, and I’m sad that I may never
‘print-to-screen’ again.

Looking back, it was pretty nice having io:format/n
around. We had some good times, for sure. Heck, it got me
out of many jams & pickles; how can I ever say thanks
enough.

The bugs ahead

It’s hard to imagine turning one’s back on such a mainstay
routine as ‘print-to-screen.’ But after an afternoon with
the Erlang runtime-tool ‘dbg,’ it’s unlikely one would ever
want to debug OTP code (and systems) any other way.

So, here’s the scoop: any place you would like to
peer into your Erlang code and see what it’s up to,
instead of putting some ‘print-to-screen’ lines in your
source-code, employ ‘dbg’ instead.

Want a ‘dbg’ ice-breaker? Then read ahead!

Holy moly, ‘dbg’

I’ve used ‘redbug’ in the past to debug production
systems, and have read Fred’s treatise in support of
his ‘recon’ libraries (which do sound fantastic).
But nothing feels better than using those low-level tools
Erlang provides right out of the box. With that, how about
we write some code and take ‘dbg’ for a spin?

Let’s create a dummy Erlang module, compile it &
then debug it using ‘dbg’ in the Erlang shell. Here’s
a module called ‘foo.erl’ :

You can compile this single module with a quick
erlc foo.erl on the command-line. No need for
anything fancier here!

Now, let’s fire up an Erlang shell and play around
with ‘foo’ alongside ‘dbg.’

erl -sname debug

Assuming all went well, you should be greeted with
the typical Erlang banner. We can fire up ‘dbg’
right after we load & test our compiled code
in the shell.

1> l(foo).
2> A = [9.0, 9, 2, -9].
3> foo:manydiv(A, 2).

Oops. That last line throws an exception. Time for ‘dbg.’
Let’s trace this function to see what’s going on…

The following is a typical way of getting ‘dbg’ started
succinctly & safely. Others may have their pet way of doing
it, but this works well for me:

4> dbg:stop_clear().
5> dbg:tracer(), dbg:p(all, c).

Let’s pause and quickly look at what’s happening. On
line four, we make sure to start a trace with a clean slate;
this will annihilate any other ‘dbg’ sessions which may be running (a
great habit to get into).

On line five, we start a tracer, and tell said tracer to
consider calls to all processes. Both tracer and p
can take oodles of options (as you’ll quickly see by
looking in the ‘dbg’ manual), but we don’t need anything
fancy for what we’re up to.

Now, to get some information on our buggy routine, ‘manydiv,’
we’ll want to observe inputs & return values at very least.
We do this the following way:

6> dbg:tpl(foo, manydiv, x).

Again, the manual will give inquiring minds more information
on what’s happening behind the scenes. But from a high level,
we are now tracing the ‘foo:manydiv/2’ routine!

Let’s debug

With our function now being traced, let’s run our code again:

7> foo:manydiv(A, 2).

There. Now we are able to observe the arguments passed to the
routine, and what was returned, all thanks to tracing. So, we are
passing the list as expected; and the number ‘2’ as the second
argument. And we are getting an {error,function_clause} exception
on line ten.

Looking at the source-code on line ten, we spot the problem.
Our list comprehension is using the wrong variable! It should be
iterating over the list, not the second argument. With our editor,
we make the fix as follows:

...
manydiv(L, N) when is_list(L) ->
[ K / N || K <- L ].
...

Let’s stay in the Erlang shell to compile this time. Running this all
again: did we fix the bug?

8> c(foo).
9> dbg:tpl(foo, manydiv, x).
10> foo:manydiv(A, 2).

There, that’s playing nice, finally. You can see that both the
function & tracer are returning the expected value now. Bug fixed!

Before we adjourn this exercise, let’s try to break our routine again,
this time by dividing by zero:

11> foo:manydiv(A, 0).

With this, the new exception is {error,badarith}. To fix this bug,
We can make a small adjustment to the code by adding another clause
to manydiv/2 :

It’s debatable whether this is defensive programming; in fact
I would vote to keep this new clause out of a real code-base.
But, just so we can play with ‘dbg’ some more, let’s go with it.

Again, we compile the code, and try it out:

12> c(foo).
13> dbg:tpl(foo, manydiv, x).
14> foo:manydiv(A, 0).

Great! Everything looks correct. Let’s now drop the tracing of
this routine, as there’s no more debugging needed to be done:

15> dbg:ctpl(foo, manydiv).

And of course, when your session is over, go ahead and stop
the tool entirely via:

16> dbg:stop_clear().

A pair of stop_clear/0 routines should be the bookends for your
debugging session, generally. Especially in production!

Wrapping up

So, there you have it. The world’s coolest companion for a
functional language, ‘dbg.’ This is just the nickel tour, but
it should whet your appetite for more. And honestly, after
getting these few runtime commands under your belt, you just
may not need to ‘print-to-screen’ any longer.

Give ‘dbg’ a try & fall in love with Erlang all over again!

Update: Sep. 2016

It is not possible to trace on what are called ‘guard bifs,’ i.e.
functions in the ‘erlang’ module that can be used in guards.