%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Copyright (c) 2001 by Erik Johansson.  All Rights Reserved 
%% Time-stamp: <2002-09-13 15:32:45 richardc>
%% ====================================================================
%%  Filename : 	hipe_profile.erl
%%  Module   :	hipe_profile
%%  Purpose  :  
%%  History  :	* 2001-07-12 Erik Johansson (happi@csd.uu.se): 
%%               Created.
%%  CVS      :
%%              $Author: kostis $
%%              $Date: 2005/12/12 21:58:04 $
%%              $Revision: 1.4 $
%% ====================================================================
%%  Exports  :
%%
%%  @doc
%%  Notes    :  
%@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-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() -> [{Mod,Calls}]
%% @doc  Returns the number of calls per module currently 
%%       recordeed since hipe_bifs:call_count_on().
%%       The resulting lists is sorted with the most called
%%       module first.
mods_res() ->
  lists:reverse(lists:keysort(2,calls())).

%% @spec () -> [{mod,calls}]
%% @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 of profiling.
%%   prof_off(),
%%   R.

%% @spec () -> ok
%% @doc Turns on profiling of all loaded modules.
prof() ->
  mods(fun prof_module/1),ok.

%% @spec () -> ok
%% @doc Turns off profiling of all loaded modules.
prof_off() ->
  mods(fun prof_module_off/1),ok.

%% @spec () -> ok
%% @doc Clears all counters.
clear()->
  mods(fun clear_module/1),ok.

%% @spec () -> [{mfa(),calls()}]
%% @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(mods(fun res_module/1)))).

%% ____________________________________________________________________
%% @spec mods(F) -> [T]    
%%       F = (mod()) -> T
%%       T = term()
%% @doc  Applies F to all loaded modules. 
%@ ____________________________________________________________________
mods(F) ->
  [F(X) || X <- mods()].

%% ____________________________________________________________________
%% @spec () -> [mod()]    
%% @doc	 Returns a list of all loaded modules. 
%@ ____________________________________________________________________
mods() ->
  [element(1,X) || X <- code:all_loaded()].

%% ____________________________________________________________________
%% @spec (mod()) -> 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 (Mod) -> ok    
%%       Mod = mod()
%% @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 (Mod)->ok
%%       Mod = mod()
%% @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 (Mod)->[{MFA,calls()}]    
%%        Mod = mod()
%%        MFA = mfa()
%% @doc	  Returns the number of profiled calls to each function (MFA) 
%%        in the module Mod.
%@ ____________________________________________________________________

res_module(Mod) ->
  Funs = Mod:module_info(functions),
  lists:reverse(lists:keysort(2,lists:map(
    fun ({F,A}) ->
	MFA = {Mod,F,A},
	{MFA,case catch hipe_bifs:call_count_get(MFA) of 
	       N when is_integer(N) -> N; 
	       _ -> 0 end
	}
    end,
    Funs))).


total_calls(Mod) ->
  Funs = Mod:module_info(functions),
  Sum = fun({F,A},Acc) -> 
	    MFA = {Mod,F,A},
	    case catch hipe_bifs:call_count_get(MFA) of 
	      N when is_integer(N) -> N+Acc; 
	       _ -> Acc 
	    end;
	   (_,Acc) -> Acc
	end,
  lists:foldl(Sum,0,Funs).

