ides

Caution

This is currently in beta, and being proven out.

Beware the Ides of March — find the supervisors and siblings that could kill your Erlang process.

Given any PID, ides shows:

API documentation is generated from source via rebar3 ex_doc. See src/ides.erl.

Examples

one_for_one

my_sup (one_for_one)
/ \
my_server my_statem
(permanent) (transient)

A child terminating independently does not affect siblings.

1> {ok, Tree} = ides:ancestors(MyStatemPid).
2> ides:print(MyStatemPid, Tree).
my_sup (one_for_one)
my_server (permanent)
* my_statem (transient)
ok

one_for_all

my_sup (one_for_all)
/ | \
worker_1 worker_2 cache
(permanent) (permanent) (temporary)

Any child terminating abnormally kills and restarts all siblings.

1> {ok, Tree} = ides:ancestors(Worker2Pid).
2> ides:print(Worker2Pid, Tree).
my_sup (one_for_all)
worker_1 (permanent)
* worker_2 (permanent)
cache (temporary)
ok

rest_for_one

my_sup (rest_for_one)
/ | \
startup process cleanup
(permanent) (permanent) (permanent)

A child at position i terminating kills and restarts all children at position i..N.

1> {ok, Tree} = ides:ancestors(ProcessPid).
2> ides:print(ProcessPid, Tree).
my_sup (rest_for_one)
startup (permanent)
* process (permanent)
cleanup (permanent)
ok

simple_one_for_one

pool_sup (simple_one_for_one)
/ \
handler_1 handler_2
(permanent) (permanent)

Same as one_for_one — children restart independently.

1> {ok, Tree} = ides:ancestors(Handler2Pid).
2> ides:print(Handler2Pid, Tree).
pool_sup (simple_one_for_one)
handler_1 (permanent)
* handler_2 (permanent)
ok

Nested escalation

app_sup (one_for_one)
|
sup1 (one_for_all)
/ \
worker_1 worker_2
(permanent) (permanent)

When sup1 exceeds its restart intensity, it terminates itself — escalating to app_sup, which kills all remaining sup1 children.

1> {ok, Tree} = ides:ancestors(Worker2Pid).
2> ides:print(Worker2Pid, Tree).
app_sup (one_for_one)
sup1 (one_for_all, permanent)
worker_1 (permanent)
* worker_2 (permanent)
ok

kill_graph

my_sup (one_for_one)
/ \
my_server my_statem
(permanent) (transient)

A process's kill graph includes ancestors plus any siblings that could trigger a cascade restart. Under one_for_one, siblings do not affect each other.

1> {ok, Killers} = ides:kill_graph(MyStatemPid).
2> Killers.
[<0.55.0>] %% only the parent supervisor

Under one_for_all, every sibling is a potential killer:

my_sup (one_for_all)
/ | \
worker_1 worker_2 cache
(perm) (perm) (temp)
1> {ok, Killers} = ides:kill_graph(Worker2Pid).
%% Killers includes my_sup, worker_1, and cache

should_restart

1> ides:should_restart(PermanentChild, normal). %% true
2> ides:should_restart(TransientChild, normal). %% false
3> ides:should_restart(TransientChild, abnormal). %% true
4> ides:should_restart(TemporaryChild, abnormal). %% false

affected_siblings

my_sup (rest_for_one)
/ | \
startup process cleanup
(permanent) (permanent) (permanent)

Under rest_for_one, a process dying affects itself and all later siblings:

1> {ok, Affected} = ides:affected_siblings(ProcessPid).
%% Affected = [process_pid, cleanup_pid]

Under one_for_one, only the terminated process itself is affected:

1> {ok, Affected} = ides:affected_siblings(StartupPid).
%% Affected = [startup_pid]

How

Uses OTP primitives:

Caveats

Build

$ rebar3 compile