Skip to content
This repository was archived by the owner on Nov 17, 2020. It is now read-only.

Commit 99d0bdd

Browse files
authored
Merge pull request #227 from rabbitmq/rabbitmq-common-224
Use recon_alloc to get memory info on Win32, /proc on Linux, ps for other OS types
2 parents 9a58adf + 23b6622 commit 99d0bdd

File tree

4 files changed

+134
-88
lines changed

4 files changed

+134
-88
lines changed

LICENSE-BSD-recon

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Copyright (c) 2012-2017, Frédéric Trottier-Hébert
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without modification,
5+
are permitted provided that the following conditions are met:
6+
7+
Redistributions of source code must retain the above copyright notice, this
8+
list of conditions and the following disclaimer.
9+
10+
Redistributions in binary form must reproduce the above copyright notice, this
11+
list of conditions and the following disclaimer in the documentation and/or
12+
other materials provided with the distribution.
13+
14+
The names of its contributors may not be used to endorse or promote
15+
products derived from this software without specific prior written
16+
permission.
17+
18+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
22+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ define PROJECT_APP_EXTRA_KEYS
1919
]}
2020
endef
2121

22+
DEPS = recon
2223
LOCAL_DEPS = compiler syntax_tools xmerl
2324

2425
# FIXME: Use erlang.mk patched for RabbitMQ, while waiting for PRs to be

src/app_utils.erl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ stop_applications(Apps) ->
5050

5151
start_applications(Apps, ErrorHandler) ->
5252
manage_applications(fun lists:foldl/3,
53-
fun application:start/1,
53+
fun application:ensure_all_started/1,
5454
fun application:stop/1,
5555
already_started,
5656
ErrorHandler,
@@ -62,7 +62,7 @@ stop_applications(Apps, ErrorHandler) ->
6262
rabbit_log:info("Stopping application '~s'", [App]),
6363
application:stop(App)
6464
end,
65-
fun application:start/1,
65+
fun application:ensure_all_started/1,
6666
not_started,
6767
ErrorHandler,
6868
Apps).
@@ -116,6 +116,9 @@ manage_applications(Iterate, Do, Undo, SkipError, ErrorHandler, Apps) ->
116116
Iterate(fun (App, Acc) ->
117117
case Do(App) of
118118
ok -> [App | Acc];
119+
{ok, []} -> Acc;
120+
{ok, [App]} -> [App | Acc];
121+
{ok, StartedApps} -> StartedApps ++ Acc;
119122
{error, {SkipError, _}} -> Acc;
120123
{error, Reason} ->
121124
lists:foreach(Undo, Acc),

src/vm_memory_monitor.erl

Lines changed: 101 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,11 @@
5050
timeout,
5151
timer,
5252
alarmed,
53-
alarm_funs
54-
}).
53+
alarm_funs,
54+
os_type = undefined,
55+
os_pid = undefined,
56+
page_size = undefined,
57+
proc_file = undefined}).
5558

5659
-include("rabbit_memory.hrl").
5760

@@ -130,86 +133,38 @@ get_memory_use(ratio) ->
130133
%% be equal to the total size of all pages mapped to the emulator,
131134
%% according to http://erlang.org/doc/man/erlang.html#memory-0
132135
%% erlang:memory(total) under-reports memory usage by around 20%
136+
%%
137+
%% Win32 Note: 3.6.12 shipped with code that used wmic.exe to get the
138+
%% WorkingSetSize value for the running erl.exe process. Unfortunately
139+
%% even with a moderate invocation rate of 1 ops/second that uses more
140+
%% CPU resources than some Windows users are willing to tolerate.
141+
%% See rabbitmq/rabbitmq-server#1343 and rabbitmq/rabbitmq-common#224
142+
%% for details.
133143
-spec get_process_memory() -> Bytes :: integer().
134144
get_process_memory() ->
135-
case get_memory_calculation_strategy() of
136-
rss ->
137-
case get_system_process_resident_memory() of
138-
{ok, MemInBytes} ->
139-
MemInBytes;
140-
{error, Reason} ->
141-
rabbit_log:debug("Unable to get system memory used. Reason: ~p."
142-
" Falling back to erlang memory reporting",
143-
[Reason]),
144-
erlang:memory(total)
145-
end;
146-
erlang ->
147-
erlang:memory(total)
145+
try
146+
{ProcMem, _} = get_memory_use(bytes),
147+
ProcMem
148+
catch exit:{noproc, Error} ->
149+
rabbit_log:warning("Memory monitor process not yet started: ~p~n", [Error]),
150+
get_process_memory_uncached()
148151
end.
149152

