Using Intellij IDEA to write and debug Erlang code


February 2015.
Image Image
Written 2015-02-02.

Requirements



My system is a Windows 8.1 (6.3 build 9600).

Download IntelliJ IDEA from JetBrains (I use the Community Edition, currently ideaIC-14.0.3.exe).

My installed erlang is:

Erlang/OTP 17 [erts-6.2] [64-bit] [smp:8:8] [async-threads:10]

Start IDEA, and go the plugin page (click "Configure"). Install the very nice Erlang plugin (currently ver 0.5.9 (2014-11-18)).

Image

Install rebar somewhere on your system (clone the repository and run bootstrap.bat with the erlang binaries in your PATH).

Configure the IDE



In the IDE's settings window, go to "Other Settings / Erlang External Tools" and input the location of your rebar binaries.

Image

In "Build, Execution, Deployment/Erlan Compiler", check "Compile project with rebar".

Image

Create a new project, and select Erlang. Leave "Additional Libraries and Frameworks" empty and click Next.

Click on "Configure" and input the location of your Erlang SDK (exemple: "C:\Program Files\erl6.2").

If you did well, the SDK is recognized correctly.

Image

Debugging a simple file



Let's create a simple Erlang code and try to debug it.

Add a file, input some code, and click "Build / Make".

If your code is incorrect, the list of warnings and errors appears, and you can double click on the items to go directly to the source of error (like in any good IDE).

Image

If your code is correct, the project is compiled.

A simple default debug configuration is created. You can put a breakpoint in your code and debug it.

Image

Image

So far, so good.

Debugging standard OTP applications



Now let's try to debug something more serious, like an OTP application.

rebarcreate-app appid=testerlangide
==> testerlangide (create-app)
Writing src/testerlangide.app.src
Writing src/testerlangide_app.erl
Writing src/testerlangide_sup.erl

Let's create a gen_server which prints something every 5 seconds

...

-define(DELAY_BETWEEN_LOOKUPS, 5000).

-record(state, {timer}).

init([]) ->
Timer = erlang:send_after(?DELAY_BETWEEN_LOOKUPS, self(), timer_ticked),
{ok, #state{timer=Timer}}.

...

handle_info(timer_ticked, #state{timer=OldTimer}=State) ->
erlang:cancel_timer(OldTimer),
io:format("Tick!~n"),
Timer = erlang:send_after(?DELAY_BETWEEN_LOOKUPS, self(), timer_ticked),
{noreply, State#state{timer=Timer}};

...

I've tried debugging the application directly, by having the debugger directly start the application (application:start(testerlangide)), but it didn't work well:

What I ended up doing was the creation of a "proxy" file, that starts the app, and does nothing until the node is stopped. That way, the breakpoints in the gen_servers work, and the node can be stopped by clicking on the red square button in the IDE.


-export([debug/0]).

loop_sleep() ->
timer:sleep(5000),
loop_sleep().

debug() ->
%% Start your dependencies here
application:start(testerlangide),
loop_sleep().

Image

Image


Debugging a remote node


Using Intellij on Windows works fine, but many Erlang libraries don't. In my opinion, Erlang is much more pleasant on UNIX systems. Still, that shouldn't prevent us from debugging.

Fortunately, the author of the plugin included a way to debug remote nodes. A local node will be spawned on the IDE's computer, connect to the target node, and fire up the debugger.

Image

If your remote node is using long names, please be sure that your intellij-erlang include this commit and this commit. Otherwise, your local node won't be started with a correct name and debugging will fail.

Image

You can even connect to an already running system, debug a few lines, and let it live again normally. That's perfect to debug hard live problems.