Merge pull request #66 from ferd/otp-21-support

OTP-21 support
diff --git a/.travis.yml b/.travis.yml
index 8f55847..0267385 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,20 +1,14 @@
 sudo: false
 language: erlang
 otp_release:
+  - 21.0
   - 20.0
   - 19.3
   - 18.3
   - 17.3
   - 17.1
   - 17.0
-  - R16B03-1
-  - R16B03
-  - R16B02
-  - R16B01
-  - R16B
-  - R15B03
-  - R15B02
-script: "rm -rf deps ebin test/*.beam logs && ./rebar get-deps compile && ./rebar ct"
+script: "wget https://s3.amazonaws.com/rebar3/rebar3 && escript rebar3 do edoc, eunit, ct"
 branches:
   only:
     - master
diff --git a/README.md b/README.md
index 02058e4..611e2e5 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@
 
 [![Build Status](https://travis-ci.org/ferd/recon.png)](https://travis-ci.org/ferd/recon)
 
-Versions supported: R15B02 and up
+Versions supported: OTP-17 and up. Support of R16B03-1 down to R15B02 is best effort.
 
 Changelog
 ---------
@@ -27,6 +27,11 @@
 
 *2.x*
 
+- 2.3.6
+  - Adapting for OTP-21. Includes the 'deprecation' of `recon:files/0`
+    since OTP-21 no longer supports listing all file descriptors, and
+    removing `error_logger_queue_len` from node stats since a new
+    logging mechanism was introduced in-process instead.
 - 2.3.5
   - fixing timefold's first iteration to prevent errors at call-site
     by sleeping before sampling
diff --git a/docsite.erl b/docsite.erl
index a071002..5077904 100755
--- a/docsite.erl
+++ b/docsite.erl
@@ -1,6 +1,6 @@
 #!/usr/bin/env escript
 %% -*- erlang -*-
-%%! -smp disable
+%%!
 %% This script takes the EDoc output page, and tries to bring them to
 %% a more modern format suitable for a recon site.
 %% Run with `escript docsite.erl' or as `./docsite.erl'. The script
diff --git a/rebar.config b/rebar.config
new file mode 100644
index 0000000..a417350
--- /dev/null
+++ b/rebar.config
@@ -0,0 +1,5 @@
+{profiles, [
+    {test, [
+        {erl_opts, [nowarn_export_all]}
+    ]}
+]}.
diff --git a/src/recon.app.src b/src/recon.app.src
index 8f0dcd6..b6da751 100644
--- a/src/recon.app.src
+++ b/src/recon.app.src
@@ -1,6 +1,6 @@
 {application, recon,
  [{description, "Diagnostic tools for production use"},
-  {vsn, "2.3.5"},
+  {vsn, "2.3.6"},
   {modules, [recon, recon_alloc, recon_lib, recon_trace]},
   {registered, []},
   {applications, [kernel, stdlib]},
diff --git a/src/recon.erl b/src/recon.erl
index 9b43ce0..c085cb0 100644
--- a/src/recon.erl
+++ b/src/recon.erl
@@ -376,7 +376,8 @@
 %%
 %% Absolutes are values that keep changing with time, and are useful to know
 %% about as a datapoint: process count, size of the run queue, error_logger
-%% queue length, and the memory of the node (total, processes, atoms, binaries,
+%% queue length in versions before OTP-21 or those thar run it explicitely,
+%% and the memory of the node (total, processes, atoms, binaries,
 %% and ets tables).
 %%
 %% Increments are values that are mostly useful when compared to a previous
@@ -392,6 +393,10 @@
       Stats :: {[Absolutes::{atom(),term()}],
                 [Increments::{atom(),term()}]}.
 node_stats(N, Interval, FoldFun, Init) ->
+    Logger = case whereis(error_logger) of
+        undefined -> logger;
+        _ -> error_logger
+    end,
     %% Turn on scheduler wall time if it wasn't there already
     FormerFlag = erlang:system_flag(scheduler_wall_time, true),
     %% Stats is an ugly fun, but it does its thing.
@@ -399,7 +404,14 @@
         %% Absolutes
         ProcC = erlang:system_info(process_count),
         RunQ = erlang:statistics(run_queue),
-        {_,LogQ} = process_info(whereis(error_logger),  message_queue_len),
+        LogQ = case Logger of
+            error_logger ->
+                {_,LogQLen} = process_info(whereis(error_logger),
+                                           message_queue_len),
+                LogQLen;
+            _ ->
+                undefined
+        end,
         %% Mem (Absolutes)
         Mem = erlang:memory(),
         Tot = proplists:get_value(total, Mem),
@@ -418,8 +430,9 @@
         SchedWallNew = erlang:statistics(scheduler_wall_time),
         SchedUsage = recon_lib:scheduler_usage_diff(SchedWall, SchedWallNew),
          %% Stats Results
-        {{[{process_count,ProcC}, {run_queue,RunQ},
-           {error_logger_queue_len,LogQ}, {memory_total,Tot},
+        {{[{process_count,ProcC}, {run_queue,RunQ}] ++
+          [{error_logger_queue_len,LogQ} || LogQ =/= undefined] ++
+          [{memory_total,Tot},
            {memory_procs,ProcM}, {memory_atoms,Atom},
            {memory_bin,Bin}, {memory_ets,Ets}],
           [{bytes_in,BytesIn}, {bytes_out,BytesOut},
@@ -511,6 +524,9 @@
 sctp() -> recon_lib:port_list(name, "sctp_inet").
 
 %% @doc returns a list of all file handles open on the node.
+%% @deprecated Starting with OTP-21, files are implemented as NIFs
+%% and can no longer be listed. This function returns an empty list
+%% in such a case.
 -spec files() -> [port()].
 files() -> recon_lib:port_list(name, "efile").
 
diff --git a/test/recon_SUITE.erl b/test/recon_SUITE.erl
index 3122157..6cf63fb 100644
--- a/test/recon_SUITE.erl
+++ b/test/recon_SUITE.erl
@@ -7,6 +7,16 @@
 -include_lib("eunit/include/eunit.hrl").
 -compile(export_all).
 
+-ifdef(OTP_RELEASE).
+-define(FILES_IMPL, nif).
+-define(ERROR_LOGGER_MATCH(_),  ).
+-define(REDUCTIONS_MATCH(X), X).
+-else.
+-define(FILES_IMPL, port).
+-define(ERROR_LOGGER_MATCH(X), X,).
+-define(REDUCTIONS_MATCH(_), []).
+-endif.
+
 all() -> [{group,info}, proc_count, proc_window, bin_leak,
           node_stats_list, get_state, source, tcp, udp, files, port_types,
           inet_count, inet_window, binary_memory, scheduler_usage].
@@ -29,6 +39,17 @@
 end_per_group(info, Config) ->
     exit(?config(pid, Config), kill).
 
+init_per_testcase(files, Config) ->
+    case ?FILES_IMPL of
+        nif -> {skip, "files can no longer be listed in OTP-21 and above"};
+        port -> Config
+    end;
+init_per_testcase(_, Config) ->
+    Config.
+
+end_per_testcase(_, Config) ->
+    Config.
+
 %%%%%%%%%%%%%
 %%% TESTS %%%
 %%%%%%%%%%%%%
@@ -36,15 +57,18 @@
 info3(Config) ->
     Pid = ?config(pid, Config),
     {A,B,C} = pid_to_triple(Pid),
-    Info = recon:info(Pid),
-    Info = recon:info(A,B,C).
-
+    Info1 = recon:info(Pid),
+    Info2 = recon:info(A,B,C),
+    %% Reduction count is unreliable
+    ?assertMatch(?REDUCTIONS_MATCH(
+                    [{work, [{reductions,_}]}, {work, [{reductions,_}]}]
+                 ), (Info1 -- Info2) ++ (Info2 -- Info1)).
 
 info4(Config) ->
     Pid = ?config(pid, Config),
     Keys = [meta, signals, location, memory_used, work,
-            links, monitors, reductions, messages,
-            [links, monitors, reductions, messages]],
+            links, monitors, messages,
+            [links, monitors, messages]],
     {A,B,C} = pid_to_triple(Pid),
     lists:map(fun(Key) ->
                   Info = recon:info(Pid, Key),
@@ -67,11 +91,25 @@
               undefined == proplists:get_value(K, proplists:get_value(Cat,Info))
         ]),
     register(info1, Pid),
-    Res = recon:info(info1),
-    Res = recon:info(whereis(info1)),
-    Res = recon:info(pid_to_triple(whereis(info1))),
-    Res = recon:info(lists:flatten(io_lib:format("~p",[Pid]))),
-    unregister(info1).
+    Res1 = recon:info(info1),
+    Res2 = recon:info(whereis(info1)),
+    Res3 = recon:info(pid_to_triple(whereis(info1))),
+    Res4 = recon:info(lists:flatten(io_lib:format("~p",[Pid]))),
+    unregister(info1),
+    L = lists:usort(Res1 ++ Res2 ++ Res3 ++ Res4),
+    ?assertMatch(?REDUCTIONS_MATCH([{work,[{reductions,_}]},
+                                    {work,[{reductions,_}]},
+                                    {work,[{reductions,_}]}]), L -- Res1),
+    ?assertMatch(?REDUCTIONS_MATCH([{work,[{reductions,_}]},
+                                    {work,[{reductions,_}]},
+                                    {work,[{reductions,_}]}]), L -- Res2),
+    ?assertMatch(?REDUCTIONS_MATCH([{work,[{reductions,_}]},
+                                    {work,[{reductions,_}]},
+                                    {work,[{reductions,_}]}]), L -- Res3),
+    ?assertMatch(?REDUCTIONS_MATCH([{work,[{reductions,_}]},
+                                    {work,[{reductions,_}]},
+                                    {work,[{reductions,_}]}]), L -- Res4),
+    ok.
 
 info2(Config) ->
     Pid = ?config(pid, Config),
@@ -82,7 +120,7 @@
                                  total_heap_size, garbage_collection]},
                   {work, [reductions]}],
     %% registered_name is special -- only returns
-    %% [] when passed through by info/2. Because we pass terms through
+    %% [] when passed through by info/2. Because we pass terms through
     %% according to the docs, we have to respect that
     [] = recon:info(Pid, registered_name),
     %% Register to get the expected tuple
@@ -138,7 +176,7 @@
     Res = recon:node_stats_list(2,100),
     2 = length([1 || {[{process_count,_},
                        {run_queue,_},
-                       {error_logger_queue_len,_},
+                       ?ERROR_LOGGER_MATCH({error_logger_queue_len,_})
                        {memory_total,_},
                        {memory_procs,_},
                        {memory_atoms,_},