lib/phoenix_profiler.ex

defmodule PhoenixProfiler do
  @external_resource "README.md"
  @moduledoc @external_resource
             |> File.read!()
             |> String.split("<!-- MDOC -->")
             |> Enum.fetch!(1)

  @doc """
  Returns the child specification to start the profiler
  under a supervision tree.
  """
  def child_spec(opts) do
    %{
      id: opts[:name] || PhoenixProfiler,
      start: {PhoenixProfiler.Supervisor, :start_link, [opts]}
    }
  end

  @behaviour Plug

  @impl Plug
  defdelegate init(opts), to: PhoenixProfiler.Plug

  @impl Plug
  defdelegate call(conn, opts), to: PhoenixProfiler.Plug

  # TODO: Remove when we require LiveView v0.17+.
  @doc false
  def mount(params, session, socket) do
    on_mount(:default, params, session, socket)
  end

  @doc """
  The callback for the mount stage of the LiveView lifecycle.

  To enable live profiling, add the following on your LiveView:

      on_mount PhoenixProfiler

  """
  def on_mount(_arg, _params, _session, socket) do
    {:cont, PhoenixProfiler.Utils.maybe_mount_profile(socket)}
  end

  @doc """
  Enables the profiler on a given `conn` or connected `socket`.

  Normally you do not need to invoke this function manually. It is invoked
  automatically by the PhoenixProfiler plug in the Endpoint when a
  profiler is enabled. In LiveView v0.16+ it is invoked automatically when
  you define `on_mount PhoenixProfiler` on your LiveView.

  This function will raise if the endpoint is not configured with a profiler,
  or if the configured profiler is not running. For LiveView specifically,
  this function also raises if the given socket is not connected.

  ## Example

  Within a Phoenix Controller (for example, on a show callback):

      def show(conn, params) do
        conn = PhoenixProfiler.enable(conn)
        # code...
      end

  Within a LiveView (for example, on the mount callback):

      def mount(params, session, socket) do
        socket =
          if connected?(socket) do
            PhoenixProfiler.enable(socket)
          else
            socket
          end

        # code...
      end

  """
  defdelegate enable(conn_or_socket), to: PhoenixProfiler.Utils, as: :enable_profiler

  @doc """
  Disables profiling on a given `conn` or `socket`.

  ## Examples

  Within a Phoenix Controller (for example, on an update callback):

      def update(conn, params) do
        conn = PhoenixProfiler.disable(conn)
        # code...
      end

  Within in a LiveView (for example, on a handle_event callback):

      def handle_event("some-event", _, socket) do
        socket = PhoenixProfiler.disable(socket)
        # code...
      end

  Note that only for LiveView, if you invoke `disable/1` on
  the LiveView `mount` callback, the profiler may not be
  registered yet and it will not receive the disable message.
  If you need on-demand profiling, it is recommended you
  start with the profiler in a disabled state and enable it
  after the LiveView has mounted.
  """
  defdelegate disable(conn_or_socket), to: PhoenixProfiler.Utils, as: :disable_profiler

  @doc """
  Resets the storage of the given `profiler`.
  """
  defdelegate reset(profiler), to: PhoenixProfiler.ProfileStore

  @doc """
  Returns all running PhoenixProfiler names.
  It is important to notice that no order is guaranteed.
  """
  def all_running do
    for {{PhoenixProfiler, name}, %PhoenixProfiler.ProfileStore{}} <- :persistent_term.get(),
        GenServer.whereis(name),
        do: name
  end
end