1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22%%-----------------------------------------------------------------
23%% Purpose: Functions for handling statistic counters
24%%-----------------------------------------------------------------
25-module(megaco_stats).
26
27
28%%-----------------------------------------------------------------
29%% Application internal exports
30%%-----------------------------------------------------------------
31
32-export([init/1, init/2]).
33
34-export([inc/2, inc/3, inc/4]).
35
36-export([get_stats/1, get_stats/2, get_stats/3,
37	 reset_stats/1, reset_stats/2]).
38
39%% -include_lib("megaco/include/megaco.hrl").
40
41
42%%-----------------------------------------------------------------
43%% Func: init/1, init/2
44%% Description: Initiate the statistics. Creates the stats table
45%%              and the global counters.
46%%-----------------------------------------------------------------
47init(Name) ->
48    init(Name, []).
49
50init(Name, GlobalCounters) ->
51    ets:new(Name, [public, named_table, {keypos, 1}]),
52    ets:insert(Name, {global_counters, GlobalCounters}),
53    create_global_snmp_counters(Name, GlobalCounters).
54
55
56create_global_snmp_counters(_Name, []) ->
57    ok;
58create_global_snmp_counters(Name, [Counter|Counters]) ->
59    ets:insert(Name, {Counter, 0}),
60    create_global_snmp_counters(Name, Counters).
61
62
63%%-----------------------------------------------------------------
64%% Func: inc/2, inc/3, inc/4
65%% Description: Increment counter value. Default increment is one
66%%              (1).
67%%-----------------------------------------------------------------
68inc(Tab, GlobalCnt) when is_atom(GlobalCnt) ->
69    inc(Tab, GlobalCnt, 1).
70
71inc(Tab, GlobalCnt, Incr)
72  when is_atom(GlobalCnt) andalso (is_integer(Incr) andalso (Incr > 0)) ->
73    do_inc(Tab, GlobalCnt, Incr);
74inc(Tab, Handle, Cnt)
75  when is_atom(Cnt) ->
76    inc(Tab, Handle, Cnt, 1).
77
78inc(Tab, Handle, Cnt, Incr)
79  when is_atom(Cnt) andalso (is_integer(Incr) andalso (Incr > 0)) ->
80    Key = {Handle, Cnt},
81    do_inc(Tab, Key, Incr).
82
83do_inc(Tab, Key, Incr) ->
84    case (catch ets:update_counter(Tab, Key, Incr)) of
85        {'EXIT', {badarg, _Reason}} ->
86            ets:insert(Tab, {Key, Incr}),
87	    Incr;
88        Val ->
89            Val
90    end.
91
92
93%%-----------------------------------------------------------------
94%% Func: get_stats/1, get_stats/2, get_stats/3
95%% Description: Get statistics
96%%-----------------------------------------------------------------
97get_stats(Ets) ->
98    Handles = get_handles_and_global_counters(Ets),
99    (catch do_get_stats(Ets, Handles, [])).
100
101do_get_stats(_Ets, [], Acc) ->
102    {ok, lists:reverse(Acc)};
103do_get_stats(Ets, [Handle|Handles], Acc) ->
104    case get_stats(Ets, Handle) of
105	{ok, Stats} ->
106	    do_get_stats(Ets, Handles, [{Handle, Stats}|Acc]);
107	{error, Reason} ->
108	    throw({error, Reason})
109    end.
110
111get_stats(Ets, GlobalCounter) when is_atom(GlobalCounter) ->
112    case (catch ets:lookup(Ets, GlobalCounter)) of
113	[{GlobalCounter, Val}] ->
114	    {ok, Val};
115	[] ->
116	    {error, {no_such_counter, GlobalCounter}}
117    end;
118
119get_stats(Ets, Handle) ->
120    case (catch ets:match(Ets, {{Handle, '$1'},'$2'})) of
121	CounterVals when is_list(CounterVals) ->
122	    {ok, [{Counter, Val} || [Counter, Val] <- CounterVals]};
123	Other ->
124	    {error, {unexpected_result, Other}}
125    end.
126
127
128get_stats(Ets, Handle, Counter) when is_atom(Counter) ->
129    Key = {Handle, Counter},
130    case (catch ets:lookup(Ets, Key)) of
131	[{Key, Val}] ->
132	    {ok, Val};
133	_ ->
134	    {error, {undefined_counter, Counter}}
135    end.
136
137
138%%-----------------------------------------------------------------
139%% Funcs: reset_stats/1, reset_stats/2
140%% Description: Reset statistics
141%%-----------------------------------------------------------------
142reset_stats(Ets) ->
143    Handles = get_handles_and_global_counters(Ets),
144    (catch do_reset_stats(Ets, Handles, [])).
145
146do_reset_stats(_Ets, [], Acc) ->
147    {ok, lists:reverse(Acc)};
148do_reset_stats(Ets, [Handle|Handles], Acc) ->
149    case reset_stats(Ets, Handle) of
150	{ok, OldStats} ->
151	    do_reset_stats(Ets, Handles, [{Handle, OldStats}|Acc]);
152	{error, Reason} ->
153	    throw({error, Reason})
154    end.
155
156reset_stats(Ets, GlobalCounter) when is_atom(GlobalCounter) ->
157    %% First get the current value of the counter
158    case (catch ets:lookup(Ets, GlobalCounter)) of
159	[{GlobalCounter, Val}] ->
160	    ets:insert(Ets, {GlobalCounter, 0}),
161	    {ok, Val};
162	[] -> %% Oooups
163	    {error, {no_such_counter, GlobalCounter}}
164    end;
165
166reset_stats(Ets, Handle) ->
167    case (catch ets:match(Ets, {{Handle, '$1'},'$2'})) of
168	CounterVals when is_list(CounterVals) ->
169	    CVs = [{Counter, Val} || [Counter, Val] <- CounterVals],
170	    reset_stats(Ets, Handle, CVs),
171	    {ok, CVs};
172	Other ->
173	    {error, {unexpected_result, Other}}
174    end.
175
176reset_stats(_Ets, _Handle, []) ->
177    ok;
178reset_stats(Ets, Handle, [{Counter, _}|CVs]) ->
179    ets:insert(Ets, {{Handle, Counter}, 0}),
180    reset_stats(Ets, Handle, CVs).
181
182
183
184%%-----------------------------------------------------------------
185%% Internal functions
186%%-----------------------------------------------------------------
187get_handles_and_global_counters(Ets) ->
188    GlobalCounters =
189	case ets:lookup(Ets, global_counters) of
190	    [{global_counters, GC}] ->
191		GC;
192	    [] ->
193		[]
194	end,
195    L1 = ets:match(Ets, {{'$1', '_'}, '_'}),
196    GlobalCounters ++
197	lists:sort([Handle || [Handle] <- remove_duplicates(L1, [])]).
198
199remove_duplicates([], L) ->
200    L;
201remove_duplicates([H|T], L) ->
202    case lists:member(H,T) of
203        true ->
204            remove_duplicates(T, L);
205        false ->
206            remove_duplicates(T, [H|L])
207    end.
208
209