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