emqttd_vm.erl 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2012-2016 Feng Lee <feng@emqtt.io>.
  3. %%
  4. %% Licensed under the Apache License, Version 2.0 (the "License");
  5. %% you may not use this file except in compliance with the License.
  6. %% You may obtain a copy of the License at
  7. %%
  8. %% http://www.apache.org/licenses/LICENSE-2.0
  9. %%
  10. %% Unless required by applicable law or agreed to in writing, software
  11. %% distributed under the License is distributed on an "AS IS" BASIS,
  12. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. %% See the License for the specific language governing permissions and
  14. %% limitations under the License.
  15. %%--------------------------------------------------------------------
  16. -module(emqttd_vm).
  17. -export([schedulers/0]).
  18. -export([microsecs/0]).
  19. -export([loads/0, get_system_info/0, get_system_info/1, mem_info/0, scheduler_usage/1]).
  20. -export([get_memory/0]).
  21. -export([get_process_list/0, get_process_info/0, get_process_info/1,
  22. get_process_gc/0, get_process_gc/1,
  23. get_process_group_leader_info/1,
  24. get_process_limit/0]).
  25. -export([get_ets_list/0, get_ets_info/0, get_ets_info/1,
  26. get_ets_object/0, get_ets_object/1]).
  27. -export([get_port_types/0, get_port_info/0, get_port_info/1]).
  28. -define(UTIL_ALLOCATORS, [temp_alloc,
  29. eheap_alloc,
  30. binary_alloc,
  31. ets_alloc,
  32. driver_alloc,
  33. sl_alloc,
  34. ll_alloc,
  35. fix_alloc,
  36. std_alloc]).
  37. -define(PROCESS_LIST, [initial_call,
  38. reductions,
  39. memory,
  40. message_queue_len,
  41. current_function]).
  42. -define(PROCESS_INFO, [initial_call,
  43. current_function,
  44. registered_name,
  45. status,
  46. message_queue_len,
  47. group_leader,
  48. priority,
  49. trap_exit,
  50. reductions,
  51. %%binary,
  52. last_calls,
  53. catchlevel,
  54. trace,
  55. suspending,
  56. sequential_trace_token,
  57. error_handler]).
  58. -define(PROCESS_GC, [memory,
  59. total_heap_size,
  60. heap_size,
  61. stack_size,
  62. min_heap_size]).
  63. %fullsweep_after]).
  64. -define(SYSTEM_INFO, [allocated_areas,
  65. allocator,
  66. alloc_util_allocators,
  67. build_type,
  68. check_io,
  69. compat_rel,
  70. creation,
  71. debug_compiled,
  72. dist,
  73. dist_ctrl,
  74. driver_version,
  75. elib_malloc,
  76. dist_buf_busy_limit,
  77. %fullsweep_after, % included in garbage_collection
  78. garbage_collection,
  79. %global_heaps_size, % deprecated
  80. heap_sizes,
  81. heap_type,
  82. info,
  83. kernel_poll,
  84. loaded,
  85. logical_processors,
  86. logical_processors_available,
  87. logical_processors_online,
  88. machine,
  89. %min_heap_size, % included in garbage_collection
  90. %min_bin_vheap_size, % included in garbage_collection
  91. modified_timing_level,
  92. multi_scheduling,
  93. multi_scheduling_blockers,
  94. otp_release,
  95. port_count,
  96. process_count,
  97. process_limit,
  98. scheduler_bind_type,
  99. scheduler_bindings,
  100. scheduler_id,
  101. schedulers,
  102. schedulers_online,
  103. smp_support,
  104. system_version,
  105. system_architecture,
  106. threads,
  107. thread_pool_size,
  108. trace_control_word,
  109. update_cpu_info,
  110. version,
  111. wordsize]).
  112. -define(SOCKET_OPTS, [active,
  113. broadcast,
  114. buffer,
  115. delay_send,
  116. dontroute,
  117. exit_on_close,
  118. header,
  119. high_watermark,
  120. ipv6_v6only,
  121. keepalive,
  122. linger,
  123. low_watermark,
  124. mode,
  125. nodelay,
  126. packet,
  127. packet_size,
  128. priority,
  129. read_packets,
  130. recbuf,
  131. reuseaddr,
  132. send_timeout,
  133. send_timeout_close,
  134. sndbuf,
  135. tos]).
  136. schedulers() ->
  137. erlang:system_info(schedulers).
  138. microsecs() ->
  139. {Mega, Sec, Micro} = erlang:now(),
  140. (Mega * 1000000 + Sec) * 1000000 + Micro.
  141. loads() ->
  142. [{load1, ftos(cpu_sup:avg1()/256)},
  143. {load5, ftos(cpu_sup:avg5()/256)},
  144. {load15, ftos(cpu_sup:avg15()/256)}].
  145. get_system_info() ->
  146. [{Key, format_system_info(Key, get_system_info(Key))} || Key <- ?SYSTEM_INFO].
  147. get_system_info(Key) ->
  148. try erlang:system_info(Key) catch
  149. error:badarg->undefined
  150. end.
  151. %% conversion functions for erlang:system_info(Key)
  152. format_system_info(allocated_areas, List) ->
  153. [convert_allocated_areas(Value) || Value <- List];
  154. format_system_info(allocator, {_,_,_,List}) ->
  155. List;
  156. format_system_info(dist_ctrl, List) ->
  157. lists:map(fun({Node, Socket}) ->
  158. {ok, Stats} = inet:getstat(Socket), {Node, Stats}
  159. end, List);
  160. format_system_info(driver_version, Value) ->
  161. list_to_binary(Value);
  162. format_system_info(machine, Value) ->
  163. list_to_binary(Value);
  164. format_system_info(otp_release, Value) ->
  165. list_to_binary(Value);
  166. format_system_info(scheduler_bindings, Value) ->
  167. tuple_to_list(Value);
  168. format_system_info(system_version, Value) ->
  169. list_to_binary(Value);
  170. format_system_info(system_architecture, Value) ->
  171. list_to_binary(Value);
  172. format_system_info(version, Value) ->
  173. list_to_binary(Value);
  174. format_system_info(_, Value) ->
  175. Value.
  176. convert_allocated_areas({Key, Value1, Value2}) ->
  177. {Key, [Value1, Value2]};
  178. convert_allocated_areas({Key, Value}) ->
  179. {Key, Value}.
  180. mem_info() ->
  181. Dataset = memsup:get_system_memory_data(),
  182. [{total_memory, proplists:get_value(total_memory, Dataset)},
  183. {used_memory, proplists:get_value(total_memory, Dataset) - proplists:get_value(free_memory, Dataset)}].
  184. ftos(F) ->
  185. [S] = io_lib:format("~.2f", [F]), S.
  186. %%%% erlang vm scheduler_usage fun copied from recon
  187. scheduler_usage(Interval) when is_integer(Interval) ->
  188. %% We start and stop the scheduler_wall_time system flag
  189. %% if it wasn't in place already. Usually setting the flag
  190. %% should have a CPU impact(make it higher) only when under low usage.
  191. FormerFlag = erlang:system_flag(scheduler_wall_time, true),
  192. First = erlang:statistics(scheduler_wall_time),
  193. timer:sleep(Interval),
  194. Last = erlang:statistics(scheduler_wall_time),
  195. erlang:system_flag(scheduler_wall_time, FormerFlag),
  196. scheduler_usage_diff(First, Last).
  197. scheduler_usage_diff(First, Last) ->
  198. lists:map(fun({{I, A0, T0},{I, A1, T1}}) ->
  199. {I, (A1 - A0)/(T1 - T0)}
  200. end, lists:zip(lists:sort(First), lists:sort(Last))).
  201. get_memory()->
  202. [{Key, get_memory(Key, current)} || Key <- [used, allocated, unused, usage]] ++ erlang:memory().
  203. get_memory(used, Keyword) ->
  204. lists:sum(lists:map(fun({_, Prop}) ->
  205. container_size(Prop, Keyword, blocks_size)
  206. end, util_alloc()));
  207. get_memory(allocated, Keyword) ->
  208. lists:sum(lists:map(fun({_, Prop})->
  209. container_size(Prop, Keyword, carriers_size)
  210. end, util_alloc()));
  211. get_memory(unused, Keyword) ->
  212. get_memory(allocated, Keyword) - get_memory(used, Keyword);
  213. get_memory(usage, Keyword) ->
  214. get_memory(used, Keyword) / get_memory(allocated, Keyword).
  215. util_alloc()->
  216. alloc(?UTIL_ALLOCATORS).
  217. alloc()->
  218. {_Mem, Allocs} = snapshot_int(),
  219. Allocs.
  220. alloc(Type) ->
  221. [{{T, Instance}, Props} || {{T, Instance}, Props} <- alloc(), lists:member(T, Type)].
  222. snapshot_int() ->
  223. {erlang:memory(), allocators()}.
  224. allocators() ->
  225. UtilAllocators = erlang:system_info(alloc_util_allocators),
  226. Allocators = [sys_alloc, mseg_alloc|UtilAllocators],
  227. [{{A, N},lists:sort(proplists:delete(versions, Props))} ||
  228. A <- Allocators, Allocs <- [erlang:system_info({allocator, A})],
  229. Allocs =/= false, {_, N, Props} <- Allocs].
  230. container_size(Prop, Keyword, Container) ->
  231. Sbcs = container_value(Prop, Keyword, sbcs, Container),
  232. Mbcs = container_value(Prop, Keyword, mbcs, Container),
  233. Sbcs+Mbcs.
  234. container_value(Prop, Keyword, Type, Container) when is_atom(Keyword)->
  235. container_value(Prop, 2, Type, Container);
  236. container_value(Props, Pos, mbcs = Type, Container) when is_integer(Pos)->
  237. Pool = case proplists:get_value(mbcs_pool, Props) of
  238. PoolProps when PoolProps =/= undefined ->
  239. element(Pos, lists:keyfind(Container, 1, PoolProps));
  240. _ ->
  241. 0
  242. end,
  243. TypeProps = proplists:get_value(Type, Props),
  244. Pool + element(Pos, lists:keyfind(Container, 1, TypeProps));
  245. container_value(Props, Pos, Type, Container) ->
  246. TypeProps = proplists:get_value(Type, Props),
  247. element(Pos, lists:keyfind(Container, 1, TypeProps)).
  248. get_process_list()->
  249. [get_process_list(Pid) || Pid <- processes()].
  250. get_process_list(Pid) when is_pid(Pid) ->
  251. [{pid, Pid} | [process_info(Pid, Key) || Key <- ?PROCESS_LIST]].
  252. get_process_info() ->
  253. [get_process_info(Pid) || Pid <- processes()].
  254. get_process_info(Pid) when is_pid(Pid) ->
  255. process_info(Pid, ?PROCESS_INFO).
  256. get_process_gc() ->
  257. [get_process_gc(Pid) || Pid <- processes()].
  258. get_process_gc(Pid) when is_pid(Pid) ->
  259. process_info(Pid, ?PROCESS_GC).
  260. get_process_group_leader_info(LeaderPid) when is_pid(LeaderPid) ->
  261. [{Key, Value}|| {Key, Value} <- process_info(LeaderPid), lists:member(Key, ?PROCESS_INFO)].
  262. get_process_limit() ->
  263. erlang:system_info(process_limit).
  264. get_ets_list() ->
  265. ets:all().
  266. get_ets_info() ->
  267. [get_ets_info(Tab) || Tab <- ets:all()].
  268. get_ets_info(Tab) ->
  269. case ets:info(Tab) of
  270. undefined ->
  271. [];
  272. Entries when is_list(Entries) ->
  273. mapping(Entries)
  274. end.
  275. get_ets_object() ->
  276. [{Tab, get_ets_object(Tab)} || Tab <- ets:all()].
  277. get_ets_object(Tab) ->
  278. TabInfo = ets:info(Tab),
  279. Size = proplists:get_value(size, TabInfo),
  280. NameTab = proplists:get_value(named_table, TabInfo),
  281. if (Size == 0) or (NameTab == false) ->
  282. [];
  283. true ->
  284. ets:tab2list(Tab)
  285. end.
  286. get_port_types() ->
  287. lists:usort(fun({KA, VA},{KB, VB})-> {VA, KB} >{VB, KA} end,
  288. ports_type_count([Type || {_Port, Type} <- ports_type_list()])).
  289. get_port_info() ->
  290. [get_port_info(Port) ||Port <- erlang:ports()].
  291. get_port_info(PortTerm) ->
  292. Port = transform_port(PortTerm),
  293. [port_info(Port, Type) || Type <- [meta, signals, io, memory_used, specific]].
  294. port_info(Port, meta) ->
  295. {meta, List} = port_info_type(Port, meta, [id, name, os_pid]),
  296. case port_info(Port, registered_name) of
  297. [] -> {meta, List};
  298. Name -> {meta, [Name | List]}
  299. end;
  300. port_info(PortTerm, signals) ->
  301. port_info_type(PortTerm, signals, [connected, links, monitors]);
  302. port_info(PortTerm, io) ->
  303. port_info_type(PortTerm, io, [input, output]);
  304. port_info(PortTerm, memory_used) ->
  305. port_info_type(PortTerm, memory_used, [memory, queue_size]);
  306. port_info(PortTerm, specific) ->
  307. Port = transform_port(PortTerm),
  308. Props = case erlang:port_info(Port, name) of
  309. {_, Type} when Type =:= "udp_inet";
  310. Type =:= "tcp_inet";
  311. Type =:= "sctp_inet" ->
  312. case catch inet:getstat(Port) of
  313. {ok, Stats} -> [{statistics, Stats}];
  314. _ -> []
  315. end ++
  316. case catch inet:peername(Port) of
  317. {ok, Peer} -> [{peername, Peer}];
  318. {error, _} -> []
  319. end ++
  320. case catch inet:sockname(Port) of
  321. {ok, Local} -> [{sockname, Local}];
  322. {error, _} -> []
  323. end ++
  324. case catch inet:getopts(Port, ?SOCKET_OPTS ) of
  325. {ok, Opts} -> [{options, Opts}];
  326. {error, _} -> []
  327. end;
  328. {_, "efile"} ->
  329. [];
  330. _ ->
  331. []
  332. end,
  333. {specific, Props};
  334. port_info(PortTerm, Keys) when is_list(Keys) ->
  335. Port = transform_port(PortTerm),
  336. [erlang:port_info(Port, Key) || Key <- Keys];
  337. port_info(PortTerm, Key) when is_atom(Key) ->
  338. Port = transform_port(PortTerm),
  339. erlang:port_info(Port, Key).
  340. port_info_type(PortTerm, Type, Keys) ->
  341. Port = transform_port(PortTerm),
  342. {Type, [erlang:port_info(Port, Key) || Key <- Keys]}.
  343. transform_port(Port) when is_port(Port) -> Port;
  344. transform_port("#Port<0." ++ Id) ->
  345. N = list_to_integer(lists:sublist(Id, length(Id) - 1)),
  346. transform_port(N);
  347. transform_port(N) when is_integer(N) ->
  348. Name = iolist_to_binary(atom_to_list(node())),
  349. NameLen = iolist_size(Name),
  350. Vsn = binary:last(term_to_binary(self())),
  351. Bin = <<131, 102, 100, NameLen:2/unit:8, Name:NameLen/binary, N:4/unit:8, Vsn:8>>,
  352. binary_to_term(Bin).
  353. ports_type_list() ->
  354. [{Port, PortType} || Port <- erlang:ports(),
  355. {_, PortType} <- [erlang:port_info(Port, name)]].
  356. ports_type_count(Types) ->
  357. DictTypes = lists:foldl(fun(Type, Acc)->
  358. dict:update_counter(Type, 1, Acc)
  359. end, dict:new(), Types),
  360. dict:to_list(DictTypes).
  361. mapping(Entries) ->
  362. mapping(Entries, []).
  363. mapping([], Acc) ->
  364. Acc;
  365. mapping([{owner, V}|Entries], Acc) when is_pid(V) ->
  366. OwnerInfo = process_info(V),
  367. Owner = proplists:get_value(registered_name, OwnerInfo, undefined),
  368. mapping(Entries, [{owner, Owner}|Acc]);
  369. mapping([{Key, Value}|Entries], Acc) ->
  370. mapping(Entries, [{Key, Value}|Acc]).