%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2008-2010. 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% %% %% %%---------------------------------------------------------------------- %% megaco_profile: Utility module used for megaco profiling %%---------------------------------------------------------------------- -module(megaco_profile). -export([profile/2]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Execute Fun and profile it with fprof. profile(Slogan, Fun) when is_function(Fun) -> Pids = [self()], profile(Slogan, Fun, Pids). profile(Slogan, Fun, Pids) -> TraceFile = lists:concat(["profile_", Slogan, "-fprof.trace"]), DestFile = lists:concat(["profile_", Slogan, ".fprof"]), TreeFile = lists:concat(["profile_", Slogan, ".calltree"]), erlang:garbage_collect(), {ok, _Pid} = fprof:start(), TraceOpts = [start, {cpu_time, false}, {procs, Pids}, {file, TraceFile} ], ok = fprof:trace(TraceOpts), Res = (catch Fun()), ok = fprof:trace(stop), ok = fprof:profile([{file, TraceFile}]), ok = fprof:analyse([{dest, DestFile}]), ok = fprof:stop(), ok = file:delete(TraceFile), reformat_total(DestFile, TreeFile), Res. reformat_total(FromFile, ToFile) -> {ok, ConsultedFromFile} = file:consult(FromFile), [_AnalysisOpts, [Totals] | Terms] = ConsultedFromFile, {totals, _, TotalAcc, _} = Totals, {ok, Fd} = file:open(ToFile, [write, raw]), Indent = "", log(Fd, Indent, TotalAcc, Totals), Processes = split_processes(Terms, [], []), Reformat = fun(P) -> reformat_process(Fd, " " ++ Indent, TotalAcc, P) end, lists:foreach(Reformat, Processes), file:close(Fd). split_processes([H | T], ProcAcc, TotalAcc) -> if is_tuple(H) -> split_processes(T, [H | ProcAcc], TotalAcc); is_list(H), ProcAcc =:= [] -> split_processes(T, [H], TotalAcc); is_list(H) -> split_processes(T, [H], [lists:reverse(ProcAcc) | TotalAcc]) end; split_processes([], [], TotalAcc) -> lists:reverse(TotalAcc); split_processes([], ProcAcc, TotalAcc) -> lists:reverse([lists:reverse(ProcAcc) | TotalAcc]). reformat_process(Fd, Indent, TotalAcc, Terms) -> case Terms of [[{ProcLabel, _, _, _}] | All] -> ok; [[{ProcLabel,_,_,_} | _] | All] -> ok end, [{_, {TopKey, TopCnt, TopAcc, TopOwn}, _} | _] = All, Process = {ProcLabel, TopCnt, TopAcc, TopOwn}, log(Fd, Indent, TotalAcc, Process), reformat_calls(Fd, " " ++ Indent, TotalAcc, TopKey, All, []). reformat_calls(Fd, Indent, TotalAcc, Key, Terms, Stack) -> {_CalledBy, Current, Calls} = find(Key, Terms), log(Fd, Indent, TotalAcc, Current), case lists:member(Key, Stack) of true -> ok; false -> case Key of {io_lib, _, _} -> ok; {disk_log, _, _} -> ok; {lists, flatten, _} -> ok; {lists, keysort, _} -> ok; _ -> Fun = fun({NextKey, _, _, _}) -> reformat_calls(Fd, " " ++ Indent, TotalAcc, NextKey, Terms, [Key | Stack]) end, lists:foreach(Fun, Calls) end end. find(Key, [{_, {Key, _, _, _}, _} = H | _]) -> H; find(Key, [{_, {_, _, _, _}, _} | T]) -> find(Key, T). log(Fd, Indent, Total, {Label, Cnt, Acc, Own}) -> Percent = case Acc of undefined -> 100; _ -> trunc((lists:max([Acc, Own]) * 100) / Total) end, Label2 = io_lib:format("~p", [Label]), IoList = io_lib:format("~s~p% ~s \t~p \t~p \t~p\n", [Indent, Percent, Label2, Cnt, trunc(Acc), trunc(Own)]), file:write(Fd, IoList).