From 2a9b7a1c7816812dd637933a8fbf61bed6742785 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Sat, 28 Nov 2009 21:11:35 -0500 Subject: gen_fsm: Fix format_status/2 to handle Pids gen_fsm:format_status/2 currently crashes if Name in the process state is a Pid. Handle Name in the same way as in gen_server:format_status/2 to eliminate the crash. --- lib/stdlib/src/gen_fsm.erl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl index f3775f967a..ba0275ae2b 100644 --- a/lib/stdlib/src/gen_fsm.erl +++ b/lib/stdlib/src/gen_fsm.erl @@ -603,7 +603,12 @@ get_msg(Msg) -> Msg. format_status(Opt, StatusData) -> [PDict, SysState, Parent, Debug, [Name, StateName, StateData, Mod, _Time]] = StatusData, - Header = lists:concat(["Status for state machine ", Name]), + NameTag = if is_pid(Name) -> + pid_to_list(Name); + is_atom(Name) -> + Name + end, + Header = lists:concat(["Status for state machine ", NameTag]), Log = sys:get_debug(log, Debug, []), Specfic = case erlang:function_exported(Mod, format_status, 2) of -- cgit v1.2.3 From 88b530ea24977081020feb2123124063e58dfc12 Mon Sep 17 00:00:00 2001 From: Steve Vinoski Date: Fri, 27 Nov 2009 12:37:13 -0500 Subject: Teach sys:get_status/1,2 to call Mod:format_status/2 Restore the ability for gen_server and gen_fsm callback modules to format their own state for display under the sys:get_status/1,2 calls. This ability is extremely useful for new behavior modules based on gen_server or gen_fsm, so that they can display their status in a more meaningful way than just dumping the state record. It is also generally useful for applications wanting to display their gen_server or gen_fsm callback module state in something other than the default manner. Also document the previously undocumented the gen_server:format_status/2 and gen_fsm:format_status/2 optional callback functions that, if exported by the callback module, are invoked when sys:get_status/1,2 are called. Add unit tests to ensure that format_status/2 functions exported from a gen_fsm callback module and a gen_server callback module are called when sys:get_status/1,2 are called. --- lib/stdlib/doc/src/gen_fsm.xml | 30 ++++++++++++++++++++++++++++++ lib/stdlib/doc/src/gen_server.xml | 29 +++++++++++++++++++++++++++++ lib/stdlib/doc/src/sys.xml | 11 +++++++++++ lib/stdlib/src/sys.erl | 11 ++++++++++- lib/stdlib/test/gen_fsm_SUITE.erl | 16 +++++++++++++--- lib/stdlib/test/gen_server_SUITE.erl | 29 ++++++++++++++++++++++++----- 6 files changed, 117 insertions(+), 9 deletions(-) diff --git a/lib/stdlib/doc/src/gen_fsm.xml b/lib/stdlib/doc/src/gen_fsm.xml index f5d8b9bb48..739cd0bffd 100644 --- a/lib/stdlib/doc/src/gen_fsm.xml +++ b/lib/stdlib/doc/src/gen_fsm.xml @@ -729,6 +729,36 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 updated internal data.

+ + Module:format_status(normal, [PDict, StateData]) -> Status + Optional function for providing a term describing the + current gen_fsm status. + + PDict = [{Key, Value}] + StateData = term() + Status = [term()] + + +

This callback is optional, so callback modules need not + export it. The gen_fsm module provides a default + implementation of this function that returns the callback + module state data.

+

This function is called by a gen_fsm process when one + of sys:get_status/1,2 + is invoked to get the gen_fsm status. A callback module + wishing to customise the sys:get_status/1,2 return + value exports an instance of format_status/2 that + returns a term describing the current status of the + gen_fsm.

+

PDict is the current value of the gen_fsm's + process dictionary.

+

StateData is the internal state data of the + gen_fsm.

+

The function should return Status, a list of one or + more terms that customise the details of the current state + and status of the gen_fsm.

+
+
diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml index 8496802259..30c04d1d52 100644 --- a/lib/stdlib/doc/src/gen_server.xml +++ b/lib/stdlib/doc/src/gen_server.xml @@ -598,6 +598,35 @@ gen_server:abcast -----> Module:handle_cast/2

The function should return the updated internal state.

+ + Module:format_status(normal, [PDict, State]) -> Status + Optional function for providing a term describing the + current gen_server status. + + PDict = [{Key, Value}] + State = term() + Status = [term()] + + +

This callback is optional, so callback modules need not + export it. The gen_server module provides a default + implementation of this function that returns the callback + module state.

+

This function is called by a gen_server process when one + of sys:get_status/1,2 + is invoked to get the gen_server status. A callback module + wishing to customise the sys:get_status/1,2 return + value exports an instance of format_status/2 that + returns a term describing the current status of the + gen_server.

+

PDict is the current value of the gen_server's + process dictionary.

+

State is the internal state of the gen_server.

+

The function should return Status, a list of one or + more terms that customise the details of the current state + and status of the gen_server.

+
+
diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml index a395a8a415..51fd5ee8d4 100644 --- a/lib/stdlib/doc/src/sys.xml +++ b/lib/stdlib/doc/src/sys.xml @@ -237,6 +237,17 @@

Gets the status of the process.

+

The value of Misc varies for different types of + processes. For example, a gen_server process returns + the callback module's state, and a gen_fsm process + returns information such as its current state name. Callback + modules for gen_server and gen_fsm can also + customise the value of Misc by exporting + a format_status/2 function that contributes + module-specific information; + see gen_server:format_status/2 + and gen_fsm:format_status/2 + for more details.

diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl index e0f2dbcd3c..12209c16d7 100644 --- a/lib/stdlib/src/sys.erl +++ b/lib/stdlib/src/sys.erl @@ -245,8 +245,17 @@ do_cmd(SysState, Other, _Parent, _Mod, Debug, Misc) -> {SysState, {error, {unknown_system_msg, Other}}, Debug, Misc}. get_status(SysState, Parent, Mod, Debug, Misc) -> + PDict = get(), + FmtMisc = + case erlang:function_exported(Mod, format_status, 2) of + true -> + FmtArgs = [PDict, SysState, Parent, Debug, Misc], + Mod:format_status(normal, FmtArgs); + _ -> + Misc + end, {status, self(), {module, Mod}, - [get(), SysState, Parent, Debug, Misc]}. + [PDict, SysState, Parent, Debug, FmtMisc]}. %%----------------------------------------------------------------- %% These are the system debug commands. diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl index 62f8b2f9dd..23c1d9a193 100644 --- a/lib/stdlib/test/gen_fsm_SUITE.erl +++ b/lib/stdlib/test/gen_fsm_SUITE.erl @@ -30,7 +30,7 @@ -export([shutdown/1]). --export([sys/1, sys1/1]). +-export([sys/1, sys1/1, call_format_status/1]). -export([hibernate/1,hiber_idle/3,hiber_wakeup/3,hiber_idle/2,hiber_wakeup/2]). @@ -42,7 +42,7 @@ % The gen_fsm behaviour -export([init/1, handle_event/3, handle_sync_event/4, terminate/3, - handle_info/3]). + handle_info/3, format_status/2]). -export([idle/2, idle/3, timeout/2, wfor_conf/2, wfor_conf/3, @@ -305,7 +305,7 @@ shutdown(Config) when is_list(Config) -> ok. -sys(suite) -> [sys1]. +sys(suite) -> [sys1, call_format_status]. sys1(Config) when is_list(Config) -> ?line {ok, Pid} = @@ -317,6 +317,13 @@ sys1(Config) when is_list(Config) -> ?line sys:resume(Pid), ?line stop_it(Pid). +call_format_status(Config) when is_list(Config) -> + ?line {ok, Pid} = gen_fsm:start(gen_fsm_SUITE, [], []), + ?line Status = sys:get_status(Pid), + ?line {status, Pid, _Mod, [_PDict, running, _Parent, _, Data]} = Status, + ?line [format_status_called | _] = lists:reverse(Data), + ?line stop_it(Pid). + %% Hibernation hibernate(suite) -> []; @@ -836,3 +843,6 @@ handle_sync_event(stop_shutdown_reason, _From, _State, Data) -> {stop, {shutdown,reason}, {shutdown,reason}, Data}; handle_sync_event({get, _Pid}, _From, State, Data) -> {reply, {state, State, Data}, State, Data}. + +format_status(_Opt, [_Pdict, _StateData]) -> + [format_status_called]. diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl index 86a5a65ba3..6efdce78a1 100644 --- a/lib/stdlib/test/gen_server_SUITE.erl +++ b/lib/stdlib/test/gen_server_SUITE.erl @@ -30,7 +30,7 @@ call_remote_n1/1, call_remote_n2/1, call_remote_n3/1, spec_init/1, spec_init_local_registered_parent/1, spec_init_global_registered_parent/1, - otp_5854/1, hibernate/1, otp_7669/1 + otp_5854/1, hibernate/1, otp_7669/1, call_format_status/1 ]). % spawn export @@ -42,7 +42,7 @@ % The gen_server behaviour -export([init/1, handle_call/3, handle_cast/2, - handle_info/2, terminate/2]). + handle_info/2, terminate/2, format_status/2]). all(suite) -> [start, crash, call, cast, cast_fast, info, @@ -51,7 +51,7 @@ all(suite) -> call_remote_n2, call_remote_n3, spec_init, spec_init_local_registered_parent, spec_init_global_registered_parent, - otp_5854,hibernate,otp_7669]. + otp_5854, hibernate, otp_7669, call_format_status]. -define(default_timeout, ?t:minutes(1)). @@ -851,7 +851,7 @@ otp_5854(Config) when is_list(Config) -> ok. %% If initialization fails (with ignore or {stop,Reason}), -%% make sure that the process is not registered when gen_sever:start() +%% make sure that the process is not registered when gen_server:start() %% returns. otp_7669(Config) when is_list(Config) -> @@ -887,6 +887,24 @@ do_otp_7669_stop() -> ?MODULE, stop, []), ?line undefined = global:whereis_name(?MODULE). +%% Verify that sys:get_status correctly calls our format_status/2 fun +%% +call_format_status(suite) -> + []; +call_format_status(doc) -> + ["Test that sys:get_status/1,2 calls format_status/2"]; +call_format_status(Config) when is_list(Config) -> + ?line {ok, Pid} = gen_server:start_link({local, call_format_status}, + gen_server_SUITE, [], []), + ?line Status1 = sys:get_status(call_format_status), + ?line {status, Pid, _Mod, [_PDict, running, _Parent, _, Data1]} = Status1, + ?line [format_status_called | _] = lists:reverse(Data1), + ?line Status2 = sys:get_status(call_format_status, 5000), + ?line {status, Pid, _Mod, [_PDict, running, _Parent, _, Data2]} = Status2, + ?line [format_status_called | _] = lists:reverse(Data2), + ok. + + %%-------------------------------------------------------------- %% Help functions to spec_init_* start_link(Init, Options) -> @@ -1046,4 +1064,5 @@ terminate({From, stopped_info}, _State) -> terminate(_Reason, _State) -> ok. - +format_status(_Opt, [_PDict, _State]) -> + [format_status_called]. -- cgit v1.2.3