Skip to content

Commit

Permalink
Extracting sublist_top_n, adding tests
Browse files Browse the repository at this point in the history
This allows a more complete match and description of prior requirements
there.
  • Loading branch information
ferd committed Aug 29, 2016
1 parent 70f1673 commit 8c69535
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 55 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -29,6 +29,8 @@ all stable changes of the first version of Recon.

- 2.3.2 (current Master)
- Allow the `return_to` option in `recon_trace`
- More efficient sorting function for procs and ports attributes
(thanks to @zhongwencool and @pichi)
- 2.3.1
- Updated `app_deps` script to run with rebar3 dependencies
- Minor docs update
Expand Down
67 changes: 14 additions & 53 deletions src/recon.erl
Expand Up @@ -260,7 +260,7 @@ proc_fake([_|T1], [H|T2]) ->
AttributeName :: atom(),
Num :: non_neg_integer().
proc_count(AttrName, Num) ->
sublist_top_n(recon_lib:proc_attrs(AttrName), Num).
recon_lib:sublist_top_n_attrs(recon_lib:proc_attrs(AttrName), Num).

%% @doc Fetches a given attribute from all processes (except the
%% caller) and returns the biggest entries, over a sliding time window.
Expand Down Expand Up @@ -294,7 +294,7 @@ proc_count(AttrName, Num) ->
proc_window(AttrName, Num, Time) ->
Sample = fun() -> recon_lib:proc_attrs(AttrName) end,
{First,Last} = recon_lib:sample(Time, Sample),
sublist_top_n(recon_lib:sliding_window(First, Last), Num).
recon_lib:sublist_top_n_attrs(recon_lib:sliding_window(First, Last), Num).

%% @doc Refc binaries can be leaking when barely-busy processes route them
%% around and do little else, or when extremely busy processes reach a stable
Expand All @@ -311,15 +311,16 @@ proc_window(AttrName, Num, Time) ->
%% for more details on refc binaries
-spec bin_leak(pos_integer()) -> [proc_attrs()].
bin_leak(N) ->
Procs = sublist_top_n([try
{ok, {_,Pre,Id}} = recon_lib:proc_attrs(binary, Pid),
erlang:garbage_collect(Pid),
{ok, {_,Post,_}} = recon_lib:proc_attrs(binary, Pid),
{Pid, length(Pre) - length(Post), Id}
catch
_:_ -> {Pid, {0, 0}, []}
end || Pid <- processes()],
N),
Procs = recon_lib:sublist_top_n_attrs([
try
{ok, {_,Pre,Id}} = recon_lib:proc_attrs(binary, Pid),
erlang:garbage_collect(Pid),
{ok, {_,Post,_}} = recon_lib:proc_attrs(binary, Pid),
{Pid, length(Pre) - length(Post), Id}
catch
_:_ -> {Pid, {0, 0}, []}
end || Pid <- processes()
], N),
[{Pid, -Val, Id} ||{Pid, Val, Id} <-Procs].

%% @doc Shorthand for `node_stats(N, Interval, fun(X,_) -> io:format("~p~n",[X]) end, nostate)'.
Expand Down Expand Up @@ -543,7 +544,7 @@ port_types() ->
| 'cnt' | 'oct',
Num :: non_neg_integer().
inet_count(Attr, Num) ->
sublist_top_n(recon_lib:inet_attrs(Attr), Num).
recon_lib:sublist_top_n_attrs(recon_lib:inet_attrs(Attr), Num).

%% @doc Fetches a given attribute from all inet ports (TCP, UDP, SCTP)
%% and returns the biggest entries, over a sliding time window.
Expand All @@ -565,7 +566,7 @@ inet_count(Attr, Num) ->
inet_window(Attr, Num, Time) when is_atom(Attr) ->
Sample = fun() -> recon_lib:inet_attrs(Attr) end,
{First,Last} = recon_lib:sample(Time, Sample),
sublist_top_n(recon_lib:sliding_window(First, Last), Num).
recon_lib:sublist_top_n_attrs(recon_lib:sliding_window(First, Last), Num).

%% @doc Allows to be similar to `erlang:port_info/1', but allows
%% more flexible port usage: usual ports, ports that were registered
Expand Down Expand Up @@ -701,43 +702,3 @@ named_rpc(Nodes=[_|_], Fun, Timeout) when is_function(Fun,0) ->
named_rpc(Node, Fun, Timeout) when is_atom(Node) ->
named_rpc([Node], Fun, Timeout).

%% @private Returns the top n element of List. n = Len
sublist_top_n(List, Len) ->
pheap_fill(List, Len, []).

pheap_fill(List, 0, Heap) ->
pheap_full(List, Heap);
pheap_fill([], _, Heap) ->
pheap_to_list(Heap, []);
pheap_fill([{Y, X, _} = H|T], N, Heap) ->
pheap_fill(T, N-1, insert({{X, Y}, H}, Heap)).

pheap_full([], Heap) ->
pheap_to_list(Heap, []);
pheap_full([{Y, X, _} = H|T], [{K, _}|HeapT] = Heap) ->
case {X, Y} of
N when N > K ->
pheap_full(T, insert({N, H}, merge_pairs(HeapT)));
_ ->
pheap_full(T, Heap)
end.

