EZProfiler.Manager (ezprofiler_deps v1.0.0)

This module requires the ezprofiler escript, see...

https://github.com/nhpip/ezprofiler.git

https://hex.pm/packages/ezprofiler

https://hexdocs.pm/ezprofiler/api-reference.html

This module provides the ability to perform code profiling programmatically within an application rather than via the ezprofiler CLI. This maybe useful in environments where shell access maybe limited. Instead the output can be redirected to a logging subsystem for example.

Use of this module still requires the ezprofiler escript, but it will be automatically initialized in the background.

ezprofiler can be downloaded from https://github.com/nhpip/ezprofiler or added to deps in mix.exs along with this package:

defp deps do
  [
    {:ezprofiler, git: "https://github.com/nhpip/ezprofiler.git"},
    {:ezprofiler_deps, git: "https://github.com/nhpip/ezprofiler_deps.git"}
  ]
end

This profiling mechanism supports two modes of operation, synchronous and asynchronous

In synchronous mode the user starts profiling and then calls a blocking call to wait for the results.

In asynchronous mode the results are sent as a message, this will be a handle_info/2 in the case of a GenServer

synchronous-example

Synchronous Example

  EZProfiler.Manager.start_ezprofiler(%EZProfiler.Manager.Configure{ezprofiler_path: :deps})
  ...
  ...
  with :ok <- EZProfiler.Manager.enable_profiling(),
       :ok <- EZProfiler.Manager.wait_for_results(),
       {:ok, filename, results} <- EZProfiler.Manager.get_profiling_results(true)
  do
      {:ok, filename, results}
  else
    rsp ->
      rsp
  end
  ...
  ...
  EZProfiler.Manager.stop_ezprofiler()

asynchronous-example-as-a-genserver

Asynchronous Example as a GenServer

  ## Your handle_cast
  def handle_cast(:start_profiling, state) do
    EZProfiler.Manager.start_ezprofiler(%EZProfiler.Manager.Configure{ezprofiler_path: :deps})
    EZProfiler.Manager.enable_profiling()
    EZProfiler.Manager.wait_for_results_non_block()
    {:noreply, state}
  end

  def handle_info({:ezprofiler, :results_available, filename, results}, state) do
    EZProfiler.Manager.stop_ezprofiler()
    do_something_with_results(filename, results)
    {:noreply, state}
  end

  def handle_info({:ezprofiler, :results_available}, state) do
    {:ok, filename, results} = EZProfiler.Manager.get_profiling_results(true)
    EZProfiler.Manager.stop_ezprofiler()
    {:noreply, state}
  end

  def handle_info({:ezprofiler, :timeout}, state) do
    # Ooops
    EZProfiler.Manager.stop_ezprofiler()
    {:noreply, state}
  end

Link to this section Summary

Functions

Disables code profiling. The equivalent of hitting r in the CLI.

Enables code profiling. The equivalent of hitting c or c label in the CLI.

Returns the resulting code profiling results. If the option display is set to true it will also output the stdout.

Starts and configures the ezprofiler escript. Takes the %EZProfiler.Manager.Configure{} struct as configuration.

Stops the ezprofiler escript. The equivalent of hitting q in the CLI.

Waits timeout seconds (default 60) for code profiling to complete.

This is an asynchronous version of wait_for_results/1. This will cause a message to be sent to the process id specified as the first argument.

Link to this section Types

@type display() :: boolean()
@type filename() :: String.t()
@type label() :: atom() | String.t()
Link to this type

profile_data()

@type profile_data() :: String.t()
Link to this type

profiling_cfg()

@type profiling_cfg() :: EZProfiler.Manager.Configure.t()
@type self() :: pid()
@type wait_time() :: integer()

Link to this section Functions

Link to this function

disable_profiling()

Disables code profiling. The equivalent of hitting r in the CLI.

Link to this function

enable_profiling(label \\ :any_label)

@spec enable_profiling(label() | none()) :: :ok

Enables code profiling. The equivalent of hitting c or c label in the CLI.

Link to this function

get_profiling_results(display \\ false)

@spec get_profiling_results(display() | false) ::
  {:ok, filename(), profile_data()} | {:error, atom()}

Returns the resulting code profiling results. If the option display is set to true it will also output the stdout.

On success it will return the tuple {:ok, filename, result_string}

Link to this function

start_ezprofiler(profiling_cfg \\ %Configure{})

@spec start_ezprofiler(profiling_cfg()) ::
  {:ok, :started} | {:error, :timeout} | {:error, :not_started}

Starts and configures the ezprofiler escript. Takes the %EZProfiler.Manager.Configure{} struct as configuration.

Most fields map directly onto the equivalent arguments for starting ezprofiler.

The exception to this is ezprofiler_path that takes the following options:

  :system - if `ezprofiler` is defined via the `PATH` env variable.
  :deps - if `ezprofiler` is included as an application in `mix.ezs`
  path - a string specifying the full path for `ezprofiler`

example

Example

  %EZProfiler.Manager.Configure{
    cookie: nil,
    cpfo: "false",
    directory: "/tmp/",
    ezprofiler_path: :system,
    mf: "_:_",
    node: nil,
    profiler: "eprof",
    sort: "mfa"
  }
Link to this function

stop_ezprofiler()

Stops the ezprofiler escript. The equivalent of hitting q in the CLI.

Link to this function

wait_for_results(wait_time \\ 60)

@spec wait_for_results(wait_time() | 60) :: :ok | {:error, :timeout}

Waits timeout seconds (default 60) for code profiling to complete.

Link to this function

wait_for_results_non_block(pid \\ nil, wait_time \\ 60)

@spec wait_for_results_non_block(pid() | self(), wait_time() | 60) :: :ok

This is an asynchronous version of wait_for_results/1. This will cause a message to be sent to the process id specified as the first argument.

If no pid is specified the result is sent to self()

Three messages can be received:

{:ezprofiler, :results_available, filename, results}
{:ezprofiler, :results_available}  # Needs to call `get_profiling_results/1`
{:ezprofiler, :timeout}

In the case of a GenServer these will be received by handle_info/2