Skip to content
Permalink
Browse files
Fix recon_lib failing on long ports or proc lists
Nodes with lots of processes or ports can see their proc_count,
proc_window, inet_count, or inet_window functions fail due to a race
condition where:

1. The list of ports or processes is created;
2. The ports or processes are iteratively polled for their properties;
3. Some port or process closes;
4. A badmatch error occurs and the entire function fails.

The error specifically happens in the functions of arity 2 in recon_lib
that made the fetch to each port or process.

The interface of these functions are getting changed to:

- account for the error
- return {ok, State} or {error, Reason} depending on the case

Moreover, the functions of arity 1 in recon_lib that make use of them
are changing so that their list comprehension filters bad data --
which we do not care about anyway.

A similar change is included to respect the new API in recon's refc
binary leak function.
  • Loading branch information
ferd committed Aug 9, 2013
1 parent c453a5b commit e293c18710bc82808a7b21e8c0dda5eef1f105fb
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 14 deletions.
@@ -12,5 +12,10 @@ Documentation for the library can be obtained at http://ferd.github.io/recon/
Changelog
---------

- 0.4.0: fixed bug where nodes with lots of processes or ports could see their
count or window functions fail because a process or socket closed between the
time the function started and before it finished. This ends up changing the
API in `recon_lib` for the window and count functions that take a specific
pid as an argument.
- 0.3.1: factored out some logic from `recon:info/1` into `recon_lib:term_to_pid`
and allowed arbitrary terms to be used for pids in `recon:get_state/1`.
@@ -1,6 +1,6 @@
{application, recon,
[{description, "Diagnostic tools for production use"},
{vsn, "0.3.1"},
{vsn, "0.4.0"},
{modules, [recon]},
{registered, []},
{applications, [kernel, stdlib]}]}.
@@ -209,9 +209,9 @@ bin_leak(N) ->
lists:usort(
fun({K1,V1,_},{K2,V2,_}) -> {V1,K1} =< {V2,K2} end,
[try
{_,Pre,Id} = recon_lib:proc_attrs(binary, Pid),
{ok, {_,Pre,Id}} = recon_lib:proc_attrs(binary, Pid),
erlang:garbage_collect(Pid),
{_,Post,_} = recon_lib:proc_attrs(binary, Pid),
{ok, {_,Post,_}} = recon_lib:proc_attrs(binary, Pid),
{Pid, length(Post)-length(Pre), Id}
catch
_:_ -> {Pid, 0}
@@ -67,16 +67,19 @@ port_list(Attr, Val) ->
-spec proc_attrs(term()) -> [recon:proc_attrs()].
proc_attrs(AttrName) ->
[Attrs || Pid <- processes() -- [self()],
Attrs <- [proc_attrs(AttrName, Pid)]].
{ok, Attrs} <- [proc_attrs(AttrName, Pid)]].

%% @doc Returns the attributes of a given process. This form of attributes
%% is standard for most comparison functions for processes in recon.
-spec proc_attrs(term(), pid()) -> recon:proc_attrs().
-spec proc_attrs(term(), pid()) -> {ok, recon:proc_attrs()} | {error, term()}.
proc_attrs(AttrName, Pid) ->
[{_, Attr}, {registered_name,Name}, Init, Cur] =
process_info(Pid, [AttrName, registered_name,
current_function, initial_call]),
{Pid, Attr, [Name || is_atom(Name)]++[Init, Cur]}.
case process_info(Pid, [AttrName, registered_name,
current_function, initial_call]) of
[{_, Attr}, {registered_name,Name}, Init, Cur] ->
{ok, {Pid, Attr, [Name || is_atom(Name)]++[Init, Cur]}};
undefined ->
{error, undefined}
end.

%% @doc Returns the attributes ({@link recon:inet_attrs()}) of
%% all inet ports (UDP, SCTP, TCP) of the node.
@@ -88,12 +91,13 @@ inet_attrs(AttrName) ->
Name =:= "udp_inet" orelse
Name =:= "sctp_inet"],
[Attrs || Port <- Ports,
Attrs <- [inet_attrs(AttrName, Port)]].
{ok, Attrs} <- [inet_attrs(AttrName, Port)]].

%% @doc Returns the attributes required for a given inet port (UDP,
%% SCTP, TCP). This form of attributes is standard for most comparison
%% functions for processes in recon.
-spec inet_attrs(AttributeName, port()) -> recon:inet_attrs() when
-spec inet_attrs(AttributeName, port()) -> {ok,recon:inet_attrs()}
| {error,term()} when
AttributeName :: 'recv_cnt' | 'recv_oct' | 'send_cnt' | 'send_oct'
| 'cnt' | 'oct'.
inet_attrs(Attr, Port) ->
@@ -102,9 +106,13 @@ inet_attrs(Attr, Port) ->
oct -> [recv_oct, send_oct];
_ -> [Attr]
end,
{ok, Props} = inet:getstat(Port, Attrs),
ValSum = lists:foldl(fun({_,X},Y) -> X+Y end, 0, Props),
{Port,ValSum,Props}.
case inet:getstat(Port, Attrs) of
{ok, Props} ->
ValSum = lists:foldl(fun({_,X},Y) -> X+Y end, 0, Props),
{ok, {Port,ValSum,Props}};
{error, Reason} ->
{error, Reason}
end.


%% @doc Equivalent of `pid(X,Y,Z)' in the Erlang shell.

0 comments on commit e293c18

Please sign in to comment.