150153
-spec get_memory_calculation_strategy() -> rss | erlang.
151154
get_memory_calculation_strategy() ->
152-
case rabbit_misc:get_env(rabbit, vm_memory_calculation_strategy, rss) of
153-
erlang ->
154-
erlang;
155-
rss ->
156-
rss;
155+
case rabbit_misc:get_env(rabbit, vm_memory_calculation_strategy, allocated) of
156+
allocated -> allocated;
157+
erlang -> erlang;
158+
legacy -> erlang; %% backwards compatibility
159+
rss -> rss;
157160
UnsupportedValue ->
158161
rabbit_log:warning(
159162
"Unsupported value '~p' for vm_memory_calculation_strategy. "
160-
"Supported values: (rss|erlang). "
161-
"Defaulting to 'rss'",
163+
"Supported values: (allocated|erlang|legacy|rss). "
164+
"Defaulting to 'allocated'",
162165
[UnsupportedValue]
163166
),
164-
rss
165-
end.
166-
167-
-spec get_system_process_resident_memory() -> {ok, Bytes :: integer()} | {error, term()}.
168-
get_system_process_resident_memory() ->
169-
try
170-
get_system_process_resident_memory(os:type())
171-
catch _:Error ->
172-
{error, {"Failed to get process resident memory", Error}}
173-
end.
174-
175-
get_system_process_resident_memory({unix,darwin}) ->
176-
get_ps_memory();
177-
178-
get_system_process_resident_memory({unix, linux}) ->
179-
get_ps_memory();
180-
181-
get_system_process_resident_memory({unix,freebsd}) ->
182-
get_ps_memory();
183-
184-
get_system_process_resident_memory({unix,openbsd}) ->
185-
get_ps_memory();
186-
187-
get_system_process_resident_memory({win32,_OSname}) ->
188-
%% Note: 3.6.12 shipped with code that used wmic.exe to get the
189-
%% WorkingSetSize value for the running erl.exe process. Unfortunately
190-
%% even with a moderate invocation rate of 1 ops/second that uses more
191-
%% CPU resources than some Windows users are willing to tolerate.
192-
%% See rabbitmq/rabbitmq-server#1343 for details.
193-
{ok, erlang:memory(total)};
194-
195-
get_system_process_resident_memory({unix, sunos}) ->
196-
get_ps_memory();
197-
198-
get_system_process_resident_memory({unix, aix}) ->
199-
get_ps_memory();
200-
201-
get_system_process_resident_memory(_OsType) ->
202-
{error, not_implemented_for_os}.
203-
204-
get_ps_memory() ->
205-
OsPid = os:getpid(),
206-
Cmd = "ps -p " ++ OsPid ++ " -o rss=",
207-
CmdOutput = os:cmd(Cmd),
208-
case re:run(CmdOutput, "[0-9]+", [{capture, first, list}]) of
209-
{match, [Match]} ->
210-
{ok, list_to_integer(Match) * 1024};
211-
_ ->
212-
{error, {unexpected_output_from_command, Cmd, CmdOutput}}
167+
allocated
213168
end.
214169

215170
%%----------------------------------------------------------------------------
@@ -226,11 +181,12 @@ start_link(MemFraction, AlarmSet, AlarmClear) ->
226181

227182
init([MemFraction, AlarmFuns]) ->
228183
TRef = erlang:send_after(?DEFAULT_MEMORY_CHECK_INTERVAL, self(), update),
229-
State = #state { timeout = ?DEFAULT_MEMORY_CHECK_INTERVAL,
230-
timer = TRef,
231-
alarmed = false,
232-
alarm_funs = AlarmFuns },
233-
{ok, set_mem_limits(State, MemFraction)}.
184+
State0 = #state{timeout = ?DEFAULT_MEMORY_CHECK_INTERVAL,
185+
timer = TRef,
186+
alarmed = false,
187+
alarm_funs = AlarmFuns},
188+
State1 = init_state_by_os(State0),
189+
{ok, set_mem_limits(State1, MemFraction)}.
234190

