| %%% |
| %%% Copyright 2011, Boundary |
| %%% |
| %%% Licensed under the Apache License, Version 2.0 (the "License"); |
| %%% you may not use this file except in compliance with the License. |
| %%% You may obtain a copy of the License at |
| %%% |
| %%% http://www.apache.org/licenses/LICENSE-2.0 |
| %%% |
| %%% Unless required by applicable law or agreed to in writing, software |
| %%% distributed under the License is distributed on an "AS IS" BASIS, |
| %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| %%% See the License for the specific language governing permissions and |
| %%% limitations under the License. |
| %%% |
| |
| |
| %%%------------------------------------------------------------------- |
| %%% File: folsom_vm_metrics.erl |
| %%% @author joe williams <j@boundary.com> |
| %%% @doc |
| %%% convert erlang system metrics to proplists |
| %%% @end |
| %%%----------------------------------------------------------------- |
| |
| -module(folsom_vm_metrics). |
| |
| -export([get_system_info/0, |
| get_statistics/0, |
| get_memory/0, |
| get_process_info/0, |
| get_port_info/0, |
| get_ets_info/0, |
| get_dets_info/0 |
| ]). |
| |
| %% exported for eunit test |
| -export([convert_system_info/1]). |
| |
| -include("folsom.hrl"). |
| |
| |
| % api |
| |
| get_memory() -> |
| erlang:memory(). |
| |
| get_statistics() -> |
| [{Key, convert_statistics({Key, erlang:statistics(Key)})} || Key <- ?STATISTICS]. |
| |
| get_system_info() -> |
| [{Key, convert_system_info({Key, erlang:system_info(Key)})} || Key <- ?SYSTEM_INFO]. |
| |
| get_process_info() -> |
| [{pid_port_fun_to_atom(Pid), get_process_info(Pid)} || Pid <- processes()]. |
| |
| get_port_info() -> |
| [{pid_port_fun_to_atom(Port), get_port_info(Port)} || Port <- erlang:ports()]. |
| |
| get_ets_info() -> |
| [{Tab, get_ets_dets_info(ets, Tab)} || Tab <- ets:all()]. |
| |
| get_dets_info() -> |
| [{Tab, get_ets_dets_info(dets, Tab)} || Tab <- dets:all()]. |
| |
| % internal functions |
| |
| %% conversion functions for erlang:statistics(Key) |
| |
| convert_statistics({context_switches, {ContextSwitches, 0}}) -> |
| ContextSwitches; |
| convert_statistics({garbage_collection, {NumberofGCs, WordsReclaimed, 0}}) -> |
| [{"number_of_gcs", NumberofGCs}, {"words_reclaimed", WordsReclaimed}]; |
| convert_statistics({io, {Input, Output}}) -> |
| [Input, Output]; |
| convert_statistics({reductions, {TotalReductions, ReductionsSinceLastCall}}) -> |
| [{"total_reductions", TotalReductions}, |
| {"reductions_since_last_call", ReductionsSinceLastCall}]; |
| convert_statistics({runtime, {TotalRunTime, TimeSinceLastCall}}) -> |
| [{"total_run_time", TotalRunTime}, {"time_since_last_call", TimeSinceLastCall}]; |
| convert_statistics({wall_clock, {TotalWallclockTime, WallclockTimeSinceLastCall}}) -> |
| [{"Total_Wall_Clock_time", TotalWallclockTime}, |
| {"wall_clock_time_since_last_call", WallclockTimeSinceLastCall}]; |
| convert_statistics({_, Value}) -> |
| Value. |
| |
| %% conversion functions for erlang:system_info(Key) |
| |
| convert_system_info({allocated_areas, List}) -> |
| [convert_allocated_areas(Value) || Value <- List]; |
| convert_system_info({allocator, {_,_,_,List}}) -> |
| List; |
| convert_system_info({c_compiler_used, {Compiler, Version}}) -> |
| [{compiler, Compiler}, {version, convert_c_compiler_version(Version)}]; |
| convert_system_info({cpu_topology, undefined}) -> |
| undefined; |
| convert_system_info({cpu_topology, List}) when is_list(List) -> |
| [{Type, convert_cpu_topology(Item, [])} || {Type, Item} <- List]; |
| convert_system_info({cpu_topology, {logical,Item}}) -> |
| convert_system_info({cpu_topology, [{processor,[{core,{logical,Item}}]}]}); |
| convert_system_info({dist_ctrl, List}) -> |
| lists:map(fun({Node, Socket}) -> |
| {ok, Stats} = inet:getstat(Socket), |
| {Node, Stats} |
| end, List); |
| convert_system_info({driver_version, Value}) -> |
| list_to_binary(Value); |
| convert_system_info({machine, Value}) -> |
| list_to_binary(Value); |
| convert_system_info({otp_release, Value}) -> |
| list_to_binary(Value); |
| convert_system_info({scheduler_bindings, Value}) -> |
| tuple_to_list(Value); |
| convert_system_info({system_version, Value}) -> |
| list_to_binary(Value); |
| convert_system_info({system_architecture, Value}) -> |
| list_to_binary(Value); |
| convert_system_info({version, Value}) -> |
| list_to_binary(Value); |
| convert_system_info({_, Value}) -> |
| Value. |
| |
| convert_allocated_areas({Key, Value1, Value2}) -> |
| {Key, [Value1, Value2]}; |
| convert_allocated_areas({Key, Value}) -> |
| {Key, Value}. |
| |
| convert_c_compiler_version({A, B, C}) -> |
| list_to_binary(io_lib:format("~p.~p.~p", [A, B, C])); |
| convert_c_compiler_version({A, B}) -> |
| list_to_binary(io_lib:format("~p.~p", [A, B])); |
| convert_c_compiler_version(A) -> |
| list_to_binary(io_lib:format("~p", [A])). |
| |
| convert_cpu_topology([{core, Value}| Tail], Acc) when is_tuple(Value) -> |
| convert_cpu_topology(Tail, lists:append(Acc, [{core, tuple_to_list(Value)}])); |
| convert_cpu_topology([{core, Value}| Tail], Acc) when is_list(Value) -> |
| convert_cpu_topology(Tail, lists:append(Acc, [{core, convert_cpu_topology(Value, [])}])); |
| convert_cpu_topology([{thread, Value}| Tail], Acc) -> |
| convert_cpu_topology(Tail, lists:append(Acc, [{thread, tuple_to_list(Value)}])); |
| convert_cpu_topology([{node, Value}| Tail], Acc) -> |
| convert_cpu_topology(Tail, lists:append(Acc, [{node, convert_cpu_topology(Value, [])}])); |
| convert_cpu_topology([{processor, Value}| Tail], Acc) -> |
| convert_cpu_topology(Tail, lists:append(Acc, [{processor, convert_cpu_topology(Value, [])}])); |
| convert_cpu_topology({Key, Value}, _) -> |
| [{Key, Value}]; |
| convert_cpu_topology([], Acc) -> |
| Acc. |
| |
| get_process_info(Pid) -> |
| Info = [process_info(Pid, Key) || Key <- ?PROCESS_INFO], |
| lists:flatten([convert_pid_info(Item) || Item <- Info]). |
| |
| get_port_info(Port) -> |
| Stat = get_socket_getstat(Port), |
| SockName = get_socket_sockname(Port), |
| Opts = get_socket_opts(Port), |
| Info = get_erlang_port_info(Port), |
| Protocol = get_socket_protocol(Port), |
| Status = get_socket_status(Port), |
| Type = get_socket_type(Port), |
| |
| lists:flatten(lists:append([ |
| Stat, |
| SockName, |
| Opts, |
| Info, |
| Protocol, |
| Status, |
| Type |
| ])). |
| |
| get_socket_getstat(Socket) -> |
| case catch inet:getstat(Socket) of |
| {ok, Info} -> |
| Info; |
| _ -> |
| [] |
| end. |
| |
| get_socket_status(Socket) -> |
| case catch prim_inet:getstatus(Socket) of |
| {ok, Status} -> |
| [{status, Status}]; |
| _ -> |
| [] |
| end. |
| |
| get_erlang_port_info(Port) -> |
| Info = erlang:port_info(Port), |
| [convert_port_info(Item) || Item <- Info]. |
| |
| get_socket_type(Socket) -> |
| case catch prim_inet:gettype(Socket) of |
| {ok, Type} -> |
| [{type, tuple_to_list(Type)}]; |
| _ -> |
| [] |
| end. |
| |
| get_socket_opts(Socket) -> |
| [get_socket_opts(Socket, Key) || Key <- ?SOCKET_OPTS]. |
| |
| get_socket_opts(Socket, Key) -> |
| case catch inet:getopts(Socket, [Key]) of |
| {ok, Opt} -> |
| Opt; |
| _ -> |
| [] |
| end. |
| |
| get_socket_protocol(Socket) -> |
| case erlang:port_info(Socket, name) of |
| {name, "tcp_inet"} -> |
| [{protocol, tcp}]; |
| {name, "udp_inet"} -> |
| [{protocol, udp}]; |
| {name,"sctp_inet"} -> |
| [{protocol, sctp}]; |
| _ -> |
| [] |
| end. |
| |
| get_socket_sockname(Socket) -> |
| case catch inet:sockname(Socket) of |
| {ok, {Ip, Port}} -> |
| [{ip, ip_to_binary(Ip)}, {port, Port}]; |
| _ -> |
| [] |
| end. |
| |
| get_ets_dets_info(Type, Tab) -> |
| [{Key, pid_port_fun_to_atom(Value)} || {Key, Value} <- Type:info(Tab)]. |
| |
| ip_to_binary(Tuple) -> |
| iolist_to_binary(string:join(lists:map(fun integer_to_list/1, tuple_to_list(Tuple)), ".")). |
| |
| 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}) -> |
| {connected, pid_port_fun_to_atom(Pid)}; |
| convert_port_info(Item) -> |
| Item. |
| |
| convert_pid_info({current_function, MFA}) -> |
| {current_function, tuple_to_list(MFA)}; |
| 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({links, List}) -> |
| {links, [pid_port_fun_to_atom(Item) || Item <- List]}; |
| convert_pid_info({suspending, List}) -> |
| {suspending, [pid_port_fun_to_atom(Item) || Item <- List]}; |
| convert_pid_info({monitors, List}) -> |
| {monitors, [{Key, pid_port_fun_to_atom(Value)} || {Key, Value} <- List]}; |
| convert_pid_info({monitored_by, List}) -> |
| {monitored_by, [pid_port_fun_to_atom(Item) || Item <- List]}; |
| convert_pid_info({binary, List}) -> |
| {binary, [tuple_to_list(Item) || Item <- List]}; |
| convert_pid_info({initial_call, MFA}) -> |
| {inital_call, tuple_to_list(MFA)}; |
| convert_pid_info(Item) -> |
| Item. |
| |
| pid_port_fun_to_atom(Term) when is_pid(Term) -> |
| erlang:list_to_atom(pid_to_list(Term)); |
| pid_port_fun_to_atom(Term) when is_port(Term) -> |
| erlang:list_to_atom(erlang:port_to_list(Term)); |
| pid_port_fun_to_atom(Term) when is_function(Term) -> |
| erlang:list_to_atom(erlang:fun_to_list(Term)); |
| pid_port_fun_to_atom(Term) -> |
| Term. |