pheap_to_list([], Acc) -> Acc;
pheap_to_list([{_, H}|T], Acc) ->
pheap_to_list(merge_pairs(T), [H|Acc]).

-compile({inline, [ insert/2
, merge/2
]}).

insert(E, []) -> [E]; %% merge([E], H)
insert(E, [E2|_] = H) when E =< E2 -> [E, H];
insert(E, [E2|H]) -> [E2, [E]|H].

merge(H1, []) -> H1;
merge([E1|H1], [E2|_]=H2) when E1 =< E2 -> [E1, H2|H1];
merge(H1, [E2|H2]) -> [E2, H1|H2].

merge_pairs([]) -> [];
merge_pairs([H]) -> H;
merge_pairs([A, B|T]) -> merge(merge(A, B), merge_pairs(T)).
49 changes: 48 additions & 1 deletion src/recon_lib.erl
Expand Up @@ -12,7 +12,8 @@
triple_to_pid/3, term_to_pid/1,
term_to_port/1,
time_map/5, time_fold/6,
scheduler_usage_diff/2]).
scheduler_usage_diff/2,
sublist_top_n_attrs/2]).
%% private exports
-export([binary_memory/1]).

Expand Down Expand Up @@ -224,7 +225,53 @@ scheduler_usage_diff(First, Last) ->
lists:zip(lists:sort(First), lists:sort(Last))
).

%% @doc Returns the top n element of a list of process or inet attributes
-spec sublist_top_n_attrs([Attrs], pos_integer()) -> [Attrs]
when Attrs :: recon:proc_attrs() | recon:inet_attrs().
sublist_top_n_attrs(_, 0) ->
%% matching lists:sublist/2 behaviour
[];
sublist_top_n_attrs(List, Len) ->
pheap_fill(List, Len, []).

%% @private crush binaries from process_info into their amount of place
%% taken in memory.
binary_memory(Bins) ->
lists:foldl(fun({_,Mem,_}, Tot) -> Mem+Tot end, 0, Bins).

%%%%%%%%%%%%%%%
%%% PRIVATE %%%
%%%%%%%%%%%%%%%
pheap_fill(List, 0, Heap) ->
pheap_full(List, Heap);
pheap_fill([], _, Heap) ->
pheap_to_list(Heap, []);
pheap_fill([{Y, X, _} = H|T], N, Heap) ->
pheap_fill(T, N-1, insert({{X, Y}, H}, Heap)).

pheap_full([], Heap) ->
pheap_to_list(Heap, []);
pheap_full([{Y, X, _} = H|T], [{K, _}|HeapT] = Heap) ->
case {X, Y} of
N when N > K ->
pheap_full(T, insert({N, H}, merge_pairs(HeapT)));
_ ->
pheap_full(T, Heap)
end.

pheap_to_list([], Acc) -> Acc;
pheap_to_list([{_, H}|T], Acc) ->
pheap_to_list(merge_pairs(T), [H|Acc]).

-compile({inline, [insert/2, merge/2]}).
insert(E, []) -> [E]; %% merge([E], H)
insert(E, [E2|_] = H) when E =< E2 -> [E, H];
insert(E, [E2|H]) -> [E2, [E]|H].

merge(H1, []) -> H1;
merge([E1|H1], [E2|_]=H2) when E1 =< E2 -> [E1, H2|H1];
merge(H1, [E2|H2]) -> [E2, H1|H2].

merge_pairs([]) -> [];
merge_pairs([H]) -> H;
merge_pairs([A, B|T]) -> merge(merge(A, B), merge_pairs(T)).
14 changes: 13 additions & 1 deletion test/recon_lib_SUITE.erl
Expand Up @@ -2,7 +2,7 @@
-include_lib("common_test/include/ct.hrl").
-compile(export_all).

all() -> [scheduler_usage_diff].
all() -> [scheduler_usage_diff, sublist_top_n].

scheduler_usage_diff(_Config) ->
{Active0, Total0} = {1000, 2000},
Expand All @@ -15,3 +15,15 @@ scheduler_usage_diff(_Config) ->
% Check for 100% usage
SchedStat2 = {1, Active0 + 1000, Total0 + 1000},
[{1, 1.0}] = recon_lib:scheduler_usage_diff([SchedStat0], [SchedStat2]).

sublist_top_n(_Config) ->
L0 = [1,1,2,4,5,6,0,8,7,4,5,2,1,8,agbg,{t},3,[bah],"te",<<"bin">>,23.0, 23],
L = [{make_ref(), Val, [{meta,data}]} || Val <- L0],
%% former sort function used prior to integraton of sublist_top_n
Sorted = lists:usort(fun({_,A,_},{_,B,_}) -> A > B end, L),
[begin
Sub = (catch lists:sublist(Sorted, N)),
ct:pal("Sub ~p: ~p", [N, Sub]),
Sub = (catch recon_lib:sublist_top_n_attrs(L, N))
end || N <- lists:seq(0, length(L)+1)],
ok.

0 comments on commit 8c69535

Please sign in to comment.