aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib/src/sys.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib/src/sys.erl')
-rw-r--r--lib/stdlib/src/sys.erl391
1 files changed, 391 insertions, 0 deletions
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
new file mode 100644
index 0000000000..e0f2dbcd3c
--- /dev/null
+++ b/lib/stdlib/src/sys.erl
@@ -0,0 +1,391 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(sys).
+
+%% External exports
+-export([suspend/1, suspend/2, resume/1, resume/2,
+ get_status/1, get_status/2,
+ change_code/4, change_code/5,
+ log/2, log/3, trace/2, trace/3, statistics/2, statistics/3,
+ log_to_file/2, log_to_file/3, no_debug/1, no_debug/2,
+ install/2, install/3, remove/2, remove/3]).
+-export([handle_system_msg/6, handle_system_msg/7, handle_debug/4,
+ print_log/1, get_debug/3, debug_options/1, suspend_loop_hib/6]).
+
+%%-----------------------------------------------------------------
+%% Types
+%%-----------------------------------------------------------------
+
+-type name() :: pid() | atom() | {'global', atom()}.
+-type system_event() :: {'in', _Msg} | {'in', _Msg, _From} | {'out', _Msg, _To}.
+
+%%-----------------------------------------------------------------
+%% System messages
+%%-----------------------------------------------------------------
+suspend(Name) -> send_system_msg(Name, suspend).
+suspend(Name, Timeout) -> send_system_msg(Name, suspend, Timeout).
+
+resume(Name) -> send_system_msg(Name, resume).
+resume(Name, Timeout) -> send_system_msg(Name, resume, Timeout).
+
+get_status(Name) -> send_system_msg(Name, get_status).
+get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout).
+
+change_code(Name, Mod, Vsn, Extra) ->
+ send_system_msg(Name, {change_code, Mod, Vsn, Extra}).
+change_code(Name, Mod, Vsn, Extra, Timeout) ->
+ send_system_msg(Name, {change_code, Mod, Vsn, Extra}, Timeout).
+
+%%-----------------------------------------------------------------
+%% Debug commands
+%%-----------------------------------------------------------------
+
+-type log_flag() :: 'true' | {'true',pos_integer()} | 'false' | 'get' | 'print'.
+
+-spec log(name(), log_flag()) -> 'ok' | {'ok', [system_event()]}.
+log(Name, Flag) ->
+ send_system_msg(Name, {debug, {log, Flag}}).
+
+-spec log(name(), log_flag(), timeout()) -> 'ok' | {'ok', [system_event()]}.
+log(Name, Flag, Timeout) ->
+ send_system_msg(Name, {debug, {log, Flag}}, Timeout).
+
+-spec trace(name(), boolean()) -> 'ok'.
+trace(Name, Flag) ->
+ send_system_msg(Name, {debug, {trace, Flag}}).
+
+-spec trace(name(), boolean(), timeout()) -> 'ok'.
+trace(Name, Flag, Timeout) ->
+ send_system_msg(Name, {debug, {trace, Flag}}, Timeout).
+
+-type l2f_fname() :: string() | 'false'.
+
+-spec log_to_file(name(), l2f_fname()) -> 'ok' | {'error','open_file'}.
+log_to_file(Name, FileName) ->
+ send_system_msg(Name, {debug, {log_to_file, FileName}}).
+
+-spec log_to_file(name(), l2f_fname(), timeout()) -> 'ok' | {'error','open_file'}.
+log_to_file(Name, FileName, Timeout) ->
+ send_system_msg(Name, {debug, {log_to_file, FileName}}, Timeout).
+
+statistics(Name, Flag) ->
+ send_system_msg(Name, {debug, {statistics, Flag}}).
+statistics(Name, Flag, Timeout) ->
+ send_system_msg(Name, {debug, {statistics, Flag}}, Timeout).
+
+-spec no_debug(name()) -> 'ok'.
+no_debug(Name) -> send_system_msg(Name, {debug, no_debug}).
+
+-spec no_debug(name(), timeout()) -> 'ok'.
+no_debug(Name, Timeout) -> send_system_msg(Name, {debug, no_debug}, Timeout).
+
+install(Name, {Func, FuncState}) ->
+ send_system_msg(Name, {debug, {install, {Func, FuncState}}}).
+install(Name, {Func, FuncState}, Timeout) ->
+ send_system_msg(Name, {debug, {install, {Func, FuncState}}}, Timeout).
+
+remove(Name, Func) ->
+ send_system_msg(Name, {debug, {remove, Func}}).
+remove(Name, Func, Timeout) ->
+ send_system_msg(Name, {debug, {remove, Func}}, Timeout).
+
+%%-----------------------------------------------------------------
+%% All system messages sent are on the form {system, From, Msg}
+%% The receiving side should send Msg to handle_system_msg/5.
+%%-----------------------------------------------------------------
+send_system_msg(Name, Request) ->
+ case catch gen:call(Name, system, Request) of
+ {ok,Res} -> Res;
+ {'EXIT', Reason} -> exit({Reason, mfa(Name, Request)})
+ end.
+
+send_system_msg(Name, Request, Timeout) ->
+ case catch gen:call(Name, system, Request, Timeout) of
+ {ok,Res} -> Res;
+ {'EXIT', Reason} -> exit({Reason, mfa(Name, Request, Timeout)})
+ end.
+
+mfa(Name, {debug, {Func, Arg2}}) ->
+ {sys, Func, [Name, Arg2]};
+mfa(Name, {change_code, Mod, Vsn, Extra}) ->
+ {sys, change_code, [Name, Mod, Vsn, Extra]};
+mfa(Name, Atom) ->
+ {sys, Atom, [Name]}.
+mfa(Name, Req, Timeout) ->
+ {M, F, A} = mfa(Name, Req),
+ {M, F, A ++ [Timeout]}.
+
+%%-----------------------------------------------------------------
+%% Func: handle_system_msg/6
+%% Args: Msg ::= term()
+%% From ::= {pid(),Ref} but don't count on that
+%% Parent ::= pid()
+%% Module ::= atom()
+%% Debug ::= [debug_opts()]
+%% Misc ::= term()
+%% Purpose: Used by a process module that wishes to take care of
+%% system messages. The process receives a {system, From,
+%% Msg} message, and passes the Msg to this function.
+%% Returns: This function *never* returns! It calls the function
+%% Module:system_continue(Parent, NDebug, Misc)
+%% there the process continues the execution or
+%% Module:system_terminate(Raeson, Parent, Debug, Misc) if
+%% the process should terminate.
+%% The Module must export system_continue/3, system_terminate/4
+%% and format_status/2 for status information.
+%%-----------------------------------------------------------------
+handle_system_msg(Msg, From, Parent, Module, Debug, Misc) ->
+ handle_system_msg(running, Msg, From, Parent, Module, Debug, Misc, false).
+
+handle_system_msg(Msg, From, Parent, Mod, Debug, Misc, Hib) ->
+ handle_system_msg(running, Msg, From, Parent, Mod, Debug, Misc, Hib).
+
+handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib) ->
+ case do_cmd(SysState, Msg, Parent, Mod, Debug, Misc) of
+ {suspended, Reply, NDebug, NMisc} ->
+ gen:reply(From, Reply),
+ suspend_loop(suspended, Parent, Mod, NDebug, NMisc, Hib);
+ {running, Reply, NDebug, NMisc} ->
+ gen:reply(From, Reply),
+ Mod:system_continue(Parent, NDebug, NMisc)
+ end.
+
+%%-----------------------------------------------------------------
+%% Func: handle_debug/4
+%% Args: Debug ::= [debug_opts()]
+%% Func ::= {M,F} | fun() arity 3
+%% State ::= term()
+%% Event ::= {in, Msg} | {in, Msg, From} | {out, Msg, To} | term()
+%% Purpose: Called by a process that wishes to debug an event.
+%% Func is a formatting function, called as Func(Device, Event).
+%% Returns: [debug_opts()]
+%%-----------------------------------------------------------------
+handle_debug([{trace, true} | T], FormFunc, State, Event) ->
+ print_event({Event, State, FormFunc}),
+ [{trace, true} | handle_debug(T, FormFunc, State, Event)];
+handle_debug([{log, {N, LogData}} | T], FormFunc, State, Event) ->
+ NLogData = [{Event, State, FormFunc} | trim(N, LogData)],
+ [{log, {N, NLogData}} | handle_debug(T, FormFunc, State, Event)];
+handle_debug([{log_to_file, Fd} | T], FormFunc, State, Event) ->
+ print_event(Fd, {Event, State, FormFunc}),
+ [{log_to_file, Fd} | handle_debug(T, FormFunc, State, Event)];
+handle_debug([{statistics, StatData} | T], FormFunc, State, Event) ->
+ NStatData = stat(Event, StatData),
+ [{statistics, NStatData} | handle_debug(T, FormFunc, State, Event)];
+handle_debug([{Func, FuncState} | T], FormFunc, State, Event) ->
+ case catch Func(FuncState, Event, State) of
+ done -> handle_debug(T, FormFunc, State, Event);
+ {'EXIT', _} -> handle_debug(T, FormFunc, State, Event);
+ NFuncState ->
+ [{Func, NFuncState} | handle_debug(T, FormFunc, State, Event)]
+ end;
+handle_debug([], _FormFunc, _State, _Event) ->
+ [].
+
+%%-----------------------------------------------------------------
+%% When a process is suspended, it can only respond to system
+%% messages.
+%%-----------------------------------------------------------------
+suspend_loop(SysState, Parent, Mod, Debug, Misc, Hib) ->
+ case Hib of
+ true ->
+ suspend_loop_hib(SysState, Parent, Mod, Debug, Misc, Hib);
+ _ ->
+ receive
+ {system, From, Msg} ->
+ handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib);
+ {'EXIT', Parent, Reason} ->
+ Mod:system_terminate(Reason, Parent, Debug, Misc)
+ end
+ end.
+
+suspend_loop_hib(SysState, Parent, Mod, Debug, Misc, Hib) ->
+ receive
+ {system, From, Msg} ->
+ handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib);
+ {'EXIT', Parent, Reason} ->
+ Mod:system_terminate(Reason, Parent, Debug, Misc)
+ after 0 -> % Not a system message, go back into hibernation
+ proc_lib:hibernate(?MODULE, suspend_loop_hib, [SysState, Parent, Mod,
+ Debug, Misc, Hib])
+ end.
+
+
+do_cmd(_, suspend, _Parent, _Mod, Debug, Misc) ->
+ {suspended, ok, Debug, Misc};
+do_cmd(_, resume, _Parent, _Mod, Debug, Misc) ->
+ {running, ok, Debug, Misc};
+do_cmd(SysState, get_status, Parent, Mod, Debug, Misc) ->
+ Res = get_status(SysState, Parent, Mod, Debug, Misc),
+ {SysState, Res, Debug, Misc};
+do_cmd(SysState, {debug, What}, _Parent, _Mod, Debug, Misc) ->
+ {Res, NDebug} = debug_cmd(What, Debug),
+ {SysState, Res, NDebug, Misc};
+do_cmd(suspended, {change_code, Module, Vsn, Extra}, _Parent,
+ Mod, Debug, Misc) ->
+ {Res, NMisc} = do_change_code(Mod, Module, Vsn, Extra, Misc),
+ {suspended, Res, Debug, NMisc};
+do_cmd(SysState, Other, _Parent, _Mod, Debug, Misc) ->
+ {SysState, {error, {unknown_system_msg, Other}}, Debug, Misc}.
+
+get_status(SysState, Parent, Mod, Debug, Misc) ->
+ {status, self(), {module, Mod},
+ [get(), SysState, Parent, Debug, Misc]}.
+
+%%-----------------------------------------------------------------
+%% These are the system debug commands.
+%% {trace, true|false} -> io:format
+%% {log, true|false|get|print} -> keeps the 10 last debug messages
+%% {log_to_file, FileName | false} -> io:format to file.
+%% {statistics, true|false|get} -> keeps track of messages in/out + reds.
+%%-----------------------------------------------------------------
+debug_cmd({trace, true}, Debug) ->
+ {ok, install_debug(trace, true, Debug)};
+debug_cmd({trace, false}, Debug) ->
+ {ok, remove_debug(trace, Debug)};
+debug_cmd({log, true}, Debug) ->
+ {_N, Logs} = get_debug(log, Debug, {0, []}),
+ {ok, install_debug(log, {10, trim(10, Logs)}, Debug)};
+debug_cmd({log, {true, N}}, Debug) when is_integer(N), N > 0 ->
+ {_N, Logs} = get_debug(log, Debug, {0, []}),
+ {ok, install_debug(log, {N, trim(N, Logs)}, Debug)};
+debug_cmd({log, false}, Debug) ->
+ {ok, remove_debug(log, Debug)};
+debug_cmd({log, print}, Debug) ->
+ print_log(Debug),
+ {ok, Debug};
+debug_cmd({log, get}, Debug) ->
+ {_N, Logs} = get_debug(log, Debug, {0, []}),
+ {{ok, lists:reverse(Logs)}, Debug};
+debug_cmd({log_to_file, false}, Debug) ->
+ NDebug = close_log_file(Debug),
+ {ok, NDebug};
+debug_cmd({log_to_file, FileName}, Debug) ->
+ NDebug = close_log_file(Debug),
+ case file:open(FileName, [write]) of
+ {ok, Fd} ->
+ {ok, install_debug(log_to_file, Fd, NDebug)};
+ _Error ->
+ {{error, open_file}, NDebug}
+ end;
+debug_cmd({statistics, true}, Debug) ->
+ {ok, install_debug(statistics, init_stat(), Debug)};
+debug_cmd({statistics, false}, Debug) ->
+ {ok, remove_debug(statistics, Debug)};
+debug_cmd({statistics, get}, Debug) ->
+ {{ok, get_stat(get_debug(statistics, Debug, []))}, Debug};
+debug_cmd(no_debug, Debug) ->
+ close_log_file(Debug),
+ {ok, []};
+debug_cmd({install, {Func, FuncState}}, Debug) ->
+ {ok, install_debug(Func, FuncState, Debug)};
+debug_cmd({remove, Func}, Debug) ->
+ {ok, remove_debug(Func, Debug)};
+debug_cmd(_Unknown, Debug) ->
+ {unknown_debug, Debug}.
+
+
+do_change_code(Mod, Module, Vsn, Extra, Misc) ->
+ case catch Mod:system_code_change(Misc, Module, Vsn, Extra) of
+ {ok, NMisc} -> {ok, NMisc};
+ Else -> {{error, Else}, Misc}
+ end.
+
+print_event(X) -> print_event(standard_io, X).
+
+print_event(Dev, {Event, State, FormFunc}) ->
+ FormFunc(Dev, Event, State).
+
+init_stat() -> {erlang:localtime(), process_info(self(), reductions), 0, 0}.
+get_stat({Time, {reductions, Reds}, In, Out}) ->
+ {reductions, Reds2} = process_info(self(), reductions),
+ [{start_time, Time}, {current_time, erlang:localtime()},
+ {reductions, Reds2 - Reds}, {messages_in, In}, {messages_out, Out}];
+get_stat(_) ->
+ no_statistics.
+
+stat({in, _Msg}, {Time, Reds, In, Out}) -> {Time, Reds, In+1, Out};
+stat({in, _Msg, _From}, {Time, Reds, In, Out}) -> {Time, Reds, In+1, Out};
+stat({out, _Msg, _To}, {Time, Reds, In, Out}) -> {Time, Reds, In, Out+1};
+stat(_, StatData) -> StatData.
+
+trim(N, LogData) ->
+ lists:sublist(LogData, 1, N-1).
+
+%%-----------------------------------------------------------------
+%% Debug structure manipulating functions
+%%-----------------------------------------------------------------
+install_debug(Item, Data, Debug) ->
+ case get_debug(Item, Debug, undefined) of
+ undefined -> [{Item, Data} | Debug];
+ _ -> Debug
+ end.
+remove_debug(Item, Debug) -> lists:keydelete(Item, 1, Debug).
+get_debug(Item, Debug, Default) ->
+ case lists:keysearch(Item, 1, Debug) of
+ {value, {Item, Data}} -> Data;
+ _ -> Default
+ end.
+
+print_log(Debug) ->
+ {_N, Logs} = get_debug(log, Debug, {0, []}),
+ lists:foreach(fun print_event/1,
+ lists:reverse(Logs)).
+
+close_log_file(Debug) ->
+ case get_debug(log_to_file, Debug, []) of
+ [] ->
+ Debug;
+ Fd ->
+ ok = file:close(Fd),
+ remove_debug(log_to_file, Debug)
+ end.
+
+%%-----------------------------------------------------------------
+%% Func: debug_options/1
+%% Args: [trace|log|{log,N}|statistics|{log_to_file, FileName}|
+%% {install, {Func, FuncState}}]
+%% Purpose: Initiate a debug structure. Called by a process that
+%% wishes to initiate the debug structure without the
+%% system messages.
+%% Returns: [debug_opts()]
+%%-----------------------------------------------------------------
+debug_options(Options) ->
+ debug_options(Options, []).
+debug_options([trace | T], Debug) ->
+ debug_options(T, install_debug(trace, true, Debug));
+debug_options([log | T], Debug) ->
+ debug_options(T, install_debug(log, {10, []}, Debug));
+debug_options([{log, N} | T], Debug) when is_integer(N), N > 0 ->
+ debug_options(T, install_debug(log, {N, []}, Debug));
+debug_options([statistics | T], Debug) ->
+ debug_options(T, install_debug(statistics, init_stat(), Debug));
+debug_options([{log_to_file, FileName} | T], Debug) ->
+ case file:open(FileName, [write]) of
+ {ok, Fd} ->
+ debug_options(T, install_debug(log_to_file, Fd, Debug));
+ _Error ->
+ debug_options(T, Debug)
+ end;
+debug_options([{install, {Func, FuncState}} | T], Debug) ->
+ debug_options(T, install_debug(Func, FuncState, Debug));
+debug_options([_ | T], Debug) ->
+ debug_options(T, Debug);
+debug_options([], Debug) ->
+ Debug.