diff options
Diffstat (limited to 'lib/runtime_tools/src/percept_profile.erl')
-rw-r--r-- | lib/runtime_tools/src/percept_profile.erl | 196 |
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. |