1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2019-2021. 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-module(megaco_test_global_sys_monitor).
22
23-export([start/0, stop/0,
24         reset_events/0,
25         events/0,
26         log/1]).
27-export([init/1]).
28
29-include("megaco_test_lib.hrl").
30
31-define(NAME, ?MODULE).
32
33
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35
36start() ->
37    Parent = self(),
38    proc_lib:start(?MODULE, init, [Parent]).
39
40stop() ->
41    cast(stop).
42
43%% This does not reset the global counter but the "collector"
44%% See events for more info.
45reset_events() ->
46    call(reset_events).
47
48events() ->
49    events(infinity).
50
51events(Timeout) when (Timeout =:= infinity) orelse
52                     (is_integer(Timeout) andalso (Timeout > 0)) ->
53    call(events, Timeout).
54
55log(Event) ->
56    cast({node(), Event}).
57
58
59%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
60
61init(Parent) ->
62    process_flag(priority, high),
63    case global:register_name(?NAME, self()) of
64        yes ->
65            info_msg("Starting as ~p (on ~p)", [self(), node()]),
66            proc_lib:init_ack(Parent, {ok, self()}),
67            loop(#{parent => Parent, ev_cnt => 0, evs => []});
68        no ->
69            warning_msg("Already started", []),
70            proc_lib:init_ack(Parent, {error, already_started}),
71            exit(normal)
72    end.
73
74
75%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76
77loop(State) ->
78    receive
79        {?MODULE, stop} ->
80            warning_msg("Stopping with ~w events counted",
81                        [maps:get(ev_cnt, State)]),
82            exit(normal);
83
84        {?MODULE, Ref, From, reset_events} ->
85            TotEvCnt = maps:get(ev_cnt, State),
86            EvCnt    = length(maps:get(evs, State)),
87            info_msg("Reset events when"
88                     "~n   Total Number of Events:   ~p"
89                     "~n   Current Number of Events: ~p",
90                     [TotEvCnt, EvCnt]),
91            From ! {?MODULE, Ref, {ok, {TotEvCnt, EvCnt}}},
92            loop(State#{evs => []});
93
94        {?MODULE, Ref, From, events} ->
95            Evs = maps:get(evs, State),
96            From ! {?MODULE, Ref, lists:reverse(Evs)},
97            loop(State);
98
99        {?MODULE, {Node, Event}} ->
100            State2 = process_event(State, Node, Event),
101            loop(State2);
102
103        {nodedown = Event, Node} ->
104            State2 = process_event(State, Node, Event),
105            loop(State2);
106
107        _ ->
108            loop(State)
109    end.
110
111
112%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
113
114process_event(State, Node, {Pid, TS, Tag, Info}) ->
115    process_system_event(State, Node, Pid, TS, Tag, Info);
116
117process_event(State, Node, {TS, starting}) ->
118    FTS = megaco:format_timestamp(TS),
119    info_msg("System Monitor starting on node ~p at ~s", [Node, FTS]),
120    if
121        (Node =/= node()) ->
122            erlang:monitor_node(Node, true);
123        true ->
124            ok
125    end,
126    State;
127
128process_event(State, Node, {TS, stopping}) ->
129    FTS = ?FTS(TS),
130    info_msg("System Monitor stopping on node ~p at ~s", [Node, FTS]),
131    if
132        (Node =/= node()) ->
133            erlang:monitor_node(Node, false);
134        true ->
135            ok
136    end,
137    State;
138
139process_event(State, Node, {TS, already_started}) ->
140    FTS = megaco:format_timestamp(TS),
141    info_msg("System Monitor already started on node ~p at ~s", [Node, FTS]),
142    State;
143
144process_event(State, Node, nodedown) ->
145    info_msg("Node ~p down", [Node]),
146    State;
147
148process_event(State, Node, Event) ->
149    warning_msg("Received unknown event from node ~p:"
150                "~n   ~p", [Node, Event]),
151    State.
152
153
154%% System Monitor events
155%% We only *count* system events
156process_system_event(#{ev_cnt := Cnt, evs := Evs} = State,
157                     Node, Pid, TS, long_gc = Ev, Info) ->
158    print_system_event(f("Long GC (~w)", [length(Evs)]), Node, Pid, TS, Info),
159    State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]};
160process_system_event(#{ev_cnt := Cnt, evs := Evs} = State,
161                     Node, Pid, TS, long_schedule = Ev, Info) ->
162    print_system_event(f("Long Schedule (~w)", [length(Evs)]), Node, Pid, TS, Info),
163    State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]};
164process_system_event(#{ev_cnt := Cnt, evs := Evs} = State,
165                     Node, Pid, TS, large_heap = Ev, Info) ->
166    print_system_event(f("Large Heap (~w)", [length(Evs)]), Node, Pid, TS, Info),
167    State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]};
168process_system_event(#{ev_cnt := Cnt, evs := Evs} = State,
169                     Node, Pid, TS, busy_port = Ev, Info) ->
170    print_system_event(f("Busy port (~w)", [length(Evs)]), Node, Pid, TS, Info),
171    State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]};
172process_system_event(#{ev_cnt := Cnt, evs := Evs} = State,
173                     Node, Pid, TS, busy_dist_port = Ev, Info) ->
174    print_system_event(f("Busy dist port (~w)", [length(Evs)]),
175                       Node, Pid, TS, Info),
176    State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]};
177
178%% And everything else
179process_system_event(State, Node, Pid, TS, Tag, Info) ->
180    Pre = f("Unknown Event '~p'", [Tag]),
181    print_system_event(Pre, Node, Pid, TS, Info),
182    State.
183
184
185print_system_event(Pre, Node, Pid, TS, Info) ->
186    FTS = megaco:format_timestamp(TS),
187    warning_msg("~s from ~p (~p) at ~s:"
188                "~n   ~p", [Pre, Node, Pid, FTS, Info]).
189
190f(F, A) ->
191    lists:flatten(io_lib:format(F, A)).
192
193
194%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
195
196cast(Msg) ->
197    try global:send(?NAME, {?MODULE, Msg}) of
198        Pid when is_pid(Pid) ->
199            ok
200    catch
201        C:E:_ ->
202            {error, {catched, C, E}}
203    end.
204
205call(Req) ->
206    call(Req, infinity).
207
208call(Req, Timeout) ->
209    Ref = make_ref(),
210    try global:send(?NAME, {?MODULE, Ref, self(), Req}) of
211        Pid when is_pid(Pid) ->
212            receive
213                {?MODULE, Ref, Rep} ->
214                    Rep
215            after Timeout ->
216                    {error, timeout}
217            end
218    catch
219        C:E:_ ->
220            {error, {catched, C, E}}
221    end.
222
223
224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
225
226info_msg(F, A) ->
227    error_logger:info_msg(format_msg(F, A), []).
228
229warning_msg(F, A) ->
230    error_logger:warning_msg(format_msg(F, A), []).
231
232
233format_msg(F, A) ->
234    f("~n" ++
235          "****** MEGACO TEST GLOBAL SYSTEM MONITOR ******~n~n" ++
236          F ++
237          "~n~n",
238      A).
239
240