EZProfiler.Manager (ezprofiler_deps v1.1.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, blocking
and non-blocking
In blocking mode the user starts profiling and then calls a blocking function to wait for the results.
In non-blocking mode the results are sent as a message, this will be a handle_info/2
in the case of a GenServer
The results are a map
or list of maps that contains:
%{type: type, # One of :normal or :pseudo
label: label, # The label used by this run
filename: filename, # The results file
profiler: profiler, # "eprof, "fprof", "cprof" or :no_profiler in case of pseudo
results_data: results # String containing the results
}
blocking-example
Blocking Example
EZProfiler.Manager.start_ezprofiler(%EZProfiler.Manager.Configure{ezprofiler_path: :deps})
...
...
with :ok <- EZProfiler.Manager.set_mgmt_mode_blocking() ## Set before `enable_profiling/0`
:ok <- EZProfiler.Manager.enable_profiling(),
{:ok, run_results} <- EZProfiler.Manager.wait_for_results(),
{:ok, results} <- EZProfiler.Manager.get_profiling_results(true)
do
{:ok, results}
else
rsp ->
rsp
end
...
...
EZProfiler.Manager.stop_ezprofiler()
The function wait_for_results/1
will return the results of that current profiling run, whereas get_profiling_results/1
returns a list of
results for that entire run (e.g. if label transition is selected). This is cleared if enable_profiling/1
or disable_profiling/0
is called
non-blocking-example-as-a-genserver
Non-blocking 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.set_mgmt_mode_non_blocking() ## Set before `enable_profiling/0`
EZProfiler.Manager.enable_profiling()
{:noreply, state}
end
def handle_info({:ezprofiler_results, results}, state) do
EZProfiler.Manager.stop_ezprofiler() # Don't have to stop if you don't want to
do_something_with_results(results.type, results.filename, results.results_data)
{:noreply, state}
end
## Change timeout with `EZProfiler.Manager.profiling_time/1`
def handle_info({:ezprofiler, :profiling_timeout}, state) do
# Ooops, but it's possible results are available
{:ok, results} = EZProfiler.Manager.get_profiling_results()
{:noreply, state}
end
## Change timeout with `EZProfiler.Manager.profiling_start_wait/1`
def handle_info({:ezprofiler, :start_profiling_timeout}, state) do
# Ooops
{:noreply, state}
end
labels
Labels
When using EZProfiler.Manager.enable_profiling/1
either a single label or a list of labels can be specified. In the case of a list there are two modes of operation,
label transition (labeltran
) true
or label transition false
(the default). The behavior is as follows:
Label Transition false
This effectively a request to profile one-of those labels. The first matching label is selected for profiling and the rest of the labels are ignored.
Label Transition true
In this case all specified labels shall be profiled sequentially (order doesn't matter), effectively the profiler automatically re-enables profiling after a label match. A label that matches and is profiled, will removed from the list of labels to be profiled next and profiling is re-enabled for the remaining labels. This allows profiling to follow the flow of code through your application, even if processes are switched.
It is important to note that the rule of only one process at a time can be profiled still exists, so ideally profiled code calls should be sequential. However, if there are sections of code that need to
be profiled that overlap in time ezprofiler
performs pseudo profiling
. This is where ezprofiler
will calculate and display how long the profiled code took to execute.
Use allow_label_transition/1
to configure label transition.
Link to this section Summary
Functions
If many labels are specified in enable_profiling/1
setting this to true
will automatically re-enable profiling
after one label has been profiled.
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. A single label, or list of labels can be specified
Returns the resulting code profiling results. If the option display
is set to true it will also output the stdout
.
Specifies how long we wait for profiling to actually start once enable_profiling/1
is issued.
Specifies how long we wait for profiling to actually complete once started.
Sets the operating mode to blocking. This requires the user to make calls to EZProfiler.Manager.wait_for_results/0
and
EZProfiler.Manager.get_profiling_results/0
to get the profiling results.
Sets the operating mode to non-blocking. This will cause events from the profiler to be sent to the process id specified.
If no process id is specified the result is sent to self()
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
milliseconds (default 5000) for any results and returns the result as a map.
Link to this section Types
display()
@type display() :: boolean()
filename()
@type filename() :: String.t()
label()
labels()
@type labels() :: [label()]
profile_data()
@type profile_data() :: String.t()
profiling_cfg()
@type profiling_cfg() :: EZProfiler.Manager.Configure.t()
profiling_time()
@type profiling_time() :: integer()
result()
@type result() :: map()
results()
@type results() :: [map()]
self()
@type self() :: pid()
wait_time()
@type wait_time() :: integer()
Link to this section Functions
allow_label_transition(allow?)
@spec allow_label_transition(boolean()) :: :ok | {:error, :not_running}
If many labels are specified in enable_profiling/1
setting this to true
will automatically re-enable profiling
after one label has been profiled.
example
Example
EZProfiler.Manager.enable_profiling(["L1", "L2", "L3"])
L1 is hit and profiled the next profile will be the equivalent of issuing:
EZProfiler.Manager.enable_profiling(["L2", "L3"])
Then L2
hit:
EZProfiler.Manager.enable_profiling(["L3"])
This permits profiling a flow that may involve messages between a number of processes.
disable_profiling()
@spec disable_profiling() :: :ok
Disables code profiling. The equivalent of hitting r
in the CLI.
enable_profiling(label \\ :any_label)
Enables code profiling. The equivalent of hitting c
or c label
in the CLI. A single label, or list of labels can be specified
get_profiling_results(display \\ false)
@spec get_profiling_results(display() | false) :: {:ok, results()} | {:error, :not_running} | {: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, results}
profiling_start_wait(time)
@spec profiling_start_wait(profiling_time() | :infinity) :: :ok | {:error, :not_running}
Specifies how long we wait for profiling to actually start once enable_profiling/1
is issued.
Time is specified in milliseconds or the atom :infinity
, default :infinity
profiling_time(time)
@spec profiling_time(profiling_time()) :: :ok | {:error, :not_running}
Specifies how long we wait for profiling to actually complete once started.
Time is specified in milliseconds.
set_mgmt_mode_blocking()
@spec set_mgmt_mode_blocking() :: :ok | {:error, :not_running}
Sets the operating mode to blocking. This requires the user to make calls to EZProfiler.Manager.wait_for_results/0
and
EZProfiler.Manager.get_profiling_results/0
to get the profiling results.
set_mgmt_mode_non_blocking(pid \\ nil)
Sets the operating mode to non-blocking. This will cause events from the profiler to be sent to the process id specified.
If no process id is specified the result is sent to self()
Three messages can be received:
{:ezprofiler_results, result}
{:ezprofiler, :profiling_timeout}
{:ezprofiler, :start_profiling_timeout}
Result is a map:
%{
type: type, # One of :normal or :pseudo
label: label, # The label used by this run
filename: filename, # The results file
profiler: profiler, # "eprof, "fprof", "cprof" or :no_profiler in case of pseudo
results_data: results # String containing the results
}
In the case of a GenServer
these will be received by handle_info/2
If {:error, :profiling_timeout}
is returned results may still be available, call EZProfiler.Manager.get_profiling_results/0
to retrieve them.
start_ezprofiler(profiling_cfg \\ %Configure{})
@spec start_ezprofiler(profiling_cfg()) :: {:ok, :started} | {:error, :timeout} | {:error, :not_started} | {:error, :already_running}
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 a dependency in `mix.exs`
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",
labeltran: true,
sort: "mfa"
}
stop_ezprofiler()
@spec stop_ezprofiler() ::
{:ok, :stopped} | {:error, :not_stopped} | {:error, :not_running}
Stops the ezprofiler
escript. The equivalent of hitting q
in the CLI.
wait_for_results(wait_time \\ 5000)
@spec wait_for_results(wait_time() | 5000) :: {:ok, result()} | {:error, :timeout} | {:error, :profiling_timeout} | {:error, :start_profiling_timeout} | {:error, :not_running}
Waits timeout
milliseconds (default 5000) for any results and returns the result as a map.
%{type: type, # One of :normal or :pseudo
label: label, # The label used by this run
filename: filename, # The results file
profiler: profiler, # "eprof, "fprof", "cprof" or :no_profiler in case of pseudo
results_data: results # String containing the results
}
If {:error, :profiling_timeout}
is returned results may still be available, call EZProfiler.Manager.get_profiling_results()
to retrieve them