Merge pull request #50 from ferd/fix-pheap

Fix pheap
diff --git a/README.md b/README.md
index 77120da..aec9c96 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,8 @@
 
 - 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
diff --git a/src/recon.erl b/src/recon.erl
index 2a3f83e..1d56897 100644
--- a/src/recon.erl
+++ b/src/recon.erl
@@ -260,7 +260,7 @@
       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.
@@ -294,7 +294,7 @@
 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
@@ -311,15 +311,16 @@
 %% 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)'.
@@ -543,7 +544,7 @@
                      | '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.
@@ -565,7 +566,7 @@
 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
@@ -701,43 +702,3 @@
 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([], 0, 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)).
diff --git a/src/recon_lib.erl b/src/recon_lib.erl
index 5a49cf3..8bae1db 100644
--- a/src/recon_lib.erl
+++ b/src/recon_lib.erl
@@ -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]).
 
@@ -224,7 +225,53 @@
         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)).
diff --git a/test/recon_lib_SUITE.erl b/test/recon_lib_SUITE.erl
index 4a69df3..5c022c8 100644
--- a/test/recon_lib_SUITE.erl
+++ b/test/recon_lib_SUITE.erl
@@ -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},
@@ -15,3 +15,15 @@
     % 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.