235191
handle_call(get_vm_memory_high_watermark, _From,
236192
#state{memory_config_limit = MemLimit} = State) ->
@@ -282,6 +238,53 @@ code_change(_OldVsn, State, _Extra) ->
282238
%%----------------------------------------------------------------------------
283239
%% Server Internals
284240
%%----------------------------------------------------------------------------
241+
242+
get_process_memory_uncached() ->
243+
TmpState = init_state_by_os(#state{}),
244+
TmpState#state.process_memory.
245+
246+
update_process_memory(State) ->
247+
Strategy = get_memory_calculation_strategy(),
248+
{ok, ProcMem} = get_process_memory_using_strategy(Strategy, State),
249+
State#state{process_memory = ProcMem}.
250+
251+
init_state_by_os(State = #state{os_type = undefined}) ->
252+
OsType = os:type(),
253+
OsPid = os:getpid(),
254+
init_state_by_os(State#state{os_type = OsType, os_pid = OsPid});
255+
init_state_by_os(State0 = #state{os_type = {unix, linux}, os_pid = OsPid}) ->
256+
PageSize = get_linux_pagesize(),
257+
ProcFile = io_lib:format("/proc/~s/statm", [OsPid]),
258+
State1 = State0#state{page_size = PageSize, proc_file = ProcFile},
259+
update_process_memory(State1);
260+
init_state_by_os(State) ->
261+
update_process_memory(State).
262+
263+
get_process_memory_using_strategy(rss, #state{os_type = {unix, linux},
264+
page_size = PageSize,
265+
proc_file = ProcFile}) ->
266+
Data = read_proc_file(ProcFile),
267+
[_|[RssPagesStr|_]] = string:split(Data, " ", all),
268+
ProcMem = list_to_integer(RssPagesStr) * PageSize,
269+
{ok, ProcMem};
270+
get_process_memory_using_strategy(rss, #state{os_type = {unix, _},
271+
os_pid = OsPid}) ->
272+
Cmd = "ps -p " ++ OsPid ++ " -o rss=",
273+
CmdOutput = os:cmd(Cmd),
274+
case re:run(CmdOutput, "[0-9]+", [{capture, first, list}]) of
275+
{match, [Match]} ->
276+
ProcMem = list_to_integer(Match) * 1024,
277+
{ok, ProcMem};
278+
_ ->
279+
{error, {unexpected_output_from_command, Cmd, CmdOutput}}
280+
end;
281+
get_process_memory_using_strategy(rss, _State) ->
282+
{ok, recon_alloc:memory(allocated)};
283+
get_process_memory_using_strategy(allocated, _State) ->
284+
{ok, recon_alloc:memory(allocated)};
285+
get_process_memory_using_strategy(erlang, _State) ->
286+
{ok, erlang:memory(total)}.
287+
285288
get_total_memory_from_os() ->
286289
try
287290
get_total_memory(os:type())
@@ -374,19 +377,20 @@ parse_mem_limit(MemLimit) ->
374377
),
375378
?DEFAULT_VM_MEMORY_HIGH_WATERMARK.
376379

377-
internal_update(State = #state { memory_limit = MemLimit,
378-
alarmed = Alarmed,
379-
alarm_funs = {AlarmSet, AlarmClear} }) ->
380-
ProcMem = get_process_memory(),
381-
NewAlarmed = ProcMem > MemLimit,
380+
internal_update(State0 = #state{memory_limit = MemLimit,
381+
alarmed = Alarmed,
382+
alarm_funs = {AlarmSet, AlarmClear}}) ->
383+
State1 = update_process_memory(State0),
384+
ProcMem = State1#state.process_memory,
385+
NewAlarmed = ProcMem > MemLimit,
382386
case {Alarmed, NewAlarmed} of
383387
{false, true} -> emit_update_info(set, ProcMem, MemLimit),
384388
AlarmSet({{resource_limit, memory, node()}, []});
385389
{true, false} -> emit_update_info(clear, ProcMem, MemLimit),
386390
AlarmClear({resource_limit, memory, node()});
387391
_ -> ok
388392
end,
389-
State #state {alarmed = NewAlarmed, process_memory = ProcMem}.
393+
State1#state{alarmed = NewAlarmed}.
390394

391395
emit_update_info(AlarmState, MemUsed, MemLimit) ->
392396
rabbit_log:info(
@@ -420,22 +424,33 @@ cmd(Command) ->
420424
_ -> os:cmd(Command)
421425
end.
422426

427+
get_linux_pagesize() ->
428+
CmdOutput = cmd("getconf PAGESIZE"),
429+
case re:run(CmdOutput, "^[0-9]+", [{capture, first, list}]) of
430+
{match, [Match]} -> list_to_integer(Match);
431+
_ ->
432+
rabbit_log:warning(
433+
"Failed to get memory page size, using 4096:~n~p~n",
434+
[CmdOutput]),
435+
4096
436+
end.
437+
423438
%% get_total_memory(OS) -> Total
424439
%% Windows and Freebsd code based on: memsup:get_memory_usage/1
425440
%% Original code was part of OTP and released under "Erlang Public License".
426441

427-
get_total_memory({unix,darwin}) ->
442+
get_total_memory({unix, darwin}) ->
428443
sysctl("hw.memsize");
429444

430-
get_total_memory({unix,freebsd}) ->
445+
get_total_memory({unix, freebsd}) ->
431446
PageSize = sysctl("vm.stats.vm.v_page_size"),
432447
PageCount = sysctl("vm.stats.vm.v_page_count"),
433448
PageCount * PageSize;
434449

435-
get_total_memory({unix,openbsd}) ->
450+
get_total_memory({unix, openbsd}) ->
436451
sysctl("hw.usermem");
437452

438-
get_total_memory({win32,_OSname}) ->
453+
get_total_memory({win32, _OSname}) ->
439454
[Result|_] = os_mon_sysinfo:get_mem_info(),
440455
{ok, [_MemLoad, TotPhys, _AvailPhys, _TotPage, _AvailPage, _TotV, _AvailV],
441456
_RestStr} =

0 commit comments

Comments
 (0)