From 5f29ba57f3e73dbd71e04cfd4a587453ccb6ac23 Mon Sep 17 00:00:00 2001 From: Fred Hebert Date: Sat, 30 Oct 2021 23:03:35 +0000 Subject: [PATCH] Fix handling of dead processes in recon:info/2 Closes https://github.com/ferd/recon/issues/95 --- README.md | 2 ++ src/recon.app.src | 2 +- src/recon.erl | 15 ++++++++++----- test/recon_SUITE.erl | 27 ++++++++++++++++++++++++++- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0cefdfc..71eb450 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ all stable changes of the first version of Recon. *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 @@ info(A,B,C, Key) -> info(recon_lib:triple_to_pid(A,B,C), Key). %% 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 @@ info(PidTerm) -> %% 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 @@ info_type(PidTerm, Type, Keys) -> %% @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 @@ 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]. -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 @@ info2(Config) -> 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),