aboutsummaryrefslogtreecommitdiffstats
path: root/lib/runtime_tools/src/percept_profile.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/runtime_tools/src/percept_profile.erl')
-rw-r--r--lib/runtime_tools/src/percept_profile.erl196
1 files changed, 196 insertions, 0 deletions
diff --git a/lib/runtime_tools/src/percept_profile.erl b/lib/runtime_tools/src/percept_profile.erl
new file mode 100644
index 0000000000..b333dee0cf
--- /dev/null
+++ b/lib/runtime_tools/src/percept_profile.erl
@@ -0,0 +1,196 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% @doc Percept Collector
+%%
+%% This module provides the user interface for the percept data
+% collection (profiling).
+%%
+
+-module(percept_profile).
+-export([
+ start/1,
+ start/2,
+ start/3,
+ stop/0
+ ]).
+
+
+%%==========================================================================
+%%
+%% Type definitions
+%%
+%%==========================================================================
+
+%% @type percept_option() = procs | ports | exclusive
+
+-type(percept_option() :: 'procs' | 'ports' | 'exclusive' | 'scheduler').
+
+%%==========================================================================
+%%
+%% Interface functions
+%%
+%%==========================================================================
+
+%% @spec start(Filename::string()) -> {ok, Port} | {already_started, Port}
+%% @equiv start(Filename, [procs])
+
+-spec(start/1 :: (Filename :: string()) ->
+ {'ok', port()} | {'already_started', port()}).
+
+start(Filename) ->
+ profile_to_file(Filename, [procs]).
+
+%% @spec start(Filename::string(), [percept_option()]) -> {ok, Port} | {already_started, Port}
+%% Port = port()
+%% @doc Starts profiling with supplied options.
+%% All events are stored in the file given by Filename.
+%% An explicit call to stop/0 is needed to stop profiling.
+
+-spec(start/2 :: (
+ Filename :: string(),
+ Options :: [percept_option()]) ->
+ {'ok', port()} | {'already_started', port()}).
+
+start(Filename, Options) ->
+ profile_to_file(Filename, Options).
+
+%% @spec start(string(), MFA::mfa(), [percept_option()]) -> ok | {already_started, Port} | {error, not_started}
+%% Port = port()
+%% @doc Starts profiling at the entrypoint specified by the MFA. All events are collected,
+%% this means that processes outside the scope of the entry-point are also profiled.
+%% No explicit call to stop/0 is needed, the profiling stops when
+%% the entry function returns.
+
+-spec(start/3 :: (
+ Filename :: string(),
+ Entry :: {atom(), atom(), list()},
+ Options :: [percept_option()]) ->
+ 'ok' | {'already_started', port()} | {'error', 'not_started'}).
+
+start(Filename, {Module, Function, Args}, Options) ->
+ case whereis(percept_port) of
+ undefined ->
+ profile_to_file(Filename, Options),
+ erlang:apply(Module, Function, Args),
+ stop();
+ Port ->
+ {already_started, Port}
+ end.
+
+deliver_all_trace() ->
+ Tracee = self(),
+ Tracer = spawn(fun() ->
+ receive {Tracee, start} -> ok end,
+ Ref = erlang:trace_delivered(Tracee),
+ receive {trace_delivered, Tracee, Ref} -> Tracee ! {self(), ok} end
+ end),
+ erlang:trace(Tracee, true, [procs, {tracer, Tracer}]),
+ Tracer ! {Tracee, start},
+ receive {Tracer, ok} -> ok end,
+ erlang:trace(Tracee, false, [procs]),
+ ok.
+-spec(stop/0 :: () -> 'ok' | {'error', 'not_started'}).
+
+%% @spec stop() -> ok | {'error', 'not_started'}
+%% @doc Stops profiling.
+
+
+stop() ->
+ erlang:system_profile(undefined, [runnable_ports, runnable_procs]),
+ erlang:trace(all, false, [procs, ports, timestamp]),
+ deliver_all_trace(),
+ case whereis(percept_port) of
+ undefined ->
+ {error, not_started};
+ Port ->
+ erlang:port_command(Port, erlang:term_to_binary({profile_stop, erlang:now()})),
+ %% trace delivered?
+ erlang:port_close(Port),
+ ok
+ end.
+
+%%==========================================================================
+%%
+%% Auxiliary functions
+%%
+%%==========================================================================
+
+profile_to_file(Filename, Opts) ->
+ case whereis(percept_port) of
+ undefined ->
+ io:format("Starting profiling.~n", []),
+
+ erlang:system_flag(multi_scheduling, block),
+ Port = (dbg:trace_port(file, Filename))(),
+ % Send start time
+ erlang:port_command(Port, erlang:term_to_binary({profile_start, erlang:now()})),
+ erlang:system_flag(multi_scheduling, unblock),
+
+ %% Register Port
+ erlang:register(percept_port, Port),
+ set_tracer(Port, Opts),
+ {ok, Port};
+ Port ->
+ io:format("Profiling already started at port ~p.~n", [Port]),
+ {already_started, Port}
+ end.
+
+%% set_tracer
+
+set_tracer(Port, Opts) ->
+ {TOpts, POpts} = parse_profile_options(Opts),
+ % Setup profiling and tracing
+ erlang:trace(all, true, [{tracer, Port}, timestamp | TOpts]),
+ erlang:system_profile(Port, POpts).
+
+%% parse_profile_options
+
+parse_profile_options(Opts) ->
+ parse_profile_options(Opts, {[],[]}).
+
+parse_profile_options([], Out) ->
+ Out;
+parse_profile_options([Opt|Opts], {TOpts, POpts}) ->
+ case Opt of
+ procs ->
+ parse_profile_options(Opts, {
+ [procs | TOpts],
+ [runnable_procs | POpts]
+ });
+ ports ->
+ parse_profile_options(Opts, {
+ [ports | TOpts],
+ [runnable_ports | POpts]
+ });
+ scheduler ->
+ parse_profile_options(Opts, {
+ TOpts,
+ [scheduler | POpts]
+ });
+ exclusive ->
+ parse_profile_options(Opts, {
+ TOpts,
+ [exclusive | POpts]
+ });
+ _ ->
+ parse_profile_options(Opts, {TOpts, POpts})
+
+ end.