1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-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-module(memsup_SUITE).
21-include_lib("common_test/include/ct.hrl").
22
23%% Test server specific exports
24-export([all/0, suite/0]).
25-export([init_per_suite/1, end_per_suite/1]).
26-export([init_per_testcase/2, end_per_testcase/2]).
27
28%% Test cases
29-export([api/1, alarm1/1, alarm2/1, process/1]).
30-export([config/1, timeout/1, unavailable/1, port/1]).
31-export([otp_5910/1, improved_system_memory_data/1]).
32
33
34-define(SYSTEM_MEMORY_DATA_TAGS,
35        [total_memory,
36         free_memory,
37         system_total_memory,
38         largest_free,
39         number_of_free,
40         free_swap,
41         total_swap,
42         cached_memory,
43         buffered_memory,
44         shared_memory]).
45
46-define(IMPROVED_SYSTEM_MEMORY_DATA_TAGS,
47        [available_memory | ?SYSTEM_MEMORY_DATA_TAGS]).
48
49
50init_per_suite(Config) when is_list(Config) ->
51    ok = application:start(os_mon),
52    Config.
53
54end_per_suite(Config) when is_list(Config) ->
55    ok = application:stop(os_mon),
56    Config.
57
58init_per_testcase(_Case, Config) ->
59    Config.
60
61end_per_testcase(_Case, _Config) ->
62    ok.
63
64suite() ->
65    [{ct_hooks,[ts_install_cth]},
66     {timetrap,{minutes,1}}].
67
68all() ->
69    All = case test_server:os_type() of
70              {unix, sunos} ->
71                  [api, alarm1, alarm2, process, config, timeout,
72                   unavailable, port];
73              {unix, linux} ->
74                  [api, alarm1, alarm2, process, timeout];
75              _OS -> [api, alarm1, alarm2, process]
76          end,
77    Bugs = [otp_5910],
78    All ++ Bugs ++ [improved_system_memory_data].
79
80
81%% Test of API functions
82api(Config) when is_list(Config) ->
83
84    %% get_memory_data()
85    RegMemData = memsup:get_memory_data(),
86    case RegMemData of
87        {TotMem, AllBytes, {Pid, PidBytes}} when is_integer(TotMem),
88                                                 is_integer(AllBytes),
89                                                 is_pid(Pid),
90                                                 is_integer(PidBytes) ->
91            ok;
92        {0, 0, _WorstPid} ->
93            ct:fail(first_data_collection_failed);
94        _ ->
95            ct:fail({bad_return, RegMemData})
96    end,
97
98    %% get_system_memory_data()
99    ExtMemData = memsup:get_system_memory_data(),
100    Tags = ?SYSTEM_MEMORY_DATA_TAGS,
101
102    true = lists:all(fun({Tag,Value}) when is_atom(Tag),
103                                           is_integer(Value) ->
104                             lists:member(Tag, Tags);
105                        (_) ->
106                             false
107                     end, ExtMemData),
108
109    %% get_os_wordsize()
110    ok = case memsup:get_os_wordsize() of
111             32 ->
112                 32 = 8*erlang:system_info({wordsize,external}),
113                 ok;
114             64 ->
115                 % No reliable test here
116                 ok;
117             unsupported_os ->
118                 ok;
119             _ ->
120                 error
121         end,
122
123    %% get_check_interval()
124    60000 = memsup:get_check_interval(),
125
126    %% set_check_interval(Minutes)
127    ok = memsup:set_check_interval(2),
128    120000 = memsup:get_check_interval(),
129    {'EXIT',{badarg,_}} =
130    (catch memsup:set_check_interval(0.2)),
131    120000 = memsup:get_check_interval(),
132    ok = memsup:set_check_interval(1),
133
134    %% get_procmem_high_watermark()
135    5 = memsup:get_procmem_high_watermark(),
136
137    %% set_procmem_high_watermark()
138    ok = memsup:set_procmem_high_watermark(0.1),
139    10 = memsup:get_procmem_high_watermark(),
140    {'EXIT',{badarg,_}} =
141    (catch memsup:set_procmem_high_watermark(-0.1)),
142    10 = memsup:get_procmem_high_watermark(),
143    ok = memsup:set_procmem_high_watermark(0.05),
144
145    %% get_sysmem_high_watermark()
146    80 = memsup:get_sysmem_high_watermark(),
147
148    %% set_sysmem_high_watermark()
149    ok = memsup:set_sysmem_high_watermark(0.9),
150    90 = memsup:get_sysmem_high_watermark(),
151    {'EXIT',{badarg,_}} =
152    (catch memsup:set_sysmem_high_watermark(-0.9)),
153    90 = memsup:get_sysmem_high_watermark(),
154    ok = memsup:set_sysmem_high_watermark(0.8),
155
156    %% get|set_helper_timeout
157    30 = memsup:get_helper_timeout(),
158    ok = memsup:set_helper_timeout(29),
159    29 = memsup:get_helper_timeout(),
160    {'EXIT',{badarg,_}} = (catch memsup:set_helper_timeout(31.0)),
161    29 = memsup:get_helper_timeout(),
162    ok.
163
164%%----------------------------------------------------------------------
165%% NOTE: The test case is a bit weak as it will fail if the memory
166%% usage changes too much during its course.
167%%----------------------------------------------------------------------
168
169%% Test alarms when memsup_system_only==false
170alarm1(Config) when is_list(Config) ->
171
172    %% If system memory usage is too high, the testcase cannot
173    %% be run correctly
174    {Total, Alloc, {_Pid,_PidAlloc}} = memsup:get_memory_data(),
175    io:format("alarm1: Total: ~p, Alloc: ~p~n", [Total, Alloc]),
176    SysUsage = Alloc/Total,
177    if
178        SysUsage > 0.99 ->
179            {skip, sys_mem_too_high};
180        true ->
181            alarm1(Config, SysUsage)
182    end.
183
184alarm1(_Config, SysUsage) ->
185    %% Set a long memory check interval, we will force memory checks
186    %% instead
187    ok = memsup:set_check_interval(60),
188
189    %% Check thresholds
190    SysThreshold = (memsup:get_sysmem_high_watermark()/100),
191    ProcThreshold = (memsup:get_procmem_high_watermark()/100),
192
193    %% Check if a system alarm already should be set or not
194    SysP = if
195               SysUsage>SysThreshold -> true;
196               SysUsage=<SysThreshold -> false
197           end,
198
199    %% If system memory is higher than threshold, make sure the system
200    %% alarm is set. Otherwise, make sure it is not set
201    case alarm_set(system_memory_high_watermark) of
202        {true, []} when SysP ->
203            ok;
204        false when not SysP ->
205            ok;
206        _ ->
207            ct:fail({sys_alarm, SysUsage, SysThreshold})
208    end,
209
210    %% Lower/raise the threshold to clear/set the alarm
211    NewSysThreshold = if
212                          SysP ->
213                              Value = 1.1*SysUsage,
214                              if
215                                  Value > 0.99 -> 0.99;
216                                  true -> Value
217                              end;
218                          not SysP -> 0.9*SysUsage
219                      end,
220
221    ok = memsup:set_sysmem_high_watermark(NewSysThreshold),
222
223    %% Initiate and wait for a new data collection
224    ok = force_collection(),
225
226    %% Make sure the alarm is cleared/set
227    ct:sleep({seconds,5}),
228    case alarm_set(system_memory_high_watermark) of
229        {true, []} when not SysP ->
230            ok;
231        false when SysP ->
232            ok;
233        _ ->
234            ct:fail({sys_alarm, SysUsage, NewSysThreshold})
235    end,
236
237    %% Reset the threshold to set/clear the alarm again
238    ok = memsup:set_sysmem_high_watermark(SysThreshold),
239    ok = force_collection(),
240    ct:sleep({seconds,1}),
241    case alarm_set(system_memory_high_watermark) of
242        {true, []} when SysP ->
243            ok;
244        false when not SysP ->
245            ok;
246        _ ->
247            ct:fail({sys_alarm, SysUsage, SysThreshold})
248    end,
249
250    %% Check memory usage
251    {Total2, _, {WorstPid, PidAlloc}} = memsup:get_memory_data(),
252
253    %% Check if a process alarm already should be set or not
254    PidUsage = PidAlloc/Total2,
255    ProcP = if
256                PidUsage>ProcThreshold -> true;
257                PidUsage=<ProcThreshold -> false
258            end,
259
260    %% Make sure the process alarm is set/not set accordingly
261    case alarm_set(process_memory_high_watermark) of
262        {true, WorstPid} when ProcP ->
263            ok;
264        false when not ProcP ->
265            ok;
266        {true, BadPid1} when ProcP ->
267            ct:fail({proc_alarm, WorstPid, BadPid1});
268        _ ->
269            ct:fail({proc_alarm, PidUsage, ProcThreshold})
270    end,
271
272    %% Lower/raise the threshold to clear/set the alarm
273    NewProcThreshold = if
274                           ProcP -> 1.1*PidUsage;
275                           not ProcP -> 0.9*PidUsage
276                       end,
277    ok = memsup:set_procmem_high_watermark(NewProcThreshold),
278    ok = force_collection(),
279    ct:sleep({seconds,1}),
280    case alarm_set(process_memory_high_watermark) of
281        {true, WorstPid} when not ProcP ->
282            ok;
283        false when ProcP ->
284            ok;
285        {true, BadPid2} when not ProcP ->
286            ct:fail({proc_alarm, WorstPid, BadPid2});
287        _ ->
288            ct:fail({proc_alarm, PidUsage, ProcThreshold})
289    end,
290
291    %% Reset the threshold to clear/set the alarm
292    ok = memsup:set_procmem_high_watermark(ProcThreshold),
293    ok = force_collection(),
294    ct:sleep({seconds,1}),
295    case alarm_set(process_memory_high_watermark) of
296        {true, WorstPid} when ProcP ->
297            ok;
298        false when not ProcP ->
299            ok;
300        {true, BadPid3} when ProcP ->
301            ct:fail({proc_alarm, WorstPid, BadPid3});
302        _ ->
303            ct:fail({proc_alarm, PidUsage, ProcThreshold})
304    end,
305
306    %% Reset memory check interval
307    ok = memsup:set_check_interval(1),
308    ok.
309
310%% Test alarms when memsup_system_only==true
311alarm2(Config) when is_list(Config) ->
312
313    %% If system memory usage is too high, the testcase cannot
314    %% be run correctly
315    {Total, Alloc, {_Pid,_PidAlloc}} = memsup:get_memory_data(),
316    SysUsage = Alloc/Total,
317    if
318        SysUsage>0.99 ->
319            {skip, sys_mem_too_high};
320        true ->
321            alarm2(Config, SysUsage)
322    end.
323
324alarm2(_Config, _SysUsage) ->
325
326    %% Change memsup_system_only and restart memsup
327    ok = application:set_env(os_mon, memsup_system_only, true),
328    ok = supervisor:terminate_child(os_mon_sup, memsup),
329    {ok, _Memsup1} = supervisor:restart_child(os_mon_sup, memsup),
330
331    %% Set a long memory check interval, we will force memory checks
332    %% instead
333    ok = memsup:set_check_interval(60),
334
335    %% Check data and thresholds
336    {Total, Alloc, undefined} = memsup:get_memory_data(),
337    SysThreshold = (memsup:get_sysmem_high_watermark()/100),
338    true = is_integer(memsup:get_procmem_high_watermark()),
339
340    %% Check if a system alarm already should be set or not
341    SysUsage = Alloc/Total,
342    SysP = if
343               SysUsage>SysThreshold -> true;
344               SysUsage=<SysThreshold -> false
345           end,
346
347    %% If system memory is higher than threshold, make sure the system
348    %% alarm is set. Otherwise, make sure it is not set
349    case alarm_set(system_memory_high_watermark) of
350        {true, []} when SysP ->
351            ok;
352        false when not SysP ->
353            ok;
354        _ ->
355            ct:fail({sys_alarm, SysUsage, SysThreshold})
356    end,
357
358    %% Lower/raise the threshold to clear/set the alarm
359    NewSysThreshold = if
360                          SysP ->
361                              Value = 1.1*SysUsage,
362                              if
363                                  Value > 0.99 -> 0.99;
364                                  true -> Value
365                              end;
366                          not SysP -> 0.9*SysUsage
367                      end,
368
369    ok = memsup:set_sysmem_high_watermark(NewSysThreshold),
370
371    %% Initiate and wait for a new data collection
372    ok = force_collection(),
373
374    %% Make sure the alarm is cleared/set
375    ct:sleep({seconds,1}),
376    case alarm_set(system_memory_high_watermark) of
377        {true, []} when not SysP ->
378            ok;
379        false when SysP ->
380            ok;
381        _ ->
382            ct:fail({sys_alarm, SysUsage, NewSysThreshold})
383    end,
384
385    %% Reset the threshold to set/clear the alarm again
386    ok = memsup:set_sysmem_high_watermark(SysThreshold),
387    ok = force_collection(),
388    ct:sleep({seconds,1}),
389    case alarm_set(system_memory_high_watermark) of
390        {true, []} when SysP ->
391            ok;
392        false when not SysP ->
393            ok;
394        _ ->
395            ct:fail({sys_alarm, SysUsage, SysThreshold})
396    end,
397
398    %% Reset memsup_system_only and restart memsup
399    %% (memory check interval is then automatically reset)
400    ok = application:set_env(os_mon, memsup_system_only, false),
401    ok = supervisor:terminate_child(os_mon_sup, memsup),
402    {ok, _Memsup2} = supervisor:restart_child(os_mon_sup, memsup),
403
404    ok.
405
406alarm_set(Alarm) ->
407    alarm_set(Alarm, alarm_handler:get_alarms()).
408alarm_set(Alarm, [{Alarm,Data}|_]) ->
409    {true,Data};
410alarm_set(Alarm, [_|T]) ->
411    alarm_set(Alarm, T);
412alarm_set(_Alarm, []) ->
413    false.
414
415%% Make sure memsup discovers a process grown very large
416process(Config) when is_list(Config) ->
417
418    %% Set a long memory check interval, we will force memory checks
419    %% instead
420    ok = memsup:set_check_interval(60),
421
422    %% Collect data
423    MemData = memsup:get_memory_data(),
424    io:format("process: memsup:get_memory_data() = ~p~n", [MemData]),
425    {_Total,_Free,{_,Bytes}} = MemData,
426
427    %% Start a new process larger than Worst
428    WorsePid = spawn(fun() -> new_hog(Bytes) end),
429    ct:sleep({seconds,1}),
430
431    %% Initiate and wait for a new data collection
432    ok = force_collection(),
433
434    %% Check that get_memory_data() returns updated result
435    case memsup:get_memory_data() of
436        {_, _, {WorsePid, _MoreBytes}} ->
437            ok;
438        {_, _, BadWorst} ->
439            ct:fail({worst_pid, BadWorst})
440    end,
441
442    %% Reset memory check interval
443    exit(WorsePid, done),
444    ok = memsup:set_check_interval(1),
445    ok.
446
447new_hog(Bytes) ->
448    WordSize = erlang:system_info(wordsize),
449    N = (Bytes+200) div WordSize div 2,
450    List = lists:duplicate(N, a),
451    new_hog_1(List).
452
453new_hog_1(List) ->
454    receive
455        _Any -> exit(List)
456    end.
457
458%% Test configuration
459config(Config) when is_list(Config) ->
460
461    %% Change configuration parameters and make sure change is reflected
462    %% when memsup is restarted
463    ok = application:set_env(os_mon, memory_check_interval, 2),
464    ok =
465    application:set_env(os_mon, system_memory_high_watermark, 0.9),
466    ok =
467    application:set_env(os_mon, process_memory_high_watermark, 0.1),
468    ok = application:set_env(os_mon, memsup_helper_timeout, 35),
469    ok = application:set_env(os_mon, memsup_system_only, true),
470
471    ok = supervisor:terminate_child(os_mon_sup, memsup),
472    {ok, _Child1} = supervisor:restart_child(os_mon_sup, memsup),
473
474    120000 = memsup:get_check_interval(),
475    90 = memsup:get_sysmem_high_watermark(),
476    10 = memsup:get_procmem_high_watermark(),
477    35 = memsup:get_helper_timeout(),
478
479    %% Also try this with bad parameter values, should be ignored
480    ok = application:set_env(os_mon, memory_check_interval, 0.2),
481    ok =
482    application:set_env(os_mon, system_memory_high_watermark, -0.9),
483    ok =
484    application:set_env(os_mon, process_memory_high_watermark,-0.1),
485    ok = application:set_env(os_mon, memsup_helper_timeout, 0.35),
486    ok = application:set_env(os_mon, memsup_system_only, arne),
487
488    ok = supervisor:terminate_child(os_mon_sup, memsup),
489    {ok, _Child2} = supervisor:restart_child(os_mon_sup, memsup),
490
491    60000 = memsup:get_check_interval(),
492    80 = memsup:get_sysmem_high_watermark(),
493    5 = memsup:get_procmem_high_watermark(),
494    30 = memsup:get_helper_timeout(),
495
496    %% Reset configuration parameters
497    ok = application:set_env(os_mon, memory_check_interval, 1),
498    ok =
499    application:set_env(os_mon, system_memory_high_watermark, 0.8),
500    ok =
501    application:set_env(os_mon, process_memory_high_watermark,0.05),
502    ok = application:set_env(os_mon, memsup_helper_timeout, 30),
503    ok = application:set_env(os_mon, memsup_system_only, false),
504
505    ok.
506
507%% Test correct behaviour when service is unavailable
508unavailable(Config) when is_list(Config) ->
509
510    %% Close memsup
511    ok = application:set_env(os_mon, start_memsup, false),
512    ok = supervisor:terminate_child(os_mon_sup, memsup),
513
514    %% Make sure all API functions return their dummy values
515    {0,0,{_Pid,0}} = memsup:get_memory_data(),
516    ok = application:set_env(os_mon, memsup_system_only, true),
517    {0,0,undefined} = memsup:get_memory_data(),
518    ok = application:set_env(os_mon, memsup_system_only, false),
519    [] = memsup:get_system_memory_data(),
520    0  = memsup:get_os_wordsize(),
521    60000 = memsup:get_check_interval(),
522    ok = memsup:set_check_interval(2),
523    5 = memsup:get_procmem_high_watermark(),
524    ok = memsup:set_procmem_high_watermark(0.10),
525    80 = memsup:get_sysmem_high_watermark(),
526    ok = memsup:set_sysmem_high_watermark(0.90),
527    30 = memsup:get_helper_timeout(),
528    ok = memsup:set_helper_timeout(35),
529
530    %% Start memsup again,
531    ok = application:set_env(os_mon, start_memsup, true),
532    {ok, _Child} = supervisor:restart_child(os_mon_sup, memsup),
533
534    ok.
535
536%% Test stability of memsup when data collection times out
537timeout(Config) when is_list(Config) ->
538
539    %% Set a long memory check interval and memsup_helper timeout,
540    %% we will force memory checks instead and fake timeouts
541    ok = memsup:set_check_interval(60),
542    ok = memsup:set_helper_timeout(3600),
543
544    %% Provoke a timeout during memory collection
545    memsup ! time_to_collect,
546    memsup ! reg_collection_timeout,
547
548    %% Not much we can check though, except that memsup is still running
549    {_,_,_} = memsup:get_memory_data(),
550
551    %% Provoke a timeout during extensive memory collection
552    %% We fake a gen_server:call/2 to be able to send a timeout message
553    %% while the request is being handled
554
555    %% Linux should be handled the same way as solaris.
556
557    %    TimeoutMsg = case ?t:os_type() of
558    %		     {unix, sunos} -> ext_collection_timeout;
559    %		     {unix, linux} -> reg_collection_timeout
560    %		 end,
561
562    TimeoutMsg = ext_collection_timeout,
563
564    Pid = whereis(memsup),
565    Mref = erlang:monitor(process, Pid),
566    Pid ! {'$gen_call', {self(), Mref}, get_system_memory_data},
567    Pid ! TimeoutMsg,
568    receive
569        {Mref, []} ->
570            erlang:demonitor(Mref),
571            ok;
572        {Mref, Res} ->
573            erlang:demonitor(Mref),
574            ct:fail({unexpected_result, Res});
575        {'DOWN', Mref, _, _, _} ->
576            ct:fail(no_result)
577    end,
578
579    %% Reset memory check interval and memsup_helper timeout
580    ok = memsup:set_check_interval(1),
581    ok = memsup:set_helper_timeout(30),
582    memsup ! time_to_collect,
583
584    [_|_] = memsup:get_system_memory_data(),
585
586    ok.
587
588%% Test that memsup handles a terminating port program
589port(Config) when is_list(Config) ->
590    Str = os:cmd("ps -e | grep '[m]emsup'"),
591    case io_lib:fread("~s", Str) of
592        {ok, [Pid], _Rest} ->
593
594            %% Monitor memsup
595            MonRef = erlang:monitor(process, memsup),
596            {Total1,_Alloc1,_Worst1} = memsup:get_memory_data(),
597            true = Total1>0,
598
599            %% Kill the port program
600            case os:cmd("kill -9 " ++ Pid) of
601                [] ->
602
603                    %% memsup should now terminate
604                    receive
605                        {'DOWN', MonRef, _, _, {port_died, _Reason}} ->
606                            ok;
607                        {'DOWN', MonRef, _, _, Reason} ->
608                            ct:fail({unexpected_exit_reason, Reason})
609                    after
610                        3000 ->
611                            ct:fail(still_alive)
612                    end,
613
614                    %% Give os_mon_sup time to restart memsup
615                    ct:sleep({seconds,3}),
616                    {Total2,_Alloc2,_Worst2} =
617                    memsup:get_memory_data(),
618                    true = Total2>0,
619
620                    ok;
621
622                Line ->
623                    erlang:demonitor(MonRef),
624                    {skip, {not_killed, Line}}
625            end;
626        _ ->
627            {skip, {os_pid_not_found, Str}}
628    end.
629
630%% Test that alarms are cleared and not set twice
631otp_5910(Config) when is_list(Config) ->
632    Alarms =
633    [system_memory_high_watermark, process_memory_high_watermark],
634
635    %% Make sure memsup sets both alarms
636    ok = application:set_env(os_mon, memory_check_interval, 60),
637    ok = memsup:set_check_interval(60),
638    SysThreshold = (memsup:get_sysmem_high_watermark()/100),
639    ProcThreshold = (memsup:get_procmem_high_watermark()/100),
640
641    MemData = memsup:get_memory_data(),
642
643    io:format("otp_5910: memsup:get_memory_data() = ~p~n", [MemData]),
644    {Total, Alloc, {_Pid, _Bytes}} = MemData,
645    Pid = spawn_opt(fun() ->
646                            receive
647                                die -> ok
648                            end
649                    end, [{min_heap_size, 1000}]),
650    %% Create a process guaranteed to live, be constant and
651    %% break memsup process limit
652    {memory, Bytes} = erlang:process_info(Pid,memory),
653    SysUsage = Alloc/Total,
654    ProcUsage = Bytes/Total,
655
656    if
657        SysUsage>SysThreshold ->
658            ok;
659        SysUsage=<SysThreshold ->
660            ok = application:set_env(os_mon,
661                                     sys_mem_high_watermark,
662                                     0.5 * SysUsage),
663            ok = memsup:set_sysmem_high_watermark(0.5 * SysUsage)
664    end,
665    if
666        ProcUsage>ProcThreshold ->
667            ok;
668        ProcUsage=<ProcThreshold ->
669            ok = application:set_env(os_mon,
670                                     proc_mem_high_watermark,
671                                     0.5 * ProcUsage),
672            ok = memsup:set_procmem_high_watermark(0.5 *ProcUsage)
673    end,
674    ok = force_collection(),
675    ct:sleep({seconds,1}),
676    lists:foreach(fun(AlarmId) ->
677                          case alarm_set(AlarmId) of
678                              {true, _} -> ok;
679                              false ->
680                                  ct:fail({alarm_not_set, AlarmId})
681                          end
682                  end,
683                  Alarms),
684
685    %% Kill guaranteed process...
686    Pid ! die,
687    %% Kill memsup
688    exit(whereis(memsup), faked_memsup_crash),
689    %% Wait a little to make sure memsup has been restarted,
690    %% then make sure the alarms are set once, but not twice
691    ct:sleep({seconds,1}),
692    MemUsage = memsup:get_memory_data(),
693    SetAlarms = alarm_handler:get_alarms(),
694    case lists:foldl(fun(system_memory_high_watermark, {S, P}) ->
695                             {S+1, P};
696                        (process_memory_high_watermark, {S, P}) ->
697                             {S, P+1};
698                        (_AlarmId, Acc0) ->
699                             Acc0
700                     end,
701                     {0, 0},
702                     SetAlarms) of
703        {0, 0} ->
704            ok;
705        _ ->
706            ct:fail({bad_number_of_alarms, SetAlarms, MemUsage})
707    end,
708
709    %% Stop OS_Mon and make sure all memsup alarms are cleared
710    ok = application:stop(os_mon),
711    ct:sleep({seconds,1}),
712    lists:foreach(fun(AlarmId) ->
713                          case alarm_set(AlarmId) of
714                              false -> ok;
715                              {true, _} ->
716                                  ct:fail({alarm_is_set, AlarmId})
717                          end
718                  end,
719                  Alarms),
720
721    %% Reset configuration and restart OS_Mon
722    ok = application:set_env(os_mon,memory_check_interval,1),
723    ok = application:set_env(os_mon,sys_mem_high_watermark,0.8),
724    ok = application:set_env(os_mon,proc_mem_high_watermark,0.05),
725    ok = application:start(os_mon),
726    ok.
727
728improved_system_memory_data(Config) ->
729    {ok, Node} = start_node(Config, "-os_mon memsup_improved_system_memory_data true"),
730    ok = rpc:call(Node, application, start, [sasl]),
731    ok = rpc:call(Node, application, start, [os_mon]),
732
733    ExtMemData = rpc:call(Node, memsup, get_system_memory_data, []),
734
735    stop_node(Node),
736
737    Tags = ?IMPROVED_SYSTEM_MEMORY_DATA_TAGS,
738    AvailableMemoryPresent
739        = lists:foldl(fun ({Tag,Value}, AMP) when is_atom(Tag),
740                                                  is_integer(Value),
741                                                  Value >= 0 ->
742                              true = lists:member(Tag, Tags),
743                              case Tag of
744                                  available_memory ->
745                                      false = AMP,
746                                      true;
747                                  _ ->
748                                      AMP
749                              end
750                      end,
751                      false,
752                      ExtMemData),
753
754    case os:type() of
755        {unix,linux} ->
756            {ok, Data} = file:read_file("/proc/meminfo"),
757            case re:run(Data, <<"MemAvailable">>) of
758                {match, _} ->
759                    true = AvailableMemoryPresent,
760                    {comment, "available_memory present in result"};
761                _ ->
762                    {comment, "No available_memory present in result"}
763            end;
764        _ ->
765            ok
766    end.
767
768
769%%----------------------------------------------------------------------
770%% Auxiliary
771%%----------------------------------------------------------------------
772
773force_collection() ->
774    erlang:trace(whereis(memsup), true, ['receive']),
775    memsup ! time_to_collect,
776    TimerRef = erlang:send_after(5000, self(), timeout),
777    force_collection(TimerRef).
778
779force_collection(TimerRef) ->
780    receive
781        {trace, _Pid, 'receive', {collected_sys, _Sys}} ->
782            erlang:cancel_timer(TimerRef),
783            erlang:trace(whereis(memsup), false, ['receive']),
784            flush(),
785            ok;
786        {trace, _Pid, 'receive', reg_collection_timeout} ->
787            erlang:cancel_timer(TimerRef),
788            erlang:trace(whereis(memsup), false, ['receive']),
789            flush(),
790            collection_timeout;
791        timout ->
792            erlang:trace(whereis(memsup), false, ['receive']),
793            flush(),
794            timeout;
795        _Msg ->
796            force_collection(TimerRef)
797    end.
798
799flush() ->
800    receive
801        {trace, _, _, _} ->
802            flush();
803        timeout ->
804            flush()
805    after 0 ->
806              ok
807    end.
808
809start_node(Config) ->
810    start_node(Config, "").
811
812start_node(Config, Args) when is_list(Config) ->
813    Pa = filename:dirname(code:which(?MODULE)),
814    Name = list_to_atom(atom_to_list(?MODULE)
815			++ "-"
816			++ atom_to_list(proplists:get_value(testcase, Config))
817			++ "-"
818			++ integer_to_list(erlang:system_time(second))
819			++ "-"
820			++ integer_to_list(erlang:unique_integer([positive]))),
821    test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
822
823stop_node(Node) ->
824    test_server:stop_node(Node).
825