defmodule Phoenix.LiveDashboard.ProcessInfoComponent do
use Phoenix.LiveDashboard.Web, :live_component
alias Phoenix.LiveDashboard.SystemInfo
@info_keys [
:initial_call,
:registered_name,
:current_function,
:status,
:message_queue_len,
:links,
:monitors,
:monitored_by,
:trap_exit,
:error_handler,
:priority,
:group_leader,
:total_heap_size,
:heap_size,
:stack_size,
:reductions,
:garbage_collection,
:suspending,
:current_stacktrace
]
@impl true
def mount(socket) do
{:ok, Enum.reduce(@info_keys, socket, &assign(&2, &1, nil))}
end
@impl true
def render(assigns) do
~H"""
<%= if @alive do %>
<:elem label="Registered name"><%= @registered_name %>
<:elem label="Current function"><%= @current_function %>
<:elem label="Initial call"><%= @initial_call %>
<:elem label="Status"><%= @status %>
<:elem label="Message queue length"><%= @message_queue_len %>
<:elem label="Ancestors"><.info links={@ancestor_links} />
<:elem label="Other links"><.info links={@other_links} />
<:elem label="Monitors"><.info links={@monitors} />
<:elem label="Monitored by"><.info links={@monitored_by} />
<:elem label="Trap exit"><%= @trap_exit %>
<:elem label="Error handler"><%= @error_handler %>
<:elem label="Priority"><%= @priority %>
<:elem label="Group leader"><%= @group_leader %>
<:elem label="Total heap size"><%= @total_heap_size %>
<:elem label="Heap size"><%= @heap_size %>
<:elem label="Stack size"><%= @stack_size %>
<:elem label="Reductions"><%= @reductions %>
<:elem label="Garbage collection"><%= @garbage_collection %>
<:elem label="Suspending"><%= @suspending %>
<:elem label="Current stacktrace"><%= @current_stacktrace %>
<%= if @page.allow_destructive_actions do %>
<% end %>
<% else %>
Process is not alive or does not exist.
<% end %>
"""
end
@impl true
def update(%{id: "PID" <> pid, path: path, return_to: return_to, page: page}, socket) do
pid = :erlang.list_to_pid(String.to_charlist(pid))
{:ok,
socket |> assign(pid: pid, path: path, page: page, return_to: return_to) |> assign_info()}
end
@impl true
def handle_event("kill", _, socket) do
true = socket.assigns.page.allow_destructive_actions
Process.exit(socket.assigns.pid, :kill)
{:noreply, push_patch(socket, to: socket.assigns.return_to)}
end
defp assign_info(%{assigns: assigns} = socket) do
case SystemInfo.fetch_process_info(assigns.pid) do
{:ok, info} ->
Enum.reduce(info, socket, fn
{:ancestors, ancestors}, acc ->
acc
|> assign(:ancestor_links, format_info(:links, ancestors, assigns.path))
|> assign(
:other_links,
format_info(:links, info[:links] -- ancestors, assigns.path)
)
{key, val}, acc ->
assign(acc, key, format_info(key, val, assigns.path))
end)
|> assign(alive: true)
:error ->
assign(socket, alive: false)
end
end
defp format_info(key, val, live_dashboard_path)
when key in [:links, :monitors, :monitored_by],
do: format_value(val, live_dashboard_path)
defp format_info(:current_function, val, _), do: format_call(val)
defp format_info(:initial_call, val, _), do: format_initial_call(val)
defp format_info(:current_stacktrace, val, _), do: format_stacktrace(val)
defp format_info(_key, val, live_dashboard_path), do: format_value(val, live_dashboard_path)
defp info(%{links: links} = assigns) when is_list(links) do
~H"""
<%= for info <- @links do %>
<%= info %>
<% end %>
"""
end
defp info(%{links: _links} = assigns), do: ~H|<%= @links %>|
end