Search code examples
erlangelixirerlelixir-iex

What is (k)ill for in the iex break menu?


I access the Break Menu of eix 1.8.2 by pressing CTRL + C. It looks like this:

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

At first I assumed kill would be similar to abort (ie, just ends the session), but no. Instead, pressing k produces a core dump and offers more options:

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


Process Information

--------------------------------------------------
=proc:<0.105.0>
State: Waiting
Spawned as: erlang:apply/2
Spawned by: <0.75.0>
Message queue length: 0
Number of heap fragments: 1
Heap fragment data: 5
Link list: [{to,<0.64.0>,#Ref<0.720592203.270008322.27074>}]
Reductions: 4202
Stack+heap: 233
OldHeap: 0
Heap unused: 177
OldHeap unused: 0
BinVHeap: 1
OldBinVHeap: 0
BinVHeap unused: 46421
OldBinVHeap unused: 46422
Memory: 2804
Stack dump:
Program counter: 0x000000001f8230e0 (io:execute_request/2 + 200)
CP: 0x0000000000000000 (invalid)
arity = 0

0x000000001ddcee08 Return addr 0x000000001f8a4ba0 ('Elixir.IEx.Server':io_get/3 + 96)
y(0)     #Ref<0.720592203.270008322.27074>
y(1)     {false,{get_line,unicode,<<"iex(1)> ">>}}
y(2)     <0.64.0>

0x000000001ddcee28 Return addr 0x000000001d53ecf8 (<terminate process normally>)
y(0)     <0.105.0>
y(1)     <0.75.0>
Internal State: ACT_PRIO_NORMAL | USR_PRIO_NORMAL | PRQ_PRIO_NORMAL
(k)ill (n)ext (r)eturn:

If I press k again, I get another core dump. Pressing n also gives me a core dump and I think it's the same as pressing k. The final option, r, does different things depending on what I've done previously. If I've only pressed k or n a few times, it just ignores it and I have to press enter twice. iex interprets the second enter as it normally would and returns nil.

(k)ill (n)ext (r)eturn:
r

nil

If I've pressed k and n a bunch of times, it either does this:

(k)ill (n)ext (r)eturn:
r
** (EXIT from #PID<0.104.0>) shell process exited with reason: killed

Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> 
09:39:57.929 [info]  Application iex exited: killed

or this:

(k)ill (n)ext (r)eturn:
r

09:46:20.268 [info]  Application iex exited: killed

09:46:20.269 [info]  Application elixir exited: killed

09:46:20.274 [error] GenServer IEx.Pry terminating
** (stop) killed
Last message: {:EXIT, #PID<0.88.0>, :killed}
State: 1

or this:

(k)ill (n)ext (r)eturn:
r
Logger - error: {removed_failing_handler,'Elixir.Logger'}
Logger - error: {removed_failing_handler,'Elixir.Logger'}
Logger - error: {removed_failing_handler,'Elixir.Logger'}

I am unsure how it decides which of those messages should be displayed.

I'm really curious what (k)ill and it's suboptions do and look forward to learning about it. Any direction is appreciated, thanks!


Solution

  • The kill command goes through all running processes, and for each of them displays a bunch of information and asks you whether to:

    • kill it and go to the next process (k)
    • go to the next process without killing this one (n), or
    • stop killing processes and go back to the shell (r).

    It might be tricky to identify the process you want to kill. One thing you can look at is the Dictionary line, which for most long-running processes has an $initial_call entry telling you which module contains the code that this process is running. For example:

    Dictionary: [{'$ancestors',[<0.70.0>]},{iex_evaluator,ack},{'$initial_call',{'Elixir.IEx.Evaluator',init,4}}]
    

    The different messages are displayed depending on which process(es) you killed. For example, it seems like Elixir.IEx.Evaluator is the process running the Elixir shell, which gives you the shell process exited with reason: killed error message.

    A way of looking at this is that it shows the fault tolerance of an Elixir application: even if a process somewhere within the system has an error (in this case caused by explicitly killing the process), the supervisors try to restart the process in question and keep the entire system running.


    Actually, I've never used this way of killing processes in a running system. If you know the process id ("pid") of the process you want to kill, you can type something like this into the shell:

    Process.exit(pid("0.10.0"), :kill)
    

    without having to step through the list of processes.