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