1%% -*- erlang-indent-level: 2 -*-
2%%
3%% Licensed under the Apache License, Version 2.0 (the "License");
4%% you may not use this file except in compliance with the License.
5%% You may obtain a copy of the License at
6%%
7%%     http://www.apache.org/licenses/LICENSE-2.0
8%%
9%% Unless required by applicable law or agreed to in writing, software
10%% distributed under the License is distributed on an "AS IS" BASIS,
11%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12%% See the License for the specific language governing permissions and
13%% limitations under the License.
14%%
15%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16%% Copyright (c) 2001 by Erik Johansson.  All Rights Reserved
17%% ====================================================================
18%%  Module   :	hipe_profile
19%%  Purpose  :
20%%  History  :	* 2001-07-12 Erik Johansson (happi@it.uu.se): Created.
21%% ====================================================================
22%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
23
24-module(hipe_profile).
25
26-export([%% profile/1, mods_profile/1,
27	 prof/0, prof_off/0, clear/0, res/0,
28	 mods_res/0,
29	 %% clear_module/1, res_module/1,
30	 prof_module/1, prof_module_off/1]).
31
32%% %% @spec mods_profile(F) -> [{mod(),calls()}]
33%% %%       F = () -> term()
34%% %%       mod()  = atom()
35%% %%       calls()= integer()
36%% %%
37%% %% @doc  Returns the number of calls per module generated by
38%% %%       applying F().
39%% %%       The resulting lists is sorted with the most called
40%% %%       module first.
41%% mods_profile(F) ->
42%%   F(),
43%%   prof(),
44%%   clear(),
45%%   F(),
46%%   R = mods_res(),
47%%   prof_off(),
48%%   R.
49
50-spec mods_res() -> [{atom(), non_neg_integer()}].
51%% @doc  Returns the number of calls per module currently
52%%       recordeed since hipe_bifs:call_count_on().
53%%       The resulting list is sorted with the most called
54%%       module first.
55mods_res() ->
56  lists:reverse(lists:keysort(2, calls())).
57
58-spec calls() -> [{atom(), non_neg_integer()}].
59%% @doc  Returns the number of calls per module currently
60%%       recordeed since hipe_bifs:call_count_on().
61calls() ->
62  [{Mod, total_calls(Mod)} || Mod <- mods(),
63			      total_calls(Mod) > 1,
64			      Mod =/= hipe_profile].
65
66%% %% @spec profile(F) -> [{mfa(),calls()}]
67%% %%       F = ()    -> term()
68%% %%       mfa()      = {mod(),function(),arity()}
69%% %%       function() = atom()
70%% %%       arity()    = intger()
71%% %%
72%% %% @doc  Returns the number of calls per module generated by
73%% %%       applying F().
74%% %%       The resulting lists is sorted with the most called
75%% %%       module first.
76%% profile(F) ->
77%%   %% Make sure all code is loaded.
78%%   F(),
79%%   %% Turn profiling on.
80%%   prof(),
81%%   clear(),
82%%   %% Apply the closure to profile.
83%%   F(),
84%%   %% Get result.
85%%   R = res(),
86%%   %% Turn off profiling.
87%%   prof_off(),
88%%   R.
89
90-spec prof() -> 'ok'.
91%% @doc Turns on profiling of all loaded modules.
92prof() ->
93  lists:foreach(fun prof_module/1, mods()).
94
95-spec prof_off() -> 'ok'.
96%% @doc Turns off profiling of all loaded modules.
97 prof_off() ->
98  lists:foreach(fun prof_module_off/1, mods()).
99
100-spec clear() -> 'ok'.
101%% @doc Clears all counters.
102clear() ->
103  lists:foreach(fun clear_module/1, mods()).
104
105-spec res() -> [{mfa(), non_neg_integer()}].
106%% @doc Returns a list of the numbers of calls to each profiled function.
107%%      The list is sorted with the most called function first.
108res() ->
109  lists:reverse(lists:keysort(2, lists:flatten([res_module(M) || M <- mods()]))).
110
111%% --------------------------------------------------------------------
112-spec mods() -> [atom()].
113%% @doc	 Returns a list of all loaded modules.
114%@ --------------------------------------------------------------------
115
116mods() ->
117  [Mod || {Mod,_} <- code:all_loaded()].
118
119%% --------------------------------------------------------------------
120-spec prof_module(atom()) -> 'ok'.
121%% @doc	 Turns on profiling for given module.
122%@ ____________________________________________________________________
123
124prof_module(Mod) ->
125  Funs = Mod:module_info(functions),
126  lists:foreach(fun ({F,A}) -> catch hipe_bifs:call_count_on({Mod,F,A}) end,
127		Funs),
128  ok.
129
130%% --------------------------------------------------------------------
131-spec prof_module_off(atom()) -> 'ok'.
132%% @doc	 Turns off profiling of the module Mod.
133%@ --------------------------------------------------------------------
134
135prof_module_off(Mod) ->
136  Funs = Mod:module_info(functions),
137  lists:foreach(fun ({F,A}) -> catch hipe_bifs:call_count_off({Mod,F,A}) end,
138		Funs),
139  ok.
140
141%% --------------------------------------------------------------------
142-spec clear_module(atom()) -> 'ok'.
143%% @doc  Clears the call counters for all functions in module Mod.
144%@ --------------------------------------------------------------------
145
146clear_module(Mod) ->
147  Funs = Mod:module_info(functions),
148  lists:foreach(fun ({F,A}) -> catch hipe_bifs:call_count_clear({Mod,F,A}) end,
149		Funs),
150  ok.
151
152%% --------------------------------------------------------------------
153-spec res_module(atom()) -> [{mfa(), non_neg_integer()}].
154%% @doc	  Returns the number of profiled calls to each function (MFA)
155%%        in the module Mod.
156%@ --------------------------------------------------------------------
157
158res_module(Mod) ->
159  Fun = fun ({F,A}) when is_atom(F), is_integer(A) ->
160	    MFA = {Mod,F,A},
161	    {MFA, try hipe_bifs:call_count_get(MFA) of
162		    N when is_integer(N) -> N;
163		    false -> 0
164		  catch
165		    _:_ -> 0
166		  end
167	    }
168	end,
169  lists:reverse(lists:keysort(2, [Fun(FA) || FA <- Mod:module_info(functions)])).
170
171-spec total_calls(atom()) -> non_neg_integer().
172
173total_calls(Mod) ->
174  Funs = Mod:module_info(functions),
175  SumF = fun ({F,A}, Acc) ->
176	    MFA = {Mod,F,A},
177	    try hipe_bifs:call_count_get(MFA) of
178	      N when is_integer(N) -> N+Acc;
179	      false -> Acc
180	    catch
181	      _:_ -> Acc
182	    end;
183	     (_, Acc) -> Acc
184	end,
185  lists:foldl(SumF, 0, Funs).
186