sys.erl 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. %% ``The contents of this file are subject to the Erlang Public License,
  2. %% Version 1.1, (the "License"); you may not use this file except in
  3. %% compliance with the License. You should have received a copy of the
  4. %% Erlang Public License along with this software. If not, it can be
  5. %% retrieved via the world wide web at http://www.erlang.org/.
  6. %%
  7. %% Software distributed under the License is distributed on an "AS IS"
  8. %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9. %% the License for the specific language governing rights and limitations
  10. %% under the License.
  11. %%
  12. %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
  13. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
  14. %% AB. All Rights Reserved.''
  15. %%
  16. %% $Id$
  17. %%
  18. -module(sys).
  19. %% External exports
  20. -export([suspend/1, suspend/2, resume/1, resume/2,
  21. get_status/1, get_status/2,
  22. change_code/4, change_code/5,
  23. log/2, log/3, trace/2, trace/3, statistics/2, statistics/3,
  24. log_to_file/2, log_to_file/3, no_debug/1, no_debug/2,
  25. install/2, install/3, remove/2, remove/3]).
  26. -export([reg/3, reg/4]).
  27. -export([handle_system_msg/6, handle_debug/4,
  28. print_log/1, get_debug/3, debug_options/1]).
  29. %%-----------------------------------------------------------------
  30. %% System messages
  31. %%-----------------------------------------------------------------
  32. suspend(Name) -> send_system_msg(Name, suspend).
  33. suspend(Name, Timeout) -> send_system_msg(Name, suspend, Timeout).
  34. resume(Name) -> send_system_msg(Name, resume).
  35. resume(Name, Timeout) -> send_system_msg(Name, resume, Timeout).
  36. get_status(Name) -> send_system_msg(Name, get_status).
  37. get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout).
  38. change_code(Name, Mod, Vsn, Extra) ->
  39. send_system_msg(Name, {change_code, Mod, Vsn, Extra}).
  40. change_code(Name, Mod, Vsn, Extra, Timeout) ->
  41. send_system_msg(Name, {change_code, Mod, Vsn, Extra}, Timeout).
  42. reg(Name, Key, Value) ->
  43. send_system_msg(Name, {reg, Key, Value}).
  44. reg(Name, Key, Value, Timeout) ->
  45. send_system_msg(Name, {reg, Key, Value}, Timeout).
  46. %%-----------------------------------------------------------------
  47. %% Debug commands
  48. %%-----------------------------------------------------------------
  49. log(Name, Flag) ->
  50. send_system_msg(Name, {debug, {log, Flag}}).
  51. log(Name, Flag, Timeout) ->
  52. send_system_msg(Name, {debug, {log, Flag}}, Timeout).
  53. trace(Name, Flag) ->
  54. send_system_msg(Name, {debug, {trace, Flag}}).
  55. trace(Name, Flag, Timeout) ->
  56. send_system_msg(Name, {debug, {trace, Flag}}, Timeout).
  57. log_to_file(Name, FileName) ->
  58. send_system_msg(Name, {debug, {log_to_file, FileName}}).
  59. log_to_file(Name, FileName, Timeout) ->
  60. send_system_msg(Name, {debug, {log_to_file, FileName}}, Timeout).
  61. statistics(Name, Flag) ->
  62. send_system_msg(Name, {debug, {statistics, Flag}}).
  63. statistics(Name, Flag, Timeout) ->
  64. send_system_msg(Name, {debug, {statistics, Flag}}, Timeout).
  65. no_debug(Name) -> send_system_msg(Name, {debug, no_debug}).
  66. no_debug(Name, Timeout) -> send_system_msg(Name, {debug, no_debug}, Timeout).
  67. install(Name, {Func, FuncState}) ->
  68. send_system_msg(Name, {debug, {install, {Func, FuncState}}}).
  69. install(Name, {Func, FuncState}, Timeout) ->
  70. send_system_msg(Name, {debug, {install, {Func, FuncState}}}, Timeout).
  71. remove(Name, Func) ->
  72. send_system_msg(Name, {debug, {remove, Func}}).
  73. remove(Name, Func, Timeout) ->
  74. send_system_msg(Name, {debug, {remove, Func}}, Timeout).
  75. %%-----------------------------------------------------------------
  76. %% All system messages sent are on the form {system, From, Msg}
  77. %% The receiving side should send Msg to handle_system_msg/5.
  78. %%-----------------------------------------------------------------
  79. send_system_msg(Name, Request) ->
  80. case catch gen:call(Name, system, Request) of
  81. {ok,Res} -> Res;
  82. {'EXIT', Reason} -> exit({Reason, mfa(Name, Request)})
  83. end.
  84. send_system_msg(Name, Request, Timeout) ->
  85. case catch gen:call(Name, system, Request, Timeout) of
  86. {ok,Res} -> Res;
  87. {'EXIT', Reason} -> exit({Reason, mfa(Name, Request, Timeout)})
  88. end.
  89. mfa(Name, {debug, {Func, Arg2}}) ->
  90. {sys, Func, [Name, Arg2]};
  91. mfa(Name, {change_code, Mod, Vsn, Extra}) ->
  92. {sys, change_code, [Name, Mod, Vsn, Extra]};
  93. mfa(Name, Atom) ->
  94. {sys, Atom, [Name]}.
  95. mfa(Name, Req, Timeout) ->
  96. {M, F, A} = mfa(Name, Req),
  97. {M, F, A ++ [Timeout]}.
  98. %%-----------------------------------------------------------------
  99. %% Func: handle_system_msg/6
  100. %% Args: Msg ::= term()
  101. %% From ::= {pid(),Ref} but don't count on that
  102. %% Parent ::= pid()
  103. %% Module ::= atom()
  104. %% Debug ::= [debug_opts()]
  105. %% Misc ::= term()
  106. %% Purpose: Used by a process module that wishes to take care of
  107. %% system messages. The process receives a {system, From,
  108. %% Msg} message, and passes the Msg to this function.
  109. %% Returns: This function *never* returns! It calls the function
  110. %% Module:system_continue(Parent, NDebug, Misc)
  111. %% there the process continues the execution or
  112. %% Module:system_terminate(Raeson, Parent, Debug, Misc) if
  113. %% the process should terminate.
  114. %% The Module must export system_continue/3, system_terminate/4
  115. %% and format_status/2 for status information.
  116. %%-----------------------------------------------------------------
  117. handle_system_msg(Msg, From, Parent, Module, Debug, Misc) ->
  118. handle_system_msg(running, Msg, From, Parent, Module, Debug, Misc).
  119. handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc) ->
  120. case do_cmd(SysState, Msg, Parent, Mod, Debug, Misc) of
  121. {suspended, Reply, NDebug, NMisc} ->
  122. gen:reply(From, Reply),
  123. suspend_loop(suspended, Parent, Mod, NDebug, NMisc);
  124. {running, Reply, NDebug, NMisc} ->
  125. gen:reply(From, Reply),
  126. Mod:system_continue(Parent, NDebug, NMisc)
  127. end.
  128. %%-----------------------------------------------------------------
  129. %% Func: handle_debug/4
  130. %% Args: Debug ::= [debug_opts()]
  131. %% Func ::= {M,F} | fun() arity 3
  132. %% State ::= term()
  133. %% Event ::= {in, Msg} | {in, Msg, From} | {out, Msg, To} | term()
  134. %% Purpose: Called by a process that wishes to debug an event.
  135. %% Func is a formatting function, called as Func(Device, Event).
  136. %% Returns: [debug_opts()]
  137. %%-----------------------------------------------------------------
  138. handle_debug([{trace, true} | T], FormFunc, State, Event) ->
  139. print_event({Event, State, FormFunc}),
  140. [{trace, true} | handle_debug(T, FormFunc, State, Event)];
  141. handle_debug([{log, {N, LogData}} | T], FormFunc, State, Event) ->
  142. NLogData = [{Event, State, FormFunc} | trim(N, LogData)],
  143. [{log, {N, NLogData}} | handle_debug(T, FormFunc, State, Event)];
  144. handle_debug([{log_to_file, Fd} | T], FormFunc, State, Event) ->
  145. print_event(Fd, {Event, State, FormFunc}),
  146. [{log_to_file, Fd} | handle_debug(T, FormFunc, State, Event)];
  147. handle_debug([{statistics, StatData} | T], FormFunc, State, Event) ->
  148. NStatData = stat(Event, StatData),
  149. [{statistics, NStatData} | handle_debug(T, FormFunc, State, Event)];
  150. handle_debug([{Func, FuncState} | T], FormFunc, State, Event) ->
  151. case catch Func(FuncState, Event, State) of
  152. done -> handle_debug(T, FormFunc, State, Event);
  153. {'EXIT', _} -> handle_debug(T, FormFunc, State, Event);
  154. NFuncState ->
  155. [{Func, NFuncState} | handle_debug(T, FormFunc, State, Event)]
  156. end;
  157. handle_debug([], _FormFunc, _State, _Event) ->
  158. [].
  159. %%-----------------------------------------------------------------
  160. %% When a process is suspended, it can only respond to system
  161. %% messages.
  162. %%-----------------------------------------------------------------
  163. suspend_loop(SysState, Parent, Mod, Debug, Misc) ->
  164. receive
  165. {system, From, Msg} ->
  166. handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc);
  167. {'EXIT', Parent, Reason} ->
  168. Mod:system_terminate(Reason, Parent, Debug, Misc)
  169. end.
  170. do_cmd(_, suspend, _Parent, _Mod, Debug, Misc) ->
  171. {suspended, ok, Debug, Misc};
  172. do_cmd(_, resume, _Parent, _Mod, Debug, Misc) ->
  173. {running, ok, Debug, Misc};
  174. do_cmd(SysState, {reg, Key, Value}, _Parent, Mod, Debug, Misc) ->
  175. Res = case erlang:function_exported(Mod, system_reg, 3) of
  176. true ->
  177. catch Mod:system_reg(Misc, Key, Value);
  178. false ->
  179. catch gproc:reg(Key, Value)
  180. end,
  181. {SysState, Res, Debug, Misc};
  182. do_cmd(SysState, get_status, Parent, Mod, Debug, Misc) ->
  183. Res = get_status(SysState, Parent, Mod, Debug, Misc),
  184. {SysState, Res, Debug, Misc};
  185. do_cmd(SysState, {debug, What}, _Parent, _Mod, Debug, Misc) ->
  186. {Res, NDebug} = debug_cmd(What, Debug),
  187. {SysState, Res, NDebug, Misc};
  188. do_cmd(suspended, {change_code, Module, Vsn, Extra}, _Parent,
  189. Mod, Debug, Misc) ->
  190. {Res, NMisc} = do_change_code(Mod, Module, Vsn, Extra, Misc),
  191. {suspended, Res, Debug, NMisc};
  192. do_cmd(SysState, Other, _Parent, _Mod, Debug, Misc) ->
  193. {SysState, {error, {unknown_system_msg, Other}}, Debug, Misc}.
  194. get_status(SysState, Parent, Mod, Debug, Misc) ->
  195. {status, self(), {module, Mod},
  196. [get(), SysState, Parent, Debug, Misc]}.
  197. %%-----------------------------------------------------------------
  198. %% These are the system debug commands.
  199. %% {trace, true|false} -> io:format
  200. %% {log, true|false|get|print} -> keeps the 10 last debug messages
  201. %% {log_to_file, FileName | false} -> io:format to file.
  202. %% {statistics, true|false|get} -> keeps track of messages in/out + reds.
  203. %%-----------------------------------------------------------------
  204. debug_cmd({trace, true}, Debug) ->
  205. {ok, install_debug(trace, true, Debug)};
  206. debug_cmd({trace, false}, Debug) ->
  207. {ok, remove_debug(trace, Debug)};
  208. debug_cmd({log, true}, Debug) ->
  209. {_N, Logs} = get_debug(log, Debug, {0, []}),
  210. {ok, install_debug(log, {10, trim(10, Logs)}, Debug)};
  211. debug_cmd({log, {true, N}}, Debug) when is_integer(N), N > 0 ->
  212. {_N, Logs} = get_debug(log, Debug, {0, []}),
  213. {ok, install_debug(log, {N, trim(N, Logs)}, Debug)};
  214. debug_cmd({log, false}, Debug) ->
  215. {ok, remove_debug(log, Debug)};
  216. debug_cmd({log, print}, Debug) ->
  217. print_log(Debug),
  218. {ok, Debug};
  219. debug_cmd({log, get}, Debug) ->
  220. {_N, Logs} = get_debug(log, Debug, {0, []}),
  221. {{ok, lists:reverse(Logs)}, Debug};
  222. debug_cmd({log_to_file, false}, Debug) ->
  223. NDebug = close_log_file(Debug),
  224. {ok, NDebug};
  225. debug_cmd({log_to_file, FileName}, Debug) ->
  226. NDebug = close_log_file(Debug),
  227. case file:open(FileName, write) of
  228. {ok, Fd} ->
  229. {ok, install_debug(log_to_file, Fd, NDebug)};
  230. _Error ->
  231. {{error, open_file}, NDebug}
  232. end;
  233. debug_cmd({statistics, true}, Debug) ->
  234. {ok, install_debug(statistics, init_stat(), Debug)};
  235. debug_cmd({statistics, false}, Debug) ->
  236. {ok, remove_debug(statistics, Debug)};
  237. debug_cmd({statistics, get}, Debug) ->
  238. {{ok, get_stat(get_debug(statistics, Debug, []))}, Debug};
  239. debug_cmd(no_debug, Debug) ->
  240. close_log_file(Debug),
  241. {ok, []};
  242. debug_cmd({install, {Func, FuncState}}, Debug) ->
  243. {ok, install_debug(Func, FuncState, Debug)};
  244. debug_cmd({remove, Func}, Debug) ->
  245. {ok, remove_debug(Func, Debug)};
  246. debug_cmd(_Unknown, Debug) ->
  247. {unknown_debug, Debug}.
  248. do_change_code(Mod, Module, Vsn, Extra, Misc) ->
  249. case catch Mod:system_code_change(Misc, Module, Vsn, Extra) of
  250. {ok, NMisc} -> {ok, NMisc};
  251. Else -> {{error, Else}, Misc}
  252. end.
  253. print_event(X) -> print_event(standard_io, X).
  254. print_event(Dev, {Event, State, FormFunc}) ->
  255. FormFunc(Dev, Event, State).
  256. init_stat() -> {erlang:localtime(), process_info(self(), reductions), 0, 0}.
  257. get_stat({Time, {reductions, Reds}, In, Out}) ->
  258. {reductions, Reds2} = process_info(self(), reductions),
  259. [{start_time, Time}, {current_time, erlang:localtime()},
  260. {reductions, Reds2 - Reds}, {messages_in, In}, {messages_out, Out}];
  261. get_stat(_) ->
  262. no_statistics.
  263. stat({in, _Msg}, {Time, Reds, In, Out}) -> {Time, Reds, In+1, Out};
  264. stat({in, _Msg, _From}, {Time, Reds, In, Out}) -> {Time, Reds, In+1, Out};
  265. stat({out, _Msg, _To}, {Time, Reds, In, Out}) -> {Time, Reds, In, Out+1};
  266. stat(_, StatData) -> StatData.
  267. trim(N, LogData) ->
  268. lists:sublist(LogData, 1, N-1).
  269. %%-----------------------------------------------------------------
  270. %% Debug structure manipulating functions
  271. %%-----------------------------------------------------------------
  272. install_debug(Item, Data, Debug) ->
  273. case get_debug(Item, Debug, undefined) of
  274. undefined -> [{Item, Data} | Debug];
  275. _ -> Debug
  276. end.
  277. remove_debug(Item, Debug) -> lists:keydelete(Item, 1, Debug).
  278. get_debug(Item, Debug, Default) ->
  279. case lists:keysearch(Item, 1, Debug) of
  280. {value, {Item, Data}} -> Data;
  281. _ -> Default
  282. end.
  283. print_log(Debug) ->
  284. {_N, Logs} = get_debug(log, Debug, {0, []}),
  285. lists:foreach(fun print_event/1,
  286. lists:reverse(Logs)).
  287. close_log_file(Debug) ->
  288. case get_debug(log_to_file, Debug, []) of
  289. [] ->
  290. Debug;
  291. Fd ->
  292. file:close(Fd),
  293. remove_debug(log_to_file, Debug)
  294. end.
  295. %%-----------------------------------------------------------------
  296. %% Func: debug_options/1
  297. %% Args: [trace|log|{log,N}|statistics|{log_to_file, FileName}|
  298. %% {install, {Func, FuncState}}]
  299. %% Purpose: Initiate a debug structure. Called by a process that
  300. %% wishes to initiate the debug structure without the
  301. %% system messages.
  302. %% Returns: [debug_opts()]
  303. %%-----------------------------------------------------------------
  304. debug_options(Options) ->
  305. debug_options(Options, []).
  306. debug_options([trace | T], Debug) ->
  307. debug_options(T, install_debug(trace, true, Debug));
  308. debug_options([log | T], Debug) ->
  309. debug_options(T, install_debug(log, {10, []}, Debug));
  310. debug_options([{log, N} | T], Debug) when is_integer(N), N > 0 ->
  311. debug_options(T, install_debug(log, {N, []}, Debug));
  312. debug_options([statistics | T], Debug) ->
  313. debug_options(T, install_debug(statistics, init_stat(), Debug));
  314. debug_options([{log_to_file, FileName} | T], Debug) ->
  315. case file:open(FileName, write) of
  316. {ok, Fd} ->
  317. debug_options(T, install_debug(log_to_file, Fd, Debug));
  318. _Error ->
  319. debug_options(T, Debug)
  320. end;
  321. debug_options([{install, {Func, FuncState}} | T], Debug) ->
  322. debug_options(T, install_debug(Func, FuncState, Debug));
  323. debug_options([_ | T], Debug) ->
  324. debug_options(T, Debug);
  325. debug_options([], Debug) ->
  326. Debug.