Merge branch 'trace-fn' of https://github.com/evanmcc/recon into evanmcc-trace-fn
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 15ba606..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,17 @@
 %% for more details on refc binaries
 -spec bin_leak(pos_integer()) -> [proc_attrs()].
 bin_leak(N) ->
-    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(Post)-length(Pre), Id}
-                   catch
-                       _:_ -> {Pid, 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)'.
 -spec node_stats_print(Repeat, Interval) -> term() when
@@ -542,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.
@@ -564,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
@@ -700,27 +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) ->
-    sublist_top_n(List, Len, 0, []).
-
-sublist_top_n([], _, _, Acc) ->
-    lists:reverse(Acc);
-sublist_top_n([NewKey = {New1, New2, _}|List], MaxLen, Len, Acc) ->
-    {NewLen, NewAcc} =
-        if Len < MaxLen -> {Len + 1, insert(NewKey, Acc)};
-           true ->
-                [{Small1, Small2, _}|Acc2] = Acc,
-                if {Small2, Small1} < {New2, New1}  -> {Len, insert(NewKey, Acc2)};
-                   true -> {Len, Acc}
-                end
-        end,
-    sublist_top_n(List, MaxLen, NewLen, NewAcc).
-
-%% @private insert Key in an ordered list
-insert(K, []) -> [K];
-insert({K1, K2, _} = K, [{H1, H2, _} = H|T]) ->
-    if {H2, H1} =< {K2, K1}  -> [H|insert(K, T)];
-       true -> [K, H|T]
-    end.
-
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_SUITE.erl b/test/recon_SUITE.erl
index 63060bc..3122157 100644
--- a/test/recon_SUITE.erl
+++ b/test/recon_SUITE.erl
@@ -127,6 +127,7 @@
     Res = recon:bin_leak(5),
     5 = length(Res),
     true = proc_attrs(Res),
+    true = lists:any(fun({_,Val,_})-> Val =/= 0  end, Res),
     %% all results are =< 0, and ordered from smallest to biggest
     lists:foldl(fun({_,Current,_}, Next) when Current =< Next -> Current end,
                 0,
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.