%% -*- erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Copyright (c) 2001 by Erik Johansson. All Rights Reserved
%% Time-stamp: <2008-04-20 14:53:42 richard>
%% ====================================================================
%% Module : hipe_profile
%% Purpose :
%% History : * 2001-07-12 Erik Johansson (happi@it.uu.se): Created.
%% ====================================================================
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-module(hipe_profile).
-export([%% profile/1, mods_profile/1,
prof/0, prof_off/0, clear/0, res/0,
mods_res/0,
%% clear_module/1, res_module/1,
prof_module/1, prof_module_off/1]).
%% %% @spec mods_profile(F) -> [{mod(),calls()}]
%% %% F = () -> term()
%% %% mod() = atom()
%% %% calls()= integer()
%% %%
%% %% @doc Returns the number of calls per module generated by
%% %% applying F().
%% %% The resulting lists is sorted with the most called
%% %% module first.
%% mods_profile(F) ->
%% F(),
%% prof(),
%% clear(),
%% F(),
%% R = mods_res(),
%% prof_off(),
%% R.
-spec mods_res() -> [{atom(), non_neg_integer()}].
%% @doc Returns the number of calls per module currently
%% recordeed since hipe_bifs:call_count_on().
%% The resulting list is sorted with the most called
%% module first.
mods_res() ->
lists:reverse(lists:keysort(2, calls())).
-spec calls() -> [{atom(), non_neg_integer()}].
%% @doc Returns the number of calls per module currently
%% recordeed since hipe_bifs:call_count_on().
calls() ->
[{Mod, total_calls(Mod)} || Mod <- mods(),
total_calls(Mod) > 1,
Mod =/= hipe_profile].
%% %% @spec profile(F) -> [{mfa(),calls()}]
%% %% F = () -> term()
%% %% mfa() = {mod(),function(),arity()}
%% %% function() = atom()
%% %% arity() = intger()
%% %%
%% %% @doc Returns the number of calls per module generated by
%% %% applying F().
%% %% The resulting lists is sorted with the most called
%% %% module first.
%% profile(F) ->
%% %% Make sure all code is loaded.
%% F(),
%% %% Turn profiling on.
%% prof(),
%% clear(),
%% %% Apply the closure to profile.
%% F(),
%% %% Get result.
%% R = res(),
%% %% Turn off profiling.
%% prof_off(),
%% R.
-spec prof() -> 'ok'.
%% @doc Turns on profiling of all loaded modules.
prof() ->
lists:foreach(fun prof_module/1, mods()).
-spec prof_off() -> 'ok'.
%% @doc Turns off profiling of all loaded modules.
prof_off() ->
lists:foreach(fun prof_module_off/1, mods()).
-spec clear() -> 'ok'.
%% @doc Clears all counters.
clear() ->
lists:foreach(fun clear_module/1, mods()).
-spec res() -> [{mfa(), non_neg_integer()}].
%% @doc Returns a list of the numbers of calls to each profiled function.
%% The list is sorted with the most called function first.
res() ->
lists:reverse(lists:keysort(2, lists:flatten([res_module(M) || M <- mods()]))).
%% --------------------------------------------------------------------
-spec mods() -> [atom()].
%% @doc Returns a list of all loaded modules.
%@ --------------------------------------------------------------------
mods() ->
[Mod || {Mod,_} <- code:all_loaded()].
%% --------------------------------------------------------------------
-spec prof_module(atom()) -> 'ok'.
%% @doc Turns on profiling for given module.
%@ ____________________________________________________________________
prof_module(Mod) ->
Funs = Mod:module_info(functions),
lists:foreach(fun ({F,A}) -> catch hipe_bifs:call_count_on({Mod,F,A}) end,
Funs),
ok.
%% --------------------------------------------------------------------
-spec prof_module_off(atom()) -> 'ok'.
%% @doc Turns off profiling of the module Mod.
%@ --------------------------------------------------------------------
prof_module_off(Mod) ->
Funs = Mod:module_info(functions),
lists:foreach(fun ({F,A}) -> catch hipe_bifs:call_count_off({Mod,F,A}) end,
Funs),
ok.
%% --------------------------------------------------------------------
-spec clear_module(atom()) -> 'ok'.
%% @doc Clears the call counters for all functions in module Mod.
%@ --------------------------------------------------------------------
clear_module(Mod) ->
Funs = Mod:module_info(functions),
lists:foreach(fun ({F,A}) -> catch hipe_bifs:call_count_clear({Mod,F,A}) end,
Funs),
ok.
%% --------------------------------------------------------------------
-spec res_module(atom()) -> [{mfa(), non_neg_integer()}].
%% @doc Returns the number of profiled calls to each function (MFA)
%% in the module Mod.
%@ --------------------------------------------------------------------
res_module(Mod) ->
Fun = fun ({F,A}) when is_atom(F), is_integer(A) ->
MFA = {Mod,F,A},
{MFA, try hipe_bifs:call_count_get(MFA) of
N when is_integer(N) -> N;
false -> 0
catch
_:_ -> 0
end
}
end,
lists:reverse(lists:keysort(2, [Fun(FA) || FA <- Mod:module_info(functions)])).
-spec total_calls(atom()) -> non_neg_integer().
total_calls(Mod) ->
Funs = Mod:module_info(functions),
SumF = fun ({F,A}, Acc) ->
MFA = {Mod,F,A},
try hipe_bifs:call_count_get(MFA) of
N when is_integer(N) -> N+Acc;
false -> Acc
catch
_:_ -> Acc
end;
(_, Acc) -> Acc
end,
lists:foldl(SumF, 0, Funs).