Permalink
Browse files

api to retrieve the VM stats

Code to expose the vm stats to couch_stats. For now just steal the code
from Folsom (https://github.com/boundary/folsom). HTTP api to expose it
is coming in another commit.
  • Loading branch information...
1 parent 00b0c1d commit 32fea300b2883727cb50136780bcddb31c522e8f @benoitc benoitc committed Sep 25, 2013
Showing with 389 additions and 0 deletions.
  1. +116 −0 apps/couch_stats/src/couch_stats.hrl
  2. +273 −0 apps/couch_stats/src/couch_stats_vm.erl
@@ -0,0 +1,116 @@
+-define(SYSTEM_INFO, [
+ allocated_areas,
+ allocator,
+ alloc_util_allocators,
+ build_type,
+ c_compiler_used,
+ check_io,
+ compat_rel,
+ cpu_topology,
+ creation,
+ debug_compiled,
+ dist,
+ dist_ctrl,
+ driver_version,
+ elib_malloc,
+ dist_buf_busy_limit,
+ %fullsweep_after, % included in garbage_collection
+ garbage_collection,
+ %global_heaps_size, % deprecated
+ heap_sizes,
+ heap_type,
+ info,
+ kernel_poll,
+ loaded,
+ logical_processors,
+ logical_processors_available,
+ logical_processors_online,
+ machine,
+ %min_heap_size, % included in garbage_collection
+ %min_bin_vheap_size, % included in garbage_collection
+ modified_timing_level,
+ multi_scheduling,
+ multi_scheduling_blockers,
+ otp_release,
+ process_count,
+ process_limit,
+ scheduler_bind_type,
+ scheduler_bindings,
+ scheduler_id,
+ schedulers,
+ schedulers_online,
+ smp_support,
+ system_version,
+ system_architecture,
+ threads,
+ thread_pool_size,
+ trace_control_word,
+ update_cpu_info,
+ version,
+ wordsize
+ ]).
+
+-define(STATISTICS, [
+ context_switches,
+ %exact_reductions, % use reductions instead
+ garbage_collection,
+ io,
+ reductions,
+ run_queue,
+ runtime,
+ wall_clock
+ ]).
+
+-define(PROCESS_INFO, [
+ backtrace,
+ binary,
+ catchlevel,
+ current_function,
+ %dictionary,
+ error_handler,
+ garbage_collection,
+ group_leader,
+ heap_size,
+ initial_call,
+ links,
+ last_calls,
+ memory,
+ %message_binary,
+ message_queue_len,
+ messages,
+ min_heap_size,
+ min_bin_vheap_size,
+ monitored_by,
+ monitors,
+ priority,
+ reductions,
+ registered_name,
+ sequential_trace_token,
+ stack_size,
+ status,
+ suspending,
+ total_heap_size,
+ trace,
+ trap_exit
+ ]).
+
+-define(SOCKET_OPTS, [
+ active,
+ broadcast,
+ delay_send,
+ dontroute,
+ exit_on_close,
+ header,
+ keepalive,
+ nodelay,
+ packet,
+ packet_size,
+ read_packets,
+ recbuf,
+ reuseaddr,
+ send_timeout,
+ send_timeout_close,
+ sndbuf,
+ priority,
+ tos
+ ]).
@@ -0,0 +1,273 @@
+%%%
+%%% 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(couch_stats_vm).
+
+-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("couch_stats.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.

0 comments on commit 32fea30

Please sign in to comment.