1%%% 2%%% Copyright 2011, Boundary 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 17 18%%%------------------------------------------------------------------- 19%%% File: folsom_vm_metrics.erl 20%%% @author joe williams <j@boundary.com> 21%%% @doc 22%%% convert erlang system metrics to proplists 23%%% @end 24%%%----------------------------------------------------------------- 25 26-module(folsom_vm_metrics). 27 28-export([get_system_info/0, 29 get_statistics/0, 30 get_memory/0, 31 get_process_info/0, 32 get_port_info/0, 33 get_ets_info/0, 34 get_dets_info/0 35 ]). 36 37%% exported for eunit test 38-export([convert_system_info/2]). 39 40-include("folsom.hrl"). 41 42 43% api 44 45get_memory() -> 46 erlang:memory(). 47 48get_statistics() -> 49 [{Key, convert_statistics(Key, get_statistics(Key))} || Key <- ?STATISTICS]. 50 51get_system_info() -> 52 [{Key, convert_system_info(Key, get_system_info(Key))} || Key <- ?SYSTEM_INFO]. 53 54get_process_info() -> 55 [{pid_port_fun_to_atom(Pid), get_process_info(Pid)} || Pid <- processes()]. 56 57get_port_info() -> 58 [{pid_port_fun_to_atom(Port), get_port_info(Port)} || Port <- erlang:ports()]. 59 60get_ets_info() -> 61 [{Tab, get_ets_dets_info(ets, Tab)} || Tab <- ets:all()]. 62 63get_dets_info() -> 64 [{Tab, get_ets_dets_info(dets, Tab)} || Tab <- dets:all()]. 65 66% internal functions 67 68% wrap system_info and statistics in a try/catch in case keys are missing 69% in old/new versions of erlang 70 71get_system_info(Key) -> 72 try erlang:system_info(Key) catch 73 error:badarg->undefined 74 end. 75 76get_statistics(Key) -> 77 try erlang:statistics(Key) catch 78 error:badarg->undefined 79 end. 80 81%% conversion functions for erlang:statistics(Key) 82 83convert_statistics(context_switches, {ContextSwitches, 0}) -> 84 ContextSwitches; 85convert_statistics(garbage_collection, {NumberofGCs, WordsReclaimed, 0}) -> 86 [{number_of_gcs, NumberofGCs}, {words_reclaimed, WordsReclaimed}]; 87convert_statistics(io, {Input, Output}) -> 88 [Input, Output]; 89convert_statistics(reductions, {TotalReductions, ReductionsSinceLastCall}) -> 90 [{total_reductions, TotalReductions}, 91 {reductions_since_last_call, ReductionsSinceLastCall}]; 92convert_statistics(runtime, {TotalRunTime, TimeSinceLastCall}) -> 93 [{total_run_time, TotalRunTime}, {time_since_last_call, TimeSinceLastCall}]; 94convert_statistics(wall_clock, {TotalWallclockTime, WallclockTimeSinceLastCall}) -> 95 [{total_wall_clock_time, TotalWallclockTime}, 96 {wall_clock_time_since_last_call, WallclockTimeSinceLastCall}]; 97convert_statistics(_, Value) -> 98 Value. 99 100%% conversion functions for erlang:system_info(Key) 101 102convert_system_info(allocated_areas, List) -> 103 [convert_allocated_areas(Value) || Value <- List]; 104convert_system_info(allocator, {_,_,_,List}) -> 105 List; 106convert_system_info(c_compiler_used, {Compiler, Version}) -> 107 [{compiler, Compiler}, {version, convert_c_compiler_version(Version)}]; 108convert_system_info(cpu_topology, undefined) -> 109 undefined; 110convert_system_info(cpu_topology, List) when is_list(List) -> 111 [{Type, convert_cpu_topology(Item, [])} || {Type, Item} <- List]; 112convert_system_info(cpu_topology, {logical,Item}) -> 113 convert_system_info(cpu_topology, [{processor,[{core,{logical,Item}}]}]); 114convert_system_info(dist_ctrl, List) -> 115 lists:map(fun({Node, Socket}) -> 116 {ok, Stats} = inet:getstat(Socket), 117 {Node, Stats} 118 end, List); 119convert_system_info(driver_version, Value) -> 120 list_to_binary(Value); 121convert_system_info(machine, Value) -> 122 list_to_binary(Value); 123convert_system_info(otp_release, Value) -> 124 list_to_binary(Value); 125convert_system_info(scheduler_bindings, Value) -> 126 tuple_to_list(Value); 127convert_system_info(system_version, Value) -> 128 list_to_binary(Value); 129convert_system_info(system_architecture, Value) -> 130 list_to_binary(Value); 131convert_system_info(version, Value) -> 132 list_to_binary(Value); 133convert_system_info(_, Value) -> 134 Value. 135 136convert_allocated_areas({Key, Value1, Value2}) -> 137 {Key, [Value1, Value2]}; 138convert_allocated_areas({Key, Value}) -> 139 {Key, Value}. 140 141convert_c_compiler_version({A, B, C}) -> 142 list_to_binary(io_lib:format("~p.~p.~p", [A, B, C])); 143convert_c_compiler_version({A, B}) -> 144 list_to_binary(io_lib:format("~p.~p", [A, B])); 145convert_c_compiler_version(A) -> 146 list_to_binary(io_lib:format("~p", [A])). 147 148convert_cpu_topology([{core, Value}| Tail], Acc) when is_tuple(Value) -> 149 convert_cpu_topology(Tail, lists:append(Acc, [{core, tuple_to_list(Value)}])); 150convert_cpu_topology([{core, Value}| Tail], Acc) when is_list(Value) -> 151 convert_cpu_topology(Tail, lists:append(Acc, [{core, convert_cpu_topology(Value, [])}])); 152convert_cpu_topology([{thread, Value}| Tail], Acc) -> 153 convert_cpu_topology(Tail, lists:append(Acc, [{thread, tuple_to_list(Value)}])); 154convert_cpu_topology([{node, Value}| Tail], Acc) -> 155 convert_cpu_topology(Tail, lists:append(Acc, [{node, convert_cpu_topology(Value, [])}])); 156convert_cpu_topology([{processor, Value}| Tail], Acc) -> 157 convert_cpu_topology(Tail, lists:append(Acc, [{processor, convert_cpu_topology(Value, [])}])); 158convert_cpu_topology({Key, Value}, _) -> 159 [{Key, Value}]; 160convert_cpu_topology([], Acc) -> 161 Acc. 162 163get_process_info(Pid) -> 164 Info = [process_info(Pid, Key) || Key <- ?PROCESS_INFO], 165 lists:flatten([convert_pid_info(Item) || Item <- Info]). 166 167get_port_info(Port) -> 168 Stat = get_socket_getstat(Port), 169 SockName = get_socket_sockname(Port), 170 Opts = get_socket_opts(Port), 171 Info = get_erlang_port_info(Port), 172 Protocol = get_socket_protocol(Port), 173 Status = get_socket_status(Port), 174 Type = get_socket_type(Port), 175 176 lists:flatten(lists:append([ 177 Stat, 178 SockName, 179 Opts, 180 Info, 181 Protocol, 182 Status, 183 Type 184 ])). 185 186get_socket_getstat(Socket) -> 187 case catch inet:getstat(Socket) of 188 {ok, Info} -> 189 Info; 190 _ -> 191 [] 192 end. 193 194get_socket_status(Socket) -> 195 case catch prim_inet:getstatus(Socket) of 196 {ok, Status} -> 197 [{status, Status}]; 198 _ -> 199 [] 200 end. 201 202get_erlang_port_info(Port) -> 203 Info = erlang:port_info(Port), 204 [convert_port_info(Item) || Item <- Info]. 205 206get_socket_type(Socket) -> 207 case catch prim_inet:gettype(Socket) of 208 {ok, Type} -> 209 [{type, tuple_to_list(Type)}]; 210 _ -> 211 [] 212 end. 213 214get_socket_opts(Socket) -> 215 [get_socket_opts(Socket, Key) || Key <- ?SOCKET_OPTS]. 216 217get_socket_opts(Socket, Key) -> 218 case catch inet:getopts(Socket, [Key]) of 219 {ok, Opt} -> 220 Opt; 221 _ -> 222 [] 223 end. 224 225get_socket_protocol(Socket) -> 226 case erlang:port_info(Socket, name) of 227 {name, "tcp_inet"} -> 228 [{protocol, tcp}]; 229 {name, "udp_inet"} -> 230 [{protocol, udp}]; 231 {name,"sctp_inet"} -> 232 [{protocol, sctp}]; 233 _ -> 234 [] 235 end. 236 237get_socket_sockname(Socket) -> 238 case catch inet:sockname(Socket) of 239 {ok, {Ip, Port}} -> 240 [{ip, ip_to_binary(Ip)}, {port, Port}]; 241 _ -> 242 [] 243 end. 244 245get_ets_dets_info(Type, Tab) -> 246 case Type:info(Tab) of 247 undefined -> []; 248 Entries when is_list(Entries) -> 249 [{Key, pid_port_fun_to_atom(Value)} || {Key, Value} <- Entries] 250 end. 251 252ip_to_binary(Tuple) -> 253 iolist_to_binary(string:join(lists:map(fun integer_to_list/1, tuple_to_list(Tuple)), ".")). 254 255convert_port_info({name, Name}) -> 256 {name, list_to_binary(Name)}; 257convert_port_info({links, List}) -> 258 {links, [pid_port_fun_to_atom(Item) || Item <- List]}; 259convert_port_info({connected, Pid}) -> 260 {connected, pid_port_fun_to_atom(Pid)}; 261convert_port_info(Item) -> 262 Item. 263 264convert_pid_info({current_function, MFA}) -> 265 {current_function, tuple_to_list(MFA)}; 266convert_pid_info({Key, Term}) when is_pid(Term) or is_port(Term) or is_function(Term) -> 267 {Key, pid_port_fun_to_atom(Term)}; 268convert_pid_info({links, List}) -> 269 {links, [pid_port_fun_to_atom(Item) || Item <- List]}; 270convert_pid_info({suspending, List}) -> 271 {suspending, [pid_port_fun_to_atom(Item) || Item <- List]}; 272convert_pid_info({monitors, List}) -> 273 {monitors, [{Key, pid_port_fun_to_atom(Value)} || {Key, Value} <- List]}; 274convert_pid_info({monitored_by, List}) -> 275 {monitored_by, [pid_port_fun_to_atom(Item) || Item <- List]}; 276convert_pid_info({binary, List}) -> 277 {binary, [tuple_to_list(Item) || Item <- List]}; 278convert_pid_info({initial_call, MFA}) -> 279 {inital_call, tuple_to_list(MFA)}; 280convert_pid_info(Item) -> 281 Item. 282 283pid_port_fun_to_atom(Term) when is_pid(Term) -> 284 erlang:list_to_atom(pid_to_list(Term)); 285pid_port_fun_to_atom(Term) when is_port(Term) -> 286 erlang:list_to_atom(erlang:port_to_list(Term)); 287pid_port_fun_to_atom(Term) when is_function(Term) -> 288 erlang:list_to_atom(erlang:fun_to_list(Term)); 289pid_port_fun_to_atom(Term) -> 290 Term. 291