%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1998-2018. All Rights Reserved.
%%
%% 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.
%%
%% %CopyrightEnd%
%%
-module(instrument).
-export([allocations/0, allocations/1,
carriers/0, carriers/1]).
-type block_histogram() :: tuple().
-type allocation_summary() ::
{HistogramStart :: non_neg_integer(),
UnscannedSize :: non_neg_integer(),
Allocations :: #{ Origin :: atom() =>
#{ Type :: atom() => block_histogram() }}}.
-spec allocations() -> {ok, Result} | {error, Reason} when
Result :: allocation_summary(),
Reason :: not_enabled.
allocations() ->
allocations(#{}).
-spec allocations(Options) -> {ok, Result} | {error, Reason} when
Result :: allocation_summary(),
Reason :: not_enabled,
Options :: #{ scheduler_ids => list(non_neg_integer()),
allocator_types => list(atom()),
histogram_start => pos_integer(),
histogram_width => pos_integer() }.
allocations(Options) ->
Ref = make_ref(),
Defaults = #{ scheduler_ids => lists:seq(0, erlang:system_info(schedulers)),
allocator_types => erlang:system_info(alloc_util_allocators),
histogram_start => 128,
histogram_width => 18 },
{HistStart, MsgCount} =
dispatch_gather(maps:merge(Defaults, Options), Ref,
fun erts_internal:gather_alloc_histograms/1),
alloc_hist_receive(HistStart, MsgCount, Ref).
alloc_hist_receive(_HistStart, 0, _Ref) ->
{error, not_enabled};
alloc_hist_receive(HistStart, MsgCount, Ref) when MsgCount > 0 ->
{Unscanned, Histograms} = alloc_hist_receive_1(MsgCount, Ref, 0, #{}),
{ok, {HistStart, Unscanned, Histograms}}.
alloc_hist_receive_1(0, _Ref, Unscanned, Result) ->
{Unscanned, Result};
alloc_hist_receive_1(MsgCount, Ref, Unscanned0, Result0) ->
receive
{Ref, Unscanned, Tags} ->
Result = lists:foldl(fun alloc_hist_fold_result/2, Result0, Tags),
alloc_hist_receive_1(MsgCount - 1, Ref, Unscanned0 + Unscanned, Result)
end.
alloc_hist_fold_result({Id, Type, BlockHist}, Result0) ->
IdAllocs0 = maps:get(Id, Result0, #{}),
MergedHists = case maps:find(Type, IdAllocs0) of
{ok, PrevHist} ->
alloc_hist_merge_hist(tuple_size(BlockHist),
BlockHist,
PrevHist);
error ->
BlockHist
end,
IdAllocs = IdAllocs0#{ Type => MergedHists },
Result0#{ Id => IdAllocs }.
alloc_hist_merge_hist(0, A, _B) ->
A;
alloc_hist_merge_hist(Index, A, B) ->
Merged = setelement(Index, A, element(Index, A) + element(Index, B)),
alloc_hist_merge_hist(Index - 1, Merged, B).
-type carrier_info_list() ::
{HistogramStart :: non_neg_integer(),
Carriers :: [{AllocatorType :: atom(),
TotalSize :: non_neg_integer(),
UnscannedSize :: non_neg_integer(),
AllocatedSize :: non_neg_integer(),
AllocatedCount :: non_neg_integer(),
InPool :: boolean(),
FreeBlocks :: block_histogram()}]}.
-spec carriers() -> {ok, Result} | {error, Reason} when
Result :: carrier_info_list(),
Reason :: not_enabled.
carriers() ->
carriers(#{}).
-spec carriers(Options) -> {ok, Result} | {error, Reason} when
Result :: carrier_info_list(),
Reason :: not_enabled,
Options :: #{ scheduler_ids => list(non_neg_integer()),
allocator_types => list(atom()),
histogram_start => pos_integer(),
histogram_width => pos_integer() }.
carriers(Options) ->
Ref = make_ref(),
Defaults = #{ scheduler_ids => lists:seq(0, erlang:system_info(schedulers)),
allocator_types => erlang:system_info(alloc_util_allocators),
histogram_start => 512,
histogram_width => 14 },
{HistStart, MsgCount} =
dispatch_gather(maps:merge(Defaults, Options), Ref,
fun erts_internal:gather_carrier_info/1),
carrier_info_receive(HistStart, MsgCount, Ref).
carrier_info_receive(_HistStart, 0, _Ref) ->
{error, not_enabled};
carrier_info_receive(HistStart, MsgCount, Ref) ->
{ok, {HistStart, carrier_info_receive_1(MsgCount, Ref, [])}}.
carrier_info_receive_1(0, _Ref, Result) ->
lists:flatten(Result);
carrier_info_receive_1(MsgCount, Ref, Result0) ->
receive
{Ref, Carriers} ->
carrier_info_receive_1(MsgCount - 1, Ref, [Carriers, Result0])
end.
dispatch_gather(#{ allocator_types := AllocatorTypes,
scheduler_ids := SchedulerIds,
histogram_start := HistStart,
histogram_width := HistWidth }, Ref, Gather)
when is_list(AllocatorTypes),
is_list(SchedulerIds),
HistStart >= 1, HistStart =< (1 bsl 28),
HistWidth >= 1, HistWidth =< 32 ->
MsgCount = lists:sum(
[Gather({AllocatorType, SchedId, HistWidth, HistStart, Ref}) ||
SchedId <- SchedulerIds,
AllocatorType <- AllocatorTypes]),
{HistStart, MsgCount};
dispatch_gather(_, _, _) ->
error(badarg).