10 Ways to Stop an Erlang VM

With or without crashing it.

Do you have an Erlang VM that you don’t need anymore and you don’t know what to do with it? Worry not, my friend, this is just the list for you! Fasten your seatbelt and get into your old fridges… we’re going to crash and blow things up!

Erlang: As reliable as an old fridge in a nuclear explosion

The Good

There are several ways to shutdown an Erlang VM nicely…

1. q/0, c:q/0, init:stop/0

The simplest and cleanest one is to just use q/0 or c:q/0 (which are, actually, the same thing), as the docs puts it…

This function is shorthand for init:stop(), that is, it causes the node to stop in a controlled fashion.

And init:stop/0 is…

The same as stop(0).

So, basically you end up running init:stop(0). To understand what that command does, let’s check the docs again:

All applications are taken down smoothly, all code is unloaded, and all ports are closed before the system terminates by calling halt(Status). If command-line flag -heart was specified, the heartprogram is terminated before the Erlang node terminates. For more information, see heart(3).
To limit the shutdown time, the time init is allowed to spend taking down applications, command-line flag -shutdown_time is to be used.

So, the cleanest shutdown possible. But you can also skip all the clean part and jump straight to…

2. erlang:halt/0

As you can see in the docs, erlang:halt() is the same as erlang:halt(0, []). More on erlang:halt/1,2 below, but for now erlang:halt() just runs a shutdown of the VM where no application is properly terminated and no port is properly closed.

3. JCL mode

The previous methods rely on you being able to run commands in the shell (or remotely, if you’re using distributed Erlang). What if you can’t do that because, for instance, you blocked your shell or something? Well if you’re in the shell of the node you want to turn down, even if it’s not receiving input, you can still press Ctrl-g (which takes you to JCL mode). Once there you can use the command q to quit the Erlang shell.

This is similar in effect to erlang:halt(0).

This is, by the way, the best way to stop a remsh node without stopping the original node.

4. BREAK mode

Instead of Ctrl-g, you can press Ctrl-c. That takes you to BREAK mode, which looks like this:

BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
(v)ersion (k)ill (D)b-tables (d)istribution

You can do two things to shut down the node there:

  • Press Ctrl-c again.
  • Press a and then return.

Both of them will behave like erlang:halt(0).


The Bad

Maybe you do want to exit your system in a not so nice way and produce some sort of report of things going wrong. These are some of your options, then…

5. erlang:halt/1,2

As you can see in the docs, there are some options to get erlang:halt/1,2 to produce errors on its way out. You can read about the options on the docs, but the key part is that:

  • if you want a clean shutdown with a proper exit status, you use an integer.
  • if you want to generate a erl_crash.dump, you use a string for the slogan.
  • if you want to generate a core dump, you use the atomabort.

Regardless of your choice, of course, no applications will be properly terminated, no ports will be properly closed, etc.

6. Just kill it

Of course, the Erlang VM is in the end just a process in your OS. And, if your OS is UNIX-like, you can kill processes by sending signals to them. Different signals have different impacts in an Erlang VM. The usual signals work as expected:kill -SIGTERM will behave like init:stop(). andkill -SIGKILL will behave like erlang:halt(137). (Thanks Tristan Sloughter for the clarification on kill -SIGTERM).

But there is one more signal that you can use, in this case to generate an erl_crash.dump: kill -SIGUSR1. In the generated crash dump, the slogan will be Received SIGUSR1.


The Ugly

Well… where is the fun in shutting down VMs as they are expected to be shut down? Let’s make them burn, instead…

7. Don’t even let it start

When you start an Erlang VM, you can run commands before the shell is started by specifying them in the command line (e.g. $ erl -s your_app will evaluate your_app:start() before booting up the shell). But then, what happens if those commands are wrong or broken? 💥 the VM crashes and an erl_crash.dump is generated. For instance…

$ erl -s something_that_doesnt_exist
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [kernel-poll:false]

init terminating in do_boot ({undef,[{something_that_doesnt_exist,start,[],[]},{init,start_em,1,[]},{init,do_boot,3,[]}]})
Crash dump is being written to: erl_crash.dump...done

8. Kill the Kernel

Regardless of Erlang’s legendary Let It Crash philosophy (a.k.a. The Zen of Erlang), there is one process in an Erlang VM that’s not allowed to crash: the application controller. So, if you kill it…

$ erl
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [kernel-poll:false]

1> exit(whereis(application_controller), kill).
true
*** ERROR: Shell process terminated! ***
*** ERROR: Shell process terminated! ***
*** ERROR: Shell process terminated! ***
*** ERROR: Shell process terminated! ***
*** ERROR: Shell process terminated! ***
*** ERROR: Shell process terminated! ***
*** ERROR: Shell process terminated! ***
*** ERROR: Shell process terminated! ***
*** ERROR: Shell process terminated! ***
*** ERROR: Shell process terminated! ***
{"Kernel pid terminated",application_controller,killed}
Kernel pid terminated (application_controller) (killed)
Crash dump is being written to: erl_crash.dump...done

9. Exhaust the Atom Table

The number of allowed atoms in an Erlang VM is huge and configurable, but still limited. Therefore, using erlang:system_info/1 we can determine that limit and, of course, exploit to generate one of the largest erl_crash.dump’s that you can easily generate (Warning: writing that erl_crash.dump may take a long time):

$ erl
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [kernel-poll:false]

1> [list_to_atom(integer_to_list(I)) || I <- lists:seq(erlang:system_info(atom_count), erlang:system_info(atom_limit))].
no more index entries in atom_tab (max=1048576)
Crash dump is being written to: erl_crash.dump...done

10. Turn off your Computer


Notable Mention

This was going to be my 10th method, but when I tried this on my Erlang/OTP 20.1 installation, I found that it didn’t crash. Something different happened…

Exhaust the Process List

The atom table is not the only thing that’s limited in an Erlang VM. Another one is the number of processes (configurable with +P on command line). Using a similar method as above, you could try to crash an Erlang VM by creating too many of them, but you’ll find a surprise there…

$ erl +P 1024
Erlang/OTP 20 [erts-9.0] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [kernel-poll:false]

1> [spawn(timer, sleep, [60000]) || I <- lists:seq(erlang:system_info(process_count), erlang:system_info(process_limit))].

That will not crash the VM, instead it will generate an endless stream of…

=ERROR REPORT==== 29-Apr-2018::01:25:51 ===
Error in process <0.52.52> with exit value:
{system_limit,[{erlang,spawn_link,
[erlang,apply,[#Fun<shell.1.98304428>,[]]],
[]},
{erlang,spawn_link,1,[]},
{shell,get_command,5,[{file,"shell.erl"},{line,300}]},
{shell,server_loop,7,[{file,"shell.erl"},{line,230}]}]}
Eshell V9.0 (abort with ^G)
*** ERROR: Shell process terminated! ***
=ERROR REPORT==== 29-Apr-2018::01:25:51 ===
Too many processes

The machine doesn’t crash, but the shell process does and when it tries to restart, well… it crashes again and again. But everything else keeps running.


I hope you have enjoyed the list. If you think I missed some other ways to crash an Erlang VM, let me know in the comments below! :)