From a8d431d1d7c542f66aeb1c272a5a2edf3cd9130d Mon Sep 17 00:00:00 2001 From: Siraaj Khandkar Date: Mon, 14 Sep 2015 20:05:49 -0400 Subject: [PATCH] Collect ETS per-table memory and size. Handled only by the StatsD consumer. --- include/beam_stats.hrl | 3 +- include/beam_stats_ets_table.hrl | 6 ++++ src/beam_stats.app.src | 2 +- src/beam_stats_consumer_statsd.erl | 37 ++++++++++++++++++- src/beam_stats_ets.erl | 17 +++++++++ src/beam_stats_ets_table.erl | 44 +++++++++++++++++++++++ src/beam_stats_state.erl | 4 +++ test/beam_stats_consumer_statsd_SUITE.erl | 21 +++++++++++ 8 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 include/beam_stats_ets_table.hrl create mode 100644 src/beam_stats_ets.erl create mode 100644 src/beam_stats_ets_table.erl diff --git a/include/beam_stats.hrl b/include/beam_stats.hrl index 8698310..5624270 100644 --- a/include/beam_stats.hrl +++ b/include/beam_stats.hrl @@ -7,10 +7,11 @@ , context_switches :: non_neg_integer() , reductions :: non_neg_integer() , run_queue :: non_neg_integer() + , ets :: beam_stats_ets:t() + %, statistics :: [{atom() , term()}] %, system :: [{atom() , term()}] %, process :: [{atom() , term()}] %, port :: [{atom() , term()}] - %, ets :: [{atom() , term()}] %, dets :: [{atom() , term()}] }). diff --git a/include/beam_stats_ets_table.hrl b/include/beam_stats_ets_table.hrl new file mode 100644 index 0000000..e69e58e --- /dev/null +++ b/include/beam_stats_ets_table.hrl @@ -0,0 +1,6 @@ +-record(beam_stats_ets_table, + { id :: beam_stats_ets_table:id() + , name :: atom() + , size :: non_neg_integer() + , memory :: non_neg_integer() + }). diff --git a/src/beam_stats.app.src b/src/beam_stats.app.src index 9eb72ab..05fb345 100644 --- a/src/beam_stats.app.src +++ b/src/beam_stats.app.src @@ -1,7 +1,7 @@ {application, beam_stats, [ {description, "Periodic VM stats production and consumption."}, - {vsn, "0.5.2"}, + {vsn, "0.6.0"}, {registered, []}, {applications, [ kernel diff --git a/src/beam_stats_consumer_statsd.erl b/src/beam_stats_consumer_statsd.erl index 7a06d68..112bb91 100644 --- a/src/beam_stats_consumer_statsd.erl +++ b/src/beam_stats_consumer_statsd.erl @@ -1,6 +1,7 @@ -module(beam_stats_consumer_statsd). -include("include/beam_stats.hrl"). +-include("include/beam_stats_ets_table.hrl"). -include("beam_stats_logging.hrl"). -behaviour(beam_stats_consumer). @@ -147,6 +148,7 @@ beam_stats_to_bins(#beam_stats , context_switches = ContextSwitches , reductions = Reductions , run_queue = RunQueue + , ets = ETS } ) -> NodeIDBin = node_id_to_bin(NodeID), @@ -157,7 +159,8 @@ beam_stats_to_bins(#beam_stats , reductions_to_msg(Reductions) , run_queue_to_msg(RunQueue) | memory_to_msgs(Memory) - ], + ] + ++ ets_to_msgs(ETS), Msgs2 = [statsd_msg_add_name_prefix(M, NodeIDBin) || M <- Msgs1], [statsd_msg_to_bin(M) || M <- Msgs2]. @@ -206,6 +209,38 @@ io_bytes_out_to_msg(IOBytesOut) -> , type = gauge }. +-spec ets_to_msgs(beam_stats_ets:t()) -> + [statsd_msg()]. +ets_to_msgs(PerTableStats) -> + NestedMsgs = lists:map(fun ets_table_to_msgs/1, PerTableStats), + lists:append(NestedMsgs). + +-spec ets_table_to_msgs(beam_stats_ets_table:t()) -> + [statsd_msg()]. +ets_table_to_msgs(#beam_stats_ets_table + { id = ID + , name = Name + , size = Size + , memory = Memory + } +) -> + IDBin = beam_stats_ets_table:id_to_bin(ID), + NameBin = atom_to_binary(Name, latin1), + NameAndID = <>, + SizeMsg = + #statsd_msg + { name = <<"ets_table.size.", NameAndID/binary>> + , value = Size + , type = gauge + }, + MemoryMsg = + #statsd_msg + { name = <<"ets_table.memory.", NameAndID/binary>> + , value = Memory + , type = gauge + }, + [SizeMsg, MemoryMsg]. + -spec memory_to_msgs([{atom(), non_neg_integer()}]) -> [statsd_msg()]. memory_to_msgs(Memory) -> diff --git a/src/beam_stats_ets.erl b/src/beam_stats_ets.erl new file mode 100644 index 0000000..3965765 --- /dev/null +++ b/src/beam_stats_ets.erl @@ -0,0 +1,17 @@ +-module(beam_stats_ets). + +-export_type( + [ t/0 + ]). + +-export( + [ collect/0 + ]). + +-type t() :: + [beam_stats_ets_table:t()]. + +-spec collect() -> + t(). +collect() -> + lists:map(fun beam_stats_ets_table:of_id/1, ets:all()). diff --git a/src/beam_stats_ets_table.erl b/src/beam_stats_ets_table.erl new file mode 100644 index 0000000..b603656 --- /dev/null +++ b/src/beam_stats_ets_table.erl @@ -0,0 +1,44 @@ +-module(beam_stats_ets_table). + +-include("include/beam_stats_ets_table.hrl"). + +-export_type( + [ t/0 + , id/0 + ]). + +-export( + [ of_id/1 + , id_to_bin/1 + ]). + +-type id() :: + atom() + | ets:tid() + % integer() is just a workaround, to let us mock ets:tid(), which is + % opaque, but represented as an integer, without Dialyzer complaining. + | integer() + . + +-type t() :: + #?MODULE{}. + +-spec of_id(id()) -> + t(). +of_id(ID) -> + WordSize = erlang:system_info(wordsize), + NumberOfWords = ets:info(ID, memory), + NumberOfBytes = NumberOfWords * WordSize, + #?MODULE + { id = ID + , name = ets:info(ID, name) + , size = ets:info(ID, size) + , memory = NumberOfBytes + }. + +-spec id_to_bin(atom() | ets:tid()) -> + binary(). +id_to_bin(ID) when is_atom(ID) -> + atom_to_binary(ID, latin1); +id_to_bin(ID) when is_integer(ID) -> + integer_to_binary(ID). diff --git a/src/beam_stats_state.erl b/src/beam_stats_state.erl index f21f231..7c41fc0 100644 --- a/src/beam_stats_state.erl +++ b/src/beam_stats_state.erl @@ -15,6 +15,7 @@ -record(snapshots, { memory :: [{atom(), non_neg_integer()}] , run_queue :: non_neg_integer() + , ets :: beam_stats_ets:t() }). -type snapshots() :: @@ -83,6 +84,7 @@ export( #snapshots { memory = Memory , run_queue = RunQueue + , ets = ETS } , deltas = #deltas @@ -111,12 +113,14 @@ export( , context_switches = CurrentContextSwitches - PreviousContextSwitches , reductions = Reductions , run_queue = RunQueue + , ets = ETS }. snapshots_new() -> #snapshots { memory = erlang:memory() , run_queue = erlang:statistics(run_queue) + , ets = beam_stats_ets:collect() }. deltas_new() -> diff --git a/test/beam_stats_consumer_statsd_SUITE.erl b/test/beam_stats_consumer_statsd_SUITE.erl index f0cffb0..dd01455 100644 --- a/test/beam_stats_consumer_statsd_SUITE.erl +++ b/test/beam_stats_consumer_statsd_SUITE.erl @@ -1,6 +1,7 @@ -module(beam_stats_consumer_statsd_SUITE). -include_lib("beam_stats/include/beam_stats.hrl"). +-include_lib("beam_stats/include/beam_stats_ets_table.hrl"). -export( [ all/0 @@ -33,6 +34,21 @@ groups() -> %% ============================================================================= t_send(_Cfg) -> + ETSTableStatsFoo = + #beam_stats_ets_table + { id = foo + , name = foo + , size = 5 + , memory = 25 + }, + ETSTableStatsBar = + #beam_stats_ets_table + { id = 37 + , name = bar + , size = 8 + , memory = 38 + }, + % TODO: Indent #beam_stats as #beam_stats_ets_table BEAMStats = #beam_stats { timestamp = {1, 2, 3} , node_id = 'node_foo@host_bar' @@ -42,6 +58,7 @@ t_send(_Cfg) -> , context_switches = 5 , reductions = 9 , run_queue = 17 + , ets = [ETSTableStatsFoo, ETSTableStatsBar] }, ServerPort = 8125, {ok, ServerSocket} = gen_udp:open(ServerPort, [binary, {active, false}]), @@ -60,4 +77,8 @@ t_send(_Cfg) -> , "beam_stats.node_foo_host_bar.reductions:9|g\n" , "beam_stats.node_foo_host_bar.run_queue:17|g\n" , "beam_stats.node_foo_host_bar.memory.mem_type_foo:1|g\n" + , "beam_stats.node_foo_host_bar.ets_table.size.foo.foo:5|g\n" + , "beam_stats.node_foo_host_bar.ets_table.memory.foo.foo:25|g\n" + , "beam_stats.node_foo_host_bar.ets_table.size.bar.37:8|g\n" + , "beam_stats.node_foo_host_bar.ets_table.memory.bar.37:38|g\n" >> = Data. -- 2.20.1