Inspect the environment of a Process

What would be the correct approach to inspect the current environment of a Process? For example, if I would have a simple Process defined as below:

defmodule Stack do
  def loop(state, ctr) do
    receive do
      {_from, :push, value} ->
        loop([value | state], ctr + 1)

      {from, :pop} ->
        [h | t] = state
        send(from, {:reply, h})
        loop(t, ctr)
    end

    loop(state)
  end
end

Here the environment consists of two variables (parameters of the loop function) at any point during execution. How could I easily get a list of such variables and their associated value as such:

state: value
ctr: value

I’m new to Elixir. Thanks in advance!

Hi @Astarno,
In your example, you could add a receive clause to return the state:

defmodule Stack do
  def loop(state, ctr) do
    receive do
      {_from, :push, value} ->
        loop([value | state], ctr + 1)

      {from, :pop} ->
        [h | t] = state
        send(from, {:reply, h})
        loop(t, ctr)

      {from, :inspect} ->
        send(from, {state, ctr})
        loop(state, ctr)
    end
  end
end

Now you can send a message like {self(), :inspect} and receive the state as a message:

iex(1)> pid = spawn(Stack, :loop, [[], 0])
#PID<0.127.0>
iex(2)> send(pid, {self, :inspect})
{#PID<0.105.0>, :inspect}
iex(3)> flush
{[], 0}
:ok
iex(4)> send(pid, {self, :push, "foo"})
{#PID<0.105.0>, :push, "foo"}
iex(5)> flush
:ok
iex(6)> send(pid, {self, :inspect})
{#PID<0.105.0>, :inspect}
iex(7)> flush
{["foo"], 1}
:ok
2 Likes

I’m trying to do this without altering any code inside of the Process. Would you have an approach for that as well?

When implementing something similar with a GenServer one can use :sys.get_state(pid) for debugging, but in this case I don’t see a way without modifying the code. In your example the state is the arguments of the recursive call to loop, so one needs to tap into the function to inspect the state.

What’s your use case?

3 Likes

I’m researching if Elixir would be fit to develop and experiment with a new visual inspection tool. For this I would require to be able to hook into and inspect a lot of meta information, such as the enivronment of an actor. The idea is that this visual inspection tool should work on existing code, requiring no changes or only very minimal changes to it (like an import).

Unfortunatly Elixir seems to let me down on this front. Other things I’ve looked at such as extending the receive macro also seem to not be possible.

In general the better practice would be to use built in OTP abstractions like GenServer, as @lucaong mentioned, rather than plain loop with receive do, and we can use :sys.get_state, :sys.replace_state, :observer and other tools to inspect running system in realtime without changing the code of the project.

1 Like

Would using GenServer have any disadvantages over simple Processes? Would this also allow me to inspect stuff like the mailbox, the arrival of messages, etc without adding code?

Would using GenServer have any disadvantages over simple Processes?

I can’t think of real disadvantages. Surely, GenServer and other OTP constructs add some overhead to bare-process as they handle more stuff for us but it’s for our own good =)
please take a look at this answer and the thread overall

Would this also allow me to inspect stuff like the mailbox, the arrival of messages, etc without adding code?

Process.info/2 could help to get the info about mailbox. Though, one thing to note, is GenServer reads messages from mailbox upon arrival and matches to one of the callbacks. So if the process is not blocked you won’t see the messages in the mailbox by checking them via Process.info/2. It’s also possible to trace the process for example with :dbg.p/2
see flag :r

r (receive) Traces the messages the process or port receives.

So it depends on what you are trying to achieve and how you build the system.