summaryrefslogtreecommitdiff
path: root/src/dynomite_prof.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/dynomite_prof.erl')
-rw-r--r--src/dynomite_prof.erl164
1 files changed, 164 insertions, 0 deletions
diff --git a/src/dynomite_prof.erl b/src/dynomite_prof.erl
new file mode 100644
index 00000000..80c4b5b7
--- /dev/null
+++ b/src/dynomite_prof.erl
@@ -0,0 +1,164 @@
+%%%-------------------------------------------------------------------
+%%% File: dynomite_prof.erl
+%%% @author Cliff Moon <> []
+%%% @copyright 2009 Cliff Moon
+%%% @doc
+%%%
+%%% @end
+%%%
+%%% @since 2009-02-15 by Cliff Moon
+%%%-------------------------------------------------------------------
+-module(dynomite_prof).
+-author('cliff@powerset.com').
+
+-behaviour(gen_server).
+
+%% API
+-export([start_link/0, start_prof/1, stop_prof/1, stats/1, averages/0, balance_prof/0]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-record(state, {ets,balance}).
+
+-record(profile, {name, count, sum}).
+
+%%====================================================================
+%% API
+%%====================================================================
+%%--------------------------------------------------------------------
+%% @spec start_link() -> {ok,Pid} | ignore | {error,Error}
+%% @doc Starts the server
+%% @end
+%%--------------------------------------------------------------------
+start_link() ->
+ gen_server:start_link({local, dynomite_prof}, ?MODULE, [], []).
+
+stats(Id) ->
+ gen_server:call(dynomite_prof, {stats, Id}).
+
+balance_prof() ->
+ gen_server:cast(dynomite_prof, {balance, self(), lib_misc:now_float()}).
+
+start_prof(Id) ->
+ gen_server:cast(dynomite_prof, {start, self(), Id, lib_misc:now_float()}).
+
+stop_prof(Id) ->
+ gen_server:cast(dynomite_prof, {stop, self(), Id, lib_misc:now_float()}).
+
+averages() ->
+ gen_server:call(dynomite_prof, averages).
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% @spec init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% @doc Initiates the server
+%% @end
+%%--------------------------------------------------------------------
+init([]) ->
+ Tid = ets:new(profiling, [set, {keypos, 2}]),
+ Bal = ets:new(balance, [set]),
+ {ok, #state{ets=Tid, balance=Bal}}.
+
+%%--------------------------------------------------------------------
+%% @spec
+%% handle_call(Request, From, State) -> {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} |
+%% {stop, Reason, State}
+%% @doc Handling call messages
+%% @end
+%%--------------------------------------------------------------------
+handle_call({stats, Id}, _From, State = #state{ets=Ets}) ->
+ Reply = ets:lookup(Ets, Id),
+ {reply, Reply, State};
+
+handle_call(table, _From, State = #state{ets=Ets}) ->
+ {reply, Ets, State};
+
+handle_call(averages, _From, State = #state{ets=Ets,balance=Bal}) ->
+ Avgs = ets:foldl(fun(#profile{name=Name,count=Count,sum=Sum}, List) ->
+ [{Name, Sum/Count}|List]
+ end, [], Ets),
+ {_, MaxCount} = ets:foldl(fun
+ ({Pid, Count}, {_P, M}) when Count > M -> {Pid, Count};
+ (_, {P, M}) -> {P, M}
+ end, {pid, 0}, Bal),
+ Balances = ets:foldl(fun({Pid, Count}, List) ->
+ [{Pid, Count / MaxCount} | List]
+ end, [], Bal),
+ {reply, [Balances, Avgs], State}.
+
+%%--------------------------------------------------------------------
+%% @spec handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% @doc Handling cast messages
+%% @end
+%%--------------------------------------------------------------------
+handle_cast({balance, Pid, _Time}, State = #state{balance=Ets}) ->
+ case ets:lookup(Ets, Pid) of
+ [] -> ets:insert(Ets, {Pid, 1});
+ [{Pid, Count}] -> ets:insert(Ets, {Pid, Count+1})
+ end,
+ {noreply, State};
+
+handle_cast({start, Pid, Id, Time}, State = #state{ets=_Ets}) ->
+ put({Pid,Id}, Time),
+ {noreply, State};
+
+handle_cast({stop, Pid, Id, Time}, State = #state{ets=Ets}) ->
+ case get({Pid, Id}) of
+ undefined -> ok;
+ OldTime ->
+ erase({Pid, Id}),
+ increment_time(Ets, Time-OldTime, Id)
+ end,
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @spec handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% @doc Handling all non call/cast messages
+%% @end
+%%--------------------------------------------------------------------
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @spec terminate(Reason, State) -> void()
+%% @doc This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any necessary
+%% cleaning up. When it returns, the gen_server terminates with Reason.
+%% The return value is ignored.
+%% @end
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% @doc Convert process state when code is changed
+%% @end
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+increment_time(Ets, Time, Id) ->
+ case ets:lookup(Ets, Id) of
+ [] -> ets:insert(Ets, #profile{name=Id,count=1,sum=Time});
+ [#profile{name=Id,count=Count,sum=Sum}] -> ets:insert(Ets, #profile{name=Id,count=Count+1,sum=Sum+Time})
+ end.