1616
1717-module (rabbit_vm ).
1818
19- -export ([memory /0 , binary /0 , ets_tables_memory /1 ]).
19+ -export ([memory /0 , total_memory / 0 , binary /0 , ets_tables_memory /1 ]).
2020
2121-define (MAGIC_PLUGINS , [" cowboy" , " sockjs" , " rfc4627_jsonrpc" ]).
2222
3030
3131% %----------------------------------------------------------------------------
3232
33- % % Like erlang:memory(), but with awareness of rabbit-y things
3433memory () ->
3534 All = interesting_sups (),
3635 {Sums , _Other } = sum_processes (
@@ -41,7 +40,7 @@ memory() ->
4140 [aggregate (Names , Sums , memory , fun (X ) -> X end )
4241 || Names <- distinguished_interesting_sups ()],
4342
44- Mnesia = mnesia_memory (),
43+ MnesiaETS = mnesia_memory (),
4544 MsgIndexETS = ets_memory ([msg_store_persistent , msg_store_transient ]),
4645 MetricsETS = ets_memory ([rabbit_metrics ]),
4746 MetricsProc = try
@@ -52,8 +51,10 @@ memory() ->
5251 0
5352 end ,
5453 MgmtDbETS = ets_memory ([rabbit_mgmt_storage ]),
54+ OsTotal = total_memory (),
5555
56- [{total , Total },
56+
57+ [{total , ErlangTotal },
5758 {processes , Processes },
5859 {ets , ETS },
5960 {atom , Atom },
@@ -66,30 +67,137 @@ memory() ->
6667 - ConnsReader - ConnsWriter - ConnsChannel - ConnsOther
6768 - Qs - QsSlave - MsgIndexProc - Plugins - MgmtDbProc - MetricsProc ,
6869
69- [{total , Total },
70+ [
71+ % % Connections
7072 {connection_readers , ConnsReader },
7173 {connection_writers , ConnsWriter },
7274 {connection_channels , ConnsChannel },
7375 {connection_other , ConnsOther },
76+
77+ % % Queues
7478 {queue_procs , Qs },
7579 {queue_slave_procs , QsSlave },
80+
81+ % % Processes
7682 {plugins , Plugins },
7783 {other_proc , lists :max ([0 , OtherProc ])}, % % [1]
78- {mnesia , Mnesia },
84+
85+ % % Metrics
7986 {metrics , MetricsETS + MetricsProc },
8087 {mgmt_db , MgmtDbETS + MgmtDbProc },
81- {msg_index , MsgIndexETS + MsgIndexProc },
82- {other_ets , ETS - Mnesia - MsgIndexETS - MgmtDbETS },
88+
89+ % % ETS
90+ {mnesia , MnesiaETS },
91+ {other_ets , ETS - MnesiaETS - MetricsETS - MgmtDbETS - MsgIndexETS },
92+
93+ % % Messages (mostly, some binaries are not messages)
8394 {binary , Bin },
95+ {msg_index , MsgIndexETS + MsgIndexProc },
96+
97+ % % System
8498 {code , Code },
8599 {atom , Atom },
86- {other_system , System - ETS - Atom - Bin - Code }].
100+ {other_system , System - ETS - Bin - Code - Atom + ( OsTotal - ErlangTotal )},
87101
102+ {total , OsTotal }
103+ ].
88104% % [1] - erlang:memory(processes) can be less than the sum of its
89105% % parts. Rather than display something nonsensical, just silence any
90106% % claims about negative memory. See
91107% % http://erlang.org/pipermail/erlang-questions/2012-September/069320.html
92108
109+ % % Memory reported by erlang:memory(total) is not supposed to
110+ % % be equal to the total size of all pages mapped to the emulator,
111+ % % according to http://erlang.org/doc/man/erlang.html#memory-0
112+ % % erlang:memory(total) under-reports memory usage by around 20%
113+ -spec total_memory () -> Bytes :: integer ().
114+ total_memory () ->
115+ case get_memory_calculation_strategy () of
116+ rss ->
117+ case get_system_process_resident_memory () of
118+ {ok , MemInBytes } ->
119+ MemInBytes ;
120+ {error , Reason } ->
121+ rabbit_log :debug (" Unable to get system memory used. Reason: ~p ."
122+ " Falling back to erlang memory reporting" ,
123+ [Reason ]),
124+ erlang :memory (total )
125+ end ;
126+ erlang ->
127+ erlang :memory (total )
128+ end .
129+
130+ -spec get_memory_calculation_strategy () -> rss | erlang .
131+ get_memory_calculation_strategy () ->
132+ case application :get_env (rabbit , vm_memory_calculation_strategy , rss ) of
133+ erlang ->
134+ erlang ;
135+ rss ->
136+ rss ;
137+ UnsupportedValue ->
138+ rabbit_log :warning (
139+ " Unsupported value '~p ' for vm_memory_calculation_strategy. "
140+ " Supported values: (rss|erlang). "
141+ " Defaulting to 'rss'" ,
142+ [UnsupportedValue ]
143+ ),
144+ rss
145+ end .
146+
147+ -spec get_system_process_resident_memory () -> {ok , Bytes :: integer ()} | {error , term ()}.
148+ get_system_process_resident_memory () ->
149+ try
150+ get_system_process_resident_memory (os :type ())
151+ catch _ :Error ->
152+ {error , {" Failed to get process resident memory" , Error }}
153+ end .
154+
155+ get_system_process_resident_memory ({unix ,darwin }) ->
156+ get_ps_memory ();
157+
158+ get_system_process_resident_memory ({unix , linux }) ->
159+ get_ps_memory ();
160+
161+ get_system_process_resident_memory ({unix ,freebsd }) ->
162+ get_ps_memory ();
163+
164+ get_system_process_resident_memory ({unix ,openbsd }) ->
165+ get_ps_memory ();
166+
167+ get_system_process_resident_memory ({win32 ,_OSname }) ->
168+ OsPid = os :getpid (),
169+ Cmd = " tasklist /fi \" pid eq " ++ OsPid ++ " \" /fo LIST 2>&1 " ,
170+ CmdOutput = os :cmd (Cmd ),
171+ % % Memory usage is displayed in kilobytes
172+ % % with comma-separated thousands
173+ case re :run (CmdOutput , " Mem Usage:\\ s+([0-9,]+)\\ s+K" , [{capture , all_but_first , list }]) of
174+ {match , [Match ]} ->
175+ NoCommas = [ N || N <- Match , N =/= $, ],
176+ {ok , list_to_integer (NoCommas ) * 1024 };
177+ _ ->
178+ {error , {unexpected_output_from_command , Cmd , CmdOutput }}
179+ end ;
180+
181+ get_system_process_resident_memory ({unix , sunos }) ->
182+ get_ps_memory ();
183+
184+ get_system_process_resident_memory ({unix , aix }) ->
185+ get_ps_memory ();
186+
187+ get_system_process_resident_memory (_OsType ) ->
188+ {error , not_implemented_for_os }.
189+
190+ get_ps_memory () ->
191+ OsPid = os :getpid (),
192+ Cmd = " ps -p " ++ OsPid ++ " -o rss=" ,
193+ CmdOutput = os :cmd (Cmd ),
194+ case re :run (CmdOutput , " [0-9]+" , [{capture , first , list }]) of
195+ {match , [Match ]} ->
196+ {ok , list_to_integer (Match ) * 1024 };
197+ _ ->
198+ {error , {unexpected_output_from_command , Cmd , CmdOutput }}
199+ end .
200+
93201binary () ->
94202 All = interesting_sups (),
95203 {Sums , Rest } =
0 commit comments