Merge branch 'dev-hd' into dev

This commit is contained in:
Feng 2015-07-05 20:52:34 +08:00
commit a68fa754fe
1 changed files with 305 additions and 118 deletions

View File

@ -27,7 +27,46 @@
-module(emqttd_vm). -module(emqttd_vm).
-author("Feng Lee <feng@emqtt.io>"). -define(UTIL_ALLOCATORS, [temp_alloc,
eheap_alloc,
binary_alloc,
ets_alloc,
driver_alloc,
sl_alloc,
ll_alloc,
fix_alloc,
std_alloc
]).
-define(PROCESS_LIST, [initial_call,
reductions,
memory,
message_queue_len,
current_function]).
-define(PROCESS_INFO, [initial_call,
current_function,
registered_name,
status,
message_queue_len,
group_leader,
priority,
trap_exit,
reductions,
binary,
last_calls,
catchlevel,
trace,
suspending,
sequential_trace_token,
error_handler]).
-define(PROCESS_GC, [memory,
total_heap_size,
heap_size,
stack_size,
min_heap_size]).%,
%fullsweep_after]).
-export([timestamp/0, microsecs/0]). -export([timestamp/0, microsecs/0]).
@ -87,31 +126,57 @@
-define(SOCKET_OPTS, [ -define(SOCKET_OPTS, [
active, active,
broadcast, broadcast,
buffer,
buffer,
buffer,
buffer,
delay_send, delay_send,
dontroute, dontroute,
exit_on_close, exit_on_close,
header, header,
high_watermark,
ipv6_v6only,
keepalive, keepalive,
linger,
low_watermark,
mode,
nodelay, nodelay,
packet, packet,
packet_size, packet_size,
priority,
read_packets, read_packets,
recbuf, recbuf,
reuseaddr, reuseaddr,
send_timeout, send_timeout,
send_timeout_close, send_timeout_close,
sndbuf, sndbuf,
priority,
tos tos
]). ]).
-author("Feng Lee <feng@emqtt.io>").
-export([get_system_info/0, -export([timestamp/0, microsecs/0]).
% get_statistics/0,
% get_process_info/0, -export([loads/0,
get_ports_info/0, scheduler_usage/1]).
get_ets_info/0]).
-export([get_memory/0]).
-export([get_process_list/0,
get_process_info/0,
get_process_gc/0,
get_process_group_leader_info/1]).
-export([get_ets_list/0,
get_ets_info/0,
get_ets_info/1,
get_ets_object/0,
get_ets_object/1]).
-export([get_port_types/0,
get_port_info/0,
get_port_info/1]).
timestamp() -> timestamp() ->
{MegaSecs, Secs, _MicroSecs} = os:timestamp(), {MegaSecs, Secs, _MicroSecs} = os:timestamp(),
@ -129,136 +194,258 @@ loads() ->
ftos(F) -> ftos(F) ->
[S] = io_lib:format("~.2f", [F]), S. [S] = io_lib:format("~.2f", [F]), S.
get_system_info() -> %%%% erlang vm scheduler_usage fun copied from recon
[{Key, format_system_info(Key, get_system_info(Key))} || Key <- ?SYSTEM_INFO]. scheduler_usage(Interval) when is_integer(Interval) ->
%% We start and stop the scheduler_wall_time system flag
%% if it wasn't in place already. Usually setting the flag
%% should have a CPU impact(make it higher) only when under low usage.
FormerFlag = erlang:system_flag(scheduler_wall_time, true),
First = erlang:statistics(scheduler_wall_time),
timer:sleep(Interval),
Last = erlang:statistics(scheduler_wall_time),
erlang:system_flag(scheduler_wall_time, FormerFlag),
scheduler_usage_diff(First, Last).
get_system_info(Key) -> scheduler_usage_diff(First, Last) ->
try erlang:system_info(Key) catch lists:map(
error:badarg->undefined fun({{I, A0, T0},{I, A1, T1}}) ->{I, (A1 - A0)/(T1 - T0)}end,
end. lists:zip(lists:sort(First), lists:sort(Last))
).
%% conversion functions for erlang:system_info(Key) get_memory()->
[{Key, get_memory(Key, current)} || Key <- [used, allocated, unused, usage]] ++ erlang:memory().
format_system_info(allocated_areas, List) -> get_memory(used, Keyword) ->
[convert_allocated_areas(Value) || Value <- List]; lists:sum(lists:map(fun({_, Prop}) ->
format_system_info(allocator, {_,_,_,List}) -> container_size(Prop, Keyword, blocks_size)
List; end, util_alloc()));
format_system_info(dist_ctrl, List) -> get_memory(allocated, Keyword) ->
lists:map(fun({Node, Socket}) -> lists:sum(lists:map(fun({_, Prop})->
{ok, Stats} = inet:getstat(Socket), container_size(Prop, Keyword, carriers_size)
{Node, Stats} end, util_alloc()));
end, List); get_memory(unused, Keyword) ->
format_system_info(driver_version, Value) -> get_memory(allocated, Keyword) - get_memory(used, Keyword);
list_to_binary(Value); get_memory(usage, Keyword) ->
format_system_info(machine, Value) -> get_memory(used, Keyword) / get_memory(allocated, Keyword).
list_to_binary(Value);
format_system_info(otp_release, Value) ->
list_to_binary(Value);
format_system_info(scheduler_bindings, Value) ->
tuple_to_list(Value);
format_system_info(system_version, Value) ->
list_to_binary(Value);
format_system_info(system_architecture, Value) ->
list_to_binary(Value);
format_system_info(version, Value) ->
list_to_binary(Value);
format_system_info(_, Value) ->
Value.
convert_allocated_areas({Key, Value1, Value2}) -> util_alloc()->
{Key, [Value1, Value2]}; alloc(?UTIL_ALLOCATORS).
convert_allocated_areas({Key, Value}) ->
{Key, Value}.
alloc()->
{_Mem, Allocs} = snapshot_int(),
Allocs.
alloc(Type) ->
[{{T, Instance}, Props} || {{T, Instance}, Props} <- alloc(), lists:member(T, Type)].
get_ports_info()-> snapshot_int() ->
[{pid_port_fun_to_atom(Port), get_port_info(Port)} || Port <- erlang:ports()]. {erlang:memory(), allocators()}.
get_port_info(Port) -> allocators() ->
Stat = get_socket_getstat(Port), UtilAllocators = erlang:system_info(alloc_util_allocators),
SockName = get_socket_sockname(Port), Allocators = [sys_alloc, mseg_alloc|UtilAllocators],
Opts = get_socket_opts(Port), [{{A, N},lists:sort(proplists:delete(versions, Props))} ||
Protocol = get_socket_protocol(Port), A <- Allocators,
Status = get_socket_status(Port), Allocs <- [erlang:system_info({allocator, A})],
Type = get_socket_type(Port), Allocs =/= false,
{_, N, Props} <- Allocs].
lists:flatten(lists:append([ container_size(Prop, Keyword, Container) ->
Stat, Sbcs = container_value(Prop, Keyword, sbcs, Container),
SockName, Mbcs = container_value(Prop, Keyword, mbcs, Container),
Opts, Sbcs+Mbcs.
Protocol,
Status,
Type
])).
get_socket_getstat(Socket) -> container_value(Prop, Keyword, Type, Container) when is_atom(Keyword)->
case catch inet:getstat(Socket) of container_value(Prop, 2, Type, Container);
{ok, Info} -> container_value(Props, Pos, mbcs = Type, Container) when is_integer(Pos)->
Info; Pool = case proplists:get_value(mbcs_pool, Props) of
_ -> PoolProps when PoolProps =/= undefined ->
[] element(Pos, lists:keyfind(Container, 1, PoolProps));
end. _ -> 0
end,
TypeProps = proplists:get_value(Type, Props),
Pool + element(Pos, lists:keyfind(Container, 1, TypeProps));
get_socket_sockname(Socket) -> container_value(Props, Pos, Type, Container) ->
case catch inet:sockname(Socket) of TypeProps = proplists:get_value(Type, Props),
{ok, {Ip, Port}} -> element(Pos, lists:keyfind(Container, 1, TypeProps)).
[{ip, ip_to_binary(Ip)}, {port, Port}];
_ ->
[]
end.
ip_to_binary(Tuple) -> get_process_list()->
iolist_to_binary(string:join(lists:map(fun integer_to_list/1, tuple_to_list(Tuple)), ".")). [get_process_list(Pid) || Pid <- processes()].
get_process_list(Pid) when is_pid(Pid) ->
Info = [process_info(Pid, Key) || Key <- ?PROCESS_LIST],
[{pid, pid_port_fun_to_atom(Pid)}] ++ lists:flatten([convert_pid_info(Item) || Item <- Info]).
get_socket_protocol(Socket) -> get_process_info() ->
case erlang:port_info(Socket, name) of [get_process_info(Pid) || Pid <- processes()].
{name, "tcp_inet"} -> get_process_info(Pid) when is_pid(Pid) ->
[{protocol, tcp}]; ProcessInfo = [process_info(Pid, Key) || Key <- ?PROCESS_INFO],
{name, "udp_inet"} -> lists:flatten([convert_pid_info(Item) || Item <- ProcessInfo]).
[{protocol, udp}];
{name,"sctp_inet"} ->
[{protocol, sctp}];
_ ->
[]
end.
get_socket_status(Socket) -> get_process_gc() ->
case catch prim_inet:getstatus(Socket) of [get_process_gc(Pid) || Pid <- processes()].
{ok, Status} -> get_process_gc(Pid) when is_pid(Pid) ->
[{status, Status}]; GcInfo = [process_info(Pid, Key) || Key <- ?PROCESS_GC],
_ -> lists:flatten([convert_pid_info(E) || E <- GcInfo]).
[]
end.
get_socket_type(Socket) -> get_process_group_leader_info(LeaderPid) when is_pid(LeaderPid) ->
case catch prim_inet:gettype(Socket) of LeaderInfo = [{Key, Value}|| {Key, Value} <- process_info(LeaderPid), lists:member(Key, ?PROCESS_INFO)],
{ok, Type} -> lists:flatten([convert_pid_info(E) || E <- LeaderInfo]).
[{type, tuple_to_list(Type)}];
_ ->
[]
end.
get_socket_opts(Socket) -> get_ets_list() ->
[get_socket_opts(Socket, Key) || Key <- ?SOCKET_OPTS]. ets:all().
get_socket_opts(Socket, Key) ->
case catch inet:getopts(Socket, [Key]) of
{ok, Opt} ->
Opt;
_ ->
[]
end.
get_ets_info() -> get_ets_info() ->
[{Tab, get_ets_dets_info(ets, Tab)} || Tab <- ets:all()]. [get_ets_info(Tab) || Tab <- ets:all()].
get_ets_info(Tab) ->
case ets:info(Tab) of
undefined ->
[];
Entries when is_list(Entries) ->
mapping(Entries)
end.
get_ets_object() ->
[{Tab, get_ets_object(Tab)} || Tab <- ets:all()].
get_ets_object(Tab) ->
TabInfo = ets:info(Tab),
Size = proplists:get_value(size, TabInfo),
NameTab = proplists:get_value(named_table, TabInfo),
if (Size == 0) or (NameTab == false) ->
[];
true ->
ets:tab2list(Tab)
end.
get_port_types() ->
lists:usort(fun({KA, VA},{KB, VB})-> {VA, KB} >{VB, KA} end,
ports_type_count([Type || {_Port, Type} <- ports_type_list()])).
get_port_info() ->
[get_port_info(Port) ||Port <- erlang:ports()].
get_port_info(PortTerm) ->
Port = transform_port(PortTerm),
[port_info(Port, Type) || Type <- [meta, signals, io, memory_used, specific]].
port_info(Port, meta) ->
{meta, List} = port_info_type(Port, meta, [id, name, os_pid]),
case port_info(Port, registered_name) of
[] -> {meta, List};
Name -> {meta, [Name | List]}
end;
port_info(PortTerm, signals) ->
port_info_type(PortTerm, signals, [connected, links, monitors]);
port_info(PortTerm, io) ->
port_info_type(PortTerm, io, [input, output]);
port_info(PortTerm, memory_used) ->
port_info_type(PortTerm, memory_used, [memory, queue_size]);
port_info(PortTerm, specific) ->
Port = transform_port(PortTerm),
Props = case erlang:port_info(Port, name) of
{_, Type} when Type =:= "udp_inet";
Type =:= "tcp_inet";
Type =:= "sctp_inet" ->
case catch inet:getstat(Port) of
{ok, Stats} -> [{statistics, Stats}];
_ ->[]
end ++
case catch inet:peername(Port) of
{ok, Peer} ->[{peername, Peer}];
{error, _} ->[]
end ++
case catch inet:sockname(Port) of
{ok, Local} ->[{sockname, Local}];
{error, _} -> []
end ++
case catch inet:getopts(Port, ?SOCKET_OPTS ) of
{ok, Opts} -> [{options, Opts}];
{error, _} -> []
end;
{_, "efile"} ->
[];
_ ->[]
end,
{specific, Props};
port_info(PortTerm, Keys) when is_list(Keys) ->
Port = transform_port(PortTerm),
[erlang:port_info(Port, Key) || Key <- Keys];
port_info(PortTerm, Key) when is_atom(Key) ->
Port = transform_port(PortTerm),
erlang:port_info(Port, Key).
port_info_type(PortTerm, Type, Keys) ->
Port = transform_port(PortTerm),
{Type, [erlang:port_info(Port, Key) || Key <- Keys]}.
transform_port(Port) when is_port(Port) -> Port;
transform_port("#Port<0." ++ Id) ->
N = list_to_integer(lists:sublist(Id, length(Id) - 1)),
transform_port(N);
transform_port(N) when is_integer(N) ->
Name = iolist_to_binary(atom_to_list(node())),
NameLen = iolist_size(Name),
Vsn = binary:last(term_to_binary(self())),
Bin = <<131, 102, 100,
NameLen:2/unit:8,
Name:NameLen/binary,
N:4/unit:8,
Vsn:8>>,
binary_to_term(Bin).
ports_type_list() ->
[{Port, PortType} || Port <- erlang:ports(),
{_, PortType} <- [erlang:port_info(Port, name)]].
ports_type_count(Types) ->
DictTypes = lists:foldl(fun(Type, Acc)->
dict:update_counter(Type, 1, Acc)
end, dict:new(), Types),
dict:to_list(DictTypes).
mapping(Entries) ->
mapping(Entries, []).
mapping([], Acc) ->
Acc;
mapping([{owner, V}|Entries], Acc) when is_pid(V) ->
OwnerInfo = process_info(V),
Owner = proplists:get_value(registered_name, OwnerInfo, undefined),
mapping(Entries, [{owner, pid_port_fun_to_atom(Owner)}|Acc]);
mapping([{Key, Value}|Entries], Acc) ->
mapping(Entries, [{Key, pid_port_fun_to_atom(Value)}|Acc]).
%ip_to_binary(Tuple) ->
% iolist_to_binary(string:join(lists:map(fun integer_to_list/1, tuple_to_list(Tuple)), ".")).
convert_pid_info({initial_call,{_M, F, _A}}) ->
{initial_call, F};
convert_pid_info({current_function, {M, F, A}}) ->
{current_function, list_to_atom(lists:concat([atom_to_list(M),":",atom_to_list(F),"/",integer_to_list(A)]))};
convert_pid_info({suspending, List}) ->
{suspending, [pid_port_fun_to_atom(E) || E <- List]};
convert_pid_info({binary, List}) ->
{binary,[tuple_to_list(E) || E <- List]};
convert_pid_info({Key, Term}) when is_pid(Term) or is_port(Term) or is_function(Term) ->
{Key, pid_port_fun_to_atom(Term)};
convert_pid_info(Item) ->
Item.
%convert_port_info({name, Name}) ->
% {name, list_to_binary(Name)};
%convert_port_info({links, List}) ->
% {links, [pid_port_fun_to_atom(Item) || Item <- List]};
%convert_port_info({connected, Pid}) ->
% erlang:process_info(Pid, registered_name);
%convert_port_info(Item) ->
% Item.
get_ets_dets_info(Type, Tab) ->
case Type:info(Tab) of
undefined -> [];
Entries when is_list(Entries) ->
[{Key, pid_port_fun_to_atom(Value)} || {Key, Value} <- Entries]
end.
pid_port_fun_to_atom(Term) when is_pid(Term) -> pid_port_fun_to_atom(Term) when is_pid(Term) ->
erlang:list_to_atom(pid_to_list(Term)); erlang:list_to_atom(pid_to_list(Term));