Fix handling of dead processes in recon:info/2
Closes https://github.com/ferd/recon/issues/95
diff --git a/README.md b/README.md
index 0cefdfc..71eb450 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,8 @@
*2.x*
+- 2.5.3 (unpublished)
+ - [Handle dead processes in `recon:info/2` types and edge cases](https://github.com/ferd/recon/pull/97)
- 2.5.2
- [Increase Dialyzer strictness](https://github.com/ferd/recon/pull/88)
- [Accumulate all block entries in `format_blocks`](https://github.com/ferd/recon/pull/83)
diff --git a/src/recon.app.src b/src/recon.app.src
index 0fc4df5..9fab2f5 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.5.2"},
+ {vsn, "2.5.3"},
{modules, [recon, recon_alloc, recon_lib, recon_trace, recon_rec]},
{registered, []},
{applications, [kernel, stdlib]},
diff --git a/src/recon.erl b/src/recon.erl
index 707f55d..9fc62af 100644
--- a/src/recon.erl
+++ b/src/recon.erl
@@ -174,6 +174,7 @@
%% another registry supported in the `{via, Module, Name}' syntax (must have a
%% `Module:whereis_name/1' function). Pids can also be passed in as a string
%% (`"<0.39.0>"') or a triple (`{0,39,0}') and will be converted to be used.
+%% Returns `undefined' as a value when a process died.
-spec info(pid_term()) -> [{info_type(), [{info_key(), Value}]},...] when
Value :: term().
info(PidTerm) ->
@@ -198,9 +199,9 @@
%% A fake attribute `binary_memory' is also available to return the
%% amount of memory used by refc binaries for a process.
-dialyzer({no_contracts, info/2}). % ... Overloaded contract for recon:info/2 has overlapping domains
--spec info(pid_term(), info_type()) -> {info_type(), [{info_key(), term()}]}
- ; (pid_term(), [atom()]) -> [{atom(), term()}]
- ; (pid_term(), atom()) -> {atom(), term()}.
+-spec info(pid_term(), info_type()) -> {info_type(), [{info_key(), term()}] | undefined}
+ ; (pid_term(), [atom()]) -> [{atom(), term()}] | undefined
+ ; (pid_term(), atom()) -> {atom(), term()} | undefined.
info(PidTerm, meta) ->
info_type(PidTerm, meta, [registered_name, dictionary, group_leader,
status]);
@@ -226,8 +227,12 @@
%% @private wrapper around `erlang:process_info/2' that allows special
%% attribute handling for items like `binary_memory'.
proc_info(Pid, binary_memory) ->
- {binary, Bins} = erlang:process_info(Pid, binary),
- {binary_memory, recon_lib:binary_memory(Bins)};
+ case erlang:process_info(Pid, binary) of
+ undefined ->
+ undefined;
+ {binary, Bins} ->
+ {binary_memory, recon_lib:binary_memory(Bins)}
+ end;
proc_info(Pid, Term) when is_atom(Term) ->
erlang:process_info(Pid, Term);
proc_info(Pid, List) when is_list(List) ->
diff --git a/test/recon_SUITE.erl b/test/recon_SUITE.erl
index dbb4d86..af1730d 100644
--- a/test/recon_SUITE.erl
+++ b/test/recon_SUITE.erl
@@ -21,7 +21,7 @@
node_stats_list, get_state, source, tcp, udp, files, port_types,
inet_count, inet_window, binary_memory, scheduler_usage].
-groups() -> [{info, [], [info3, info4, info1, info2,
+groups() -> [{info, [], [info3, info4, info1, info2, info_dead,
port_info1, port_info2]}].
init_per_group(info, Config) ->
@@ -144,6 +144,31 @@
K1 == K2]),
unregister(info2).
+info_dead(_Config) ->
+ Pid = spawn(fun() -> ok end),
+ timer:sleep(10),
+ Categories = [{meta, [registered_name, dictionary, group_leader, status]},
+ {signals, [links, monitors, monitored_by, trap_exit]},
+ {location, [initial_call, current_stacktrace]},
+ {memory_used, [memory, message_queue_len, heap_size,
+ total_heap_size, garbage_collection]},
+ {work, [reductions]}],
+ undefined = recon:info(Pid, registered_name),
+ Keys = lists:flatten([K || {_,L} <- Categories, K <- L]),
+ %% check that individual category calls return undefined values
+ [] = lists:flatten(
+ [GetCat || {Cat, _List} <- Categories,
+ {GetCat,Info} <- [recon:info(Pid, Cat)],
+ Cat =:= GetCat,
+ undefined =/= Info]
+ ),
+ true = lists:all(fun(X) -> X == undefined end,
+ [recon:info(Pid, K) || K <- Keys]),
+ undefined = recon:info(Pid, Keys),
+ %% Special virtual record.
+ undefined = recon:info(Pid, binary_memory),
+ ok.
+
proc_count(_Config) ->
Res = recon:proc_count(memory, 10),
true = proc_attrs(Res),