aboutsummaryrefslogtreecommitdiffstats
path: root/lib/percept/src/percept_analyzer.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/percept/src/percept_analyzer.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/percept/src/percept_analyzer.erl')
-rw-r--r--lib/percept/src/percept_analyzer.erl367
1 files changed, 367 insertions, 0 deletions
diff --git a/lib/percept/src/percept_analyzer.erl b/lib/percept/src/percept_analyzer.erl
new file mode 100644
index 0000000000..a5440e4cd2
--- /dev/null
+++ b/lib/percept/src/percept_analyzer.erl
@@ -0,0 +1,367 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-2009. 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%
+
+%% @doc Utility functions to operate on percept data. These functions should
+%% be considered experimental. Behaviour may change in future releases.
+
+-module(percept_analyzer).
+-export([
+ minmax/1,
+ waiting_activities/1,
+ activities2count/2,
+ activities2count/3,
+ activities2count2/2,
+ analyze_activities/2,
+ runnable_count/1,
+ runnable_count/2,
+ seconds2ts/2,
+ minmax_activities/2,
+ mean/1
+ ]).
+
+-include("percept.hrl").
+
+%%==========================================================================
+%%
+%% Interface functions
+%%
+%%==========================================================================
+
+
+%% @spec minmax([{X, Y}]) -> {MinX, MinY, MaxX, MaxY}
+%% X = number()
+%% Y = number()
+%% MinX = number()
+%% MinY = number()
+%% MaxX = number()
+%% MaxY = number()
+%% @doc Returns the min and max of a set of 2-dimensional numbers.
+
+minmax(Data) ->
+ Xs = [ X || {X,_Y} <- Data],
+ Ys = [ Y || {_X, Y} <- Data],
+ {lists:min(Xs), lists:min(Ys), lists:max(Xs), lists:max(Ys)}.
+
+%% @spec mean([number()]) -> {Mean, StdDev, N}
+%% Mean = float()
+%% StdDev = float()
+%% N = integer()
+%% @doc Calculates the mean and the standard deviation of a set of
+%% numbers.
+
+mean([]) -> {0, 0, 0};
+mean([Value]) -> {Value, 0, 1};
+mean(List) -> mean(List, {0, 0, 0}).
+
+mean([], {Sum, SumSquare, N}) ->
+ Mean = Sum / N,
+ StdDev = math:sqrt((SumSquare - Sum*Sum/N)/(N - 1)),
+ {Mean, StdDev, N};
+mean([Value | List], {Sum, SumSquare, N}) ->
+ mean(List, {Sum + Value, SumSquare + Value*Value, N + 1}).
+
+
+
+activities2count2(Acts, StartTs) ->
+ Start = inactive_start_states(Acts),
+ activities2count2(Acts, StartTs, Start, []).
+
+activities2count2([], _, _, Out) -> lists:reverse(Out);
+activities2count2([#activity{ id = Id, timestamp = Ts, state = active} | Acts], StartTs, {Proc,Port}, Out) when is_pid(Id) ->
+ activities2count2(Acts, StartTs, {Proc + 1, Port}, [{?seconds(Ts, StartTs), Proc + 1, Port}|Out]);
+activities2count2([#activity{ id = Id, timestamp = Ts, state = inactive} | Acts], StartTs, {Proc,Port}, Out) when is_pid(Id) ->
+ activities2count2(Acts, StartTs, {Proc - 1, Port}, [{?seconds(Ts, StartTs), Proc - 1, Port}|Out]);
+activities2count2([#activity{ id = Id, timestamp = Ts, state = active} | Acts], StartTs, {Proc,Port}, Out) when is_port(Id) ->
+ activities2count2(Acts, StartTs, {Proc, Port + 1}, [{?seconds(Ts, StartTs), Proc, Port + 1}|Out]);
+activities2count2([#activity{ id = Id, timestamp = Ts, state = inactive} | Acts], StartTs, {Proc,Port}, Out) when is_port(Id) ->
+ activities2count2(Acts, StartTs, {Proc, Port - 1}, [{?seconds(Ts, StartTs), Proc, Port - 1}|Out]).
+
+
+inactive_start_states(Acts) ->
+ D = activity_start_states(Acts, dict:new()),
+ dict:fold(fun
+ (K, inactive, {Procs, Ports}) when is_pid(K) -> {Procs + 1, Ports};
+ (K, inactive, {Procs, Ports}) when is_port(K) -> {Procs, Ports + 1};
+ (_, _, {Procs, Ports}) -> {Procs, Ports}
+ end, {0,0}, D).
+activity_start_states([], D) -> D;
+activity_start_states([#activity{id = Id, state = State}|Acts], D) ->
+ case dict:is_key(Id, D) of
+ true -> activity_start_states(Acts, D);
+ false -> activity_start_states(Acts, dict:store(Id, State, D))
+ end.
+
+
+
+
+%% @spec activities2count(#activity{}, timestamp()) -> Result
+%% Result = [{Time, ProcessCount, PortCount}]
+%% Time = float()
+%% ProcessCount = integer()
+%% PortCount = integer()
+%% @doc Calculate the resulting active processes and ports during
+%% the activity interval.
+%% Also checks active/inactive consistency.
+%% A task will always begin with an active state and end with an inactive state.
+
+activities2count(Acts, StartTs) when is_list(Acts) -> activities2count(Acts, StartTs, separated).
+
+activities2count(Acts, StartTs, Type) when is_list(Acts) -> activities2count_loop(Acts, {StartTs, {0,0}}, Type, []).
+
+activities2count_loop([], _, _, Out) -> lists:reverse(Out);
+activities2count_loop(
+ [#activity{ timestamp = Ts, id = Id, runnable_count = Rc} | Acts],
+ {StartTs, {Procs, Ports}}, separated, Out) ->
+
+ Time = ?seconds(Ts, StartTs),
+ case Id of
+ Id when is_port(Id) ->
+ Entry = {Time, Procs, Rc},
+ activities2count_loop(Acts, {StartTs, {Procs, Rc}}, separated, [Entry | Out]);
+ Id when is_pid(Id) ->
+ Entry = {Time, Rc, Ports},
+ activities2count_loop(Acts, {StartTs, {Rc, Ports}}, separated, [Entry | Out]);
+ _ ->
+ activities2count_loop(Acts, {StartTs,{Procs, Ports}}, separated, Out)
+ end;
+activities2count_loop(
+ [#activity{ timestamp = Ts, id = Id, runnable_count = Rc} | Acts],
+ {StartTs, {Procs, Ports}}, summated, Out) ->
+
+ Time = ?seconds(Ts, StartTs),
+ case Id of
+ Id when is_port(Id) ->
+ Entry = {Time, Procs + Rc},
+ activities2count_loop(Acts, {StartTs, {Procs, Rc}}, summated, [Entry | Out]);
+ Id when is_pid(Id) ->
+ Entry = {Time, Rc + Ports},
+ activities2count_loop(Acts, {StartTs, {Rc, Ports}}, summated, [Entry | Out])
+ end.
+
+%% @spec waiting_activities([#activity{}]) -> FunctionList
+%% FunctionList = [{Seconds, Mfa, {Mean, StdDev, N}}]
+%% Seconds = float()
+%% Mfa = mfa()
+%% Mean = float()
+%% StdDev = float()
+%% N = integer()
+%% @doc Calculates the time, both average and total, that a process has spent
+%% in a receive state at specific function. However, if there are multiple receives
+%% in a function it cannot differentiate between them.
+
+waiting_activities(Activities) ->
+ ListedMfas = waiting_activities_mfa_list(Activities, []),
+ Unsorted = lists:foldl(
+ fun (Mfa, MfaList) ->
+ {Total, WaitingTimes} = get({waiting_mfa, Mfa}),
+
+ % cleanup
+ erlang:erase({waiting_mfa, Mfa}),
+
+ % statistics of receive waiting places
+ Stats = mean(WaitingTimes),
+
+ [{Total, Mfa, Stats} | MfaList]
+ end, [], ListedMfas),
+ lists:sort(fun ({A,_,_},{B,_,_}) ->
+ if
+ A > B -> true;
+ true -> false
+ end
+ end, Unsorted).
+
+
+%% Generate lists of receive waiting times per mfa
+%% Out:
+%% ListedMfas = [mfa()]
+%% Intrisnic:
+%% get({waiting, mfa()}) ->
+%% [{waiting, mfa()}, {Total, [WaitingTime]})
+%% WaitingTime = float()
+
+waiting_activities_mfa_list([], ListedMfas) -> ListedMfas;
+waiting_activities_mfa_list([Activity|Activities], ListedMfas) ->
+ #activity{id = Pid, state = Act, timestamp = Time, where = MFA} = Activity,
+ case Act of
+ active ->
+ waiting_activities_mfa_list(Activities, ListedMfas);
+ inactive ->
+ % Want to know how long the wait is in a receive,
+ % it is given via the next activity
+ case Activities of
+ [] ->
+ [Info] = percept_db:select(information, Pid),
+ case Info#information.stop of
+ undefined ->
+ % get profile end time
+ Waited = ?seconds(
+ percept_db:select({system,stop_ts}),
+ Time);
+ Time2 ->
+ Waited = ?seconds(Time2, Time)
+ end,
+ case get({waiting_mfa, MFA}) of
+ undefined ->
+ put({waiting_mfa, MFA}, {Waited, [Waited]}),
+ [MFA | ListedMfas];
+ {Total, TimedMfa} ->
+ put({waiting_mfa, MFA}, {Total + Waited, [Waited | TimedMfa]}),
+ ListedMfas
+ end;
+ [#activity{timestamp=Time2, id = Pid, state = active} | _ ] ->
+ % Calculate waiting time
+ Waited = ?seconds(Time2, Time),
+ % Get previous entry
+
+ case get({waiting_mfa, MFA}) of
+ undefined ->
+ % add entry to list
+ put({waiting_mfa, MFA}, {Waited, [Waited]}),
+ waiting_activities_mfa_list(Activities, [MFA|ListedMfas]);
+ {Total, TimedMfa} ->
+ put({waiting_mfa, MFA}, {Total + Waited, [Waited | TimedMfa]}),
+ waiting_activities_mfa_list(Activities, ListedMfas)
+ end;
+ _ -> error
+ end
+ end.
+
+%% seconds2ts(Seconds, StartTs) -> TS
+%% In:
+%% Seconds = float()
+%% StartTs = timestamp()
+%% Out:
+%% TS = timestamp()
+
+%% @spec seconds2ts(float(), StartTs::{integer(),integer(),integer()}) -> timestamp()
+%% @doc Calculates a timestamp given a duration in seconds and a starting timestamp.
+
+seconds2ts(Seconds, {Ms, S, Us}) ->
+ % Calculate mega seconds integer
+ MsInteger = trunc(Seconds) div 1000000 ,
+
+ % Calculate the reminder for seconds
+ SInteger = trunc(Seconds),
+
+ % Calculate the reminder for micro seconds
+ UsInteger = trunc((Seconds - SInteger) * 1000000),
+
+ % Wrap overflows
+
+ UsOut = (UsInteger + Us) rem 1000000,
+ SOut = ((SInteger + S) + (UsInteger + Us) div 1000000) rem 1000000,
+ MsOut = (MsInteger+ Ms) + ((SInteger + S) + (UsInteger + Us) div 1000000) div 1000000,
+
+ {MsOut, SOut, UsOut}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Analyze interval for concurrency
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% @spec analyze_activities(integer(), [#activity{}]) -> [{integer(),#activity{}}]
+%% @hidden
+
+analyze_activities(Threshold, Activities) ->
+ RunnableCount = runnable_count(Activities, 0),
+ analyze_runnable_activities(Threshold, RunnableCount).
+
+
+%% runnable_count(Activities, StartValue) -> RunnableCount
+%% In:
+%% Activities = [activity()]
+%% StartValue = integer()
+%% Out:
+%% RunnableCount = [{integer(), activity()}]
+%% Purpose:
+%% Calculate the runnable count of a given interval of generic
+%% activities.
+
+%% @spec runnable_count([#activity{}]) -> [{integer(),#activity{}}]
+%% @hidden
+
+runnable_count(Activities) ->
+ Threshold = runnable_count_threshold(Activities),
+ runnable_count(Activities, Threshold, []).
+
+runnable_count_threshold(Activities) ->
+ CountedActs = runnable_count(Activities, 0),
+ Counts = [C || {C, _} <- CountedActs],
+ Min = lists:min(Counts),
+ 0 - Min.
+%% @spec runnable_count([#activity{}],integer()) -> [{integer(),#activity{}}]
+%% @hidden
+
+runnable_count(Activities, StartCount) when is_integer(StartCount) ->
+ runnable_count(Activities, StartCount, []).
+runnable_count([], _ , Out) ->
+ lists:reverse(Out);
+runnable_count([A | As], PrevCount, Out) ->
+ case A#activity.state of
+ active ->
+ runnable_count(As, PrevCount + 1, [{PrevCount + 1, A} | Out]);
+ inactive ->
+ runnable_count(As, PrevCount - 1, [{PrevCount - 1, A} | Out])
+ end.
+
+%% In:
+%% Threshold = integer(),
+%% RunnableActivities = [{Rc, activity()}]
+%% Rc = integer()
+
+analyze_runnable_activities(Threshold, RunnableActivities) ->
+ analyze_runnable_activities(Threshold, RunnableActivities, []).
+
+analyze_runnable_activities( _z, [], Out) ->
+ lists:reverse(Out);
+analyze_runnable_activities(Threshold, [{Rc, Act} | RunnableActs], Out) ->
+ if
+ Rc =< Threshold ->
+ analyze_runnable_activities(Threshold, RunnableActs, [{Rc,Act} | Out]);
+ true ->
+ analyze_runnable_activities(Threshold, RunnableActs, Out)
+ end.
+
+%% minmax_activity(Activities, Count) -> {Min, Max}
+%% In:
+%% Activities = [activity()]
+%% InitialCount = non_neg_integer()
+%% Out:
+%% {Min, Max}
+%% Min = non_neg_integer()
+%% Max = non_neg_integer()
+%% Purpose:
+%% Minimal and maximal activity during an activity interval.
+%% Initial activity count needs to be supplied.
+
+%% @spec minmax_activities([#activity{}], integer()) -> {integer(), integer()}
+%% @doc Calculates the minimum and maximum of runnable activites (processes
+% and ports) during the interval of reffered by the activity list.
+
+minmax_activities(Activities, Count) ->
+ minmax_activities(Activities, Count, {Count, Count}).
+minmax_activities([], _, Out) ->
+ Out;
+minmax_activities([A|Acts], Count, {Min, Max}) ->
+ case A#activity.state of
+ active ->
+ minmax_activities(Acts, Count + 1, {Min, lists:max([Count + 1, Max])});
+ inactive ->
+ minmax_activities(Acts, Count - 1, {lists:min([Count - 1, Min]), Max})
+ end.