user_default.erl 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2021-2024 EMQ Technologies Co., Ltd. All Rights Reserved.
  3. %%
  4. %% Licensed under the Apache License, Version 2.0 (the "License");
  5. %% you may not use this file except in compliance with the License.
  6. %% You may obtain a copy of the License at
  7. %%
  8. %% http://www.apache.org/licenses/LICENSE-2.0
  9. %%
  10. %% Unless required by applicable law or agreed to in writing, software
  11. %% distributed under the License is distributed on an "AS IS" BASIS,
  12. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. %% See the License for the specific language governing permissions and
  14. %% limitations under the License.
  15. %%--------------------------------------------------------------------
  16. -module(user_default).
  17. %% INCLUDE BEGIN
  18. %% Import all the record definitions from the header file into the erlang shell.
  19. -include_lib("emqx/include/emqx.hrl").
  20. -include_lib("emqx/include/logger.hrl").
  21. -include_lib("emqx_utils/include/emqx_message.hrl").
  22. -include_lib("emqx/include/emqx_mqtt.hrl").
  23. -include_lib("emqx_conf/include/emqx_conf.hrl").
  24. -include_lib("emqx_dashboard/include/emqx_dashboard.hrl").
  25. %% INCLUDE END
  26. -define(TIME, 3 * 60).
  27. -define(MESSAGE, 512).
  28. -define(GREEN, <<"\e[32;1m">>).
  29. -define(RED, <<"\e[31m">>).
  30. -define(RESET, <<"\e[0m">>).
  31. %% API
  32. -export([lock/0, unlock/0]).
  33. -export([trace/0, t/0, t/1, t/2, t_msg/0, t_msg/1, t_stop/0]).
  34. -dialyzer({nowarn_function, start_trace/3}).
  35. -dialyzer({no_return, [t/0, t/1, t/2]}).
  36. lock() -> emqx_restricted_shell:lock().
  37. unlock() -> emqx_restricted_shell:unlock().
  38. trace() ->
  39. ?ULOG("Trace Usage:~n", []),
  40. ?ULOG(" --------------------------------------------------~n", []),
  41. ?ULOG(" t(Mod, Func) -> trace a specify function.~n", []),
  42. ?ULOG(" t(RTPs) -> trace in Redbug Trace Patterns.~n", []),
  43. ?ULOG(" eg1: t(\"emqx_hooks:run\").~n", []),
  44. ?ULOG(" eg2: t(\"emqx_hooks:run/2\").~n", []),
  45. ?ULOG(" eg3: t(\"emqx_hooks:run/2 -> return\").~n", []),
  46. ?ULOG(
  47. " eg4: t(\"emqx_hooks:run('message.dropped',[_, #{node := N}, _])"
  48. "when N =:= 'emqx@127.0.0.1' -> stack,return\"~n",
  49. []
  50. ),
  51. ?ULOG(" t() -> when you forget the RTPs.~n", []),
  52. ?ULOG(" --------------------------------------------------~n", []),
  53. ?ULOG(" t_msg(PidorRegName) -> trace a pid/registed name's messages.~n", []),
  54. ?ULOG(" t_msg([Pid,RegName]) -> trace a list pids's messages.~n", []),
  55. ?ULOG(" t_msg() -> when you forget the pids.~n", []),
  56. ?ULOG(" --------------------------------------------------~n", []),
  57. ?ULOG(" t_stop() -> stop running trace.~n", []),
  58. ?ULOG(" --------------------------------------------------~n", []),
  59. ok.
  60. t_stop() ->
  61. ensure_redbug_stop().
  62. t() ->
  63. {M, F} = get_rtp_fun(),
  64. t(M, F).
  65. t(M) ->
  66. t(M, "").
  67. t(M, F) ->
  68. ensure_redbug_stop(),
  69. RTP = format_rtp(emqx_utils_conv:str(M), emqx_utils_conv:str(F)),
  70. Pids = get_procs(erlang:system_info(process_count)),
  71. Options = [{time, ?TIME * 1000}, {msgs, ?MESSAGE}, debug, {procs, Pids}],
  72. start_trace(RTP, Options, Pids).
  73. t_msg() ->
  74. ?ULOG("Tracing on specific pids's send/receive message: ~n", []),
  75. Pids = get_pids(),
  76. t_msg(Pids).
  77. t_msg([]) ->
  78. exit("procs can't be empty");
  79. t_msg(Pids) when is_list(Pids) ->
  80. ensure_redbug_stop(),
  81. Options = [{time, ?TIME * 1000}, {msgs, ?MESSAGE}, {procs, Pids}],
  82. start_trace(['send', 'receive'], Options, Pids);
  83. t_msg(Pid) ->
  84. t_msg([Pid]).
  85. start_trace(RTP, Options, Pids) ->
  86. info("~nredbug:start(~0p, ~0p)", [RTP, Options]),
  87. case redbug:start(RTP, Options) of
  88. {argument_error, no_matching_functions} ->
  89. warning("~p no matching function", [RTP]);
  90. {argument_error, no_matching_processes} ->
  91. case Pids of
  92. [Pid] -> warning("~p is dead", [Pid]);
  93. _ -> warning("~p are dead", [Pids])
  94. end;
  95. {argument_error, Reason} ->
  96. warning("argument_error:~p~n", [Reason]);
  97. normal ->
  98. warning("bad RTPs: ~p", [RTP]);
  99. {_Name, ProcessCount, 0} ->
  100. info(
  101. "Tracing (~w) processes matching ~p within ~w seconds",
  102. [ProcessCount, RTP, ?TIME]
  103. );
  104. {_Name, ProcessCount, FunCount} ->
  105. info(
  106. "Tracing (~w) processes matching ~ts within ~w seconds and ~w function",
  107. [ProcessCount, RTP, ?TIME, FunCount]
  108. )
  109. end.
  110. get_rtp_fun() ->
  111. RTP0 = io:get_line("Module:Function | Module | RTPs:\n"),
  112. RTP1 = string:trim(RTP0, both, " \n"),
  113. case string:split(RTP1, ":") of
  114. [M] -> {M, get_function()};
  115. [M, ""] -> {M, get_function()};
  116. [M, F] -> {M, F}
  117. end.
  118. get_function() ->
  119. ?ULOG("Function(func|func/3|func('_', atom, X) when is_integer(X)) :~n", []),
  120. F0 = io:get_line(""),
  121. string:trim(F0, both, " \n").
  122. format_rtp("", _) ->
  123. exit("Module can't be empty");
  124. format_rtp(M, "") ->
  125. add_return(M);
  126. format_rtp(M, F) ->
  127. M ++ ":" ++ add_return(F).
  128. add_return(M) ->
  129. case string:find(M, "->") of
  130. nomatch -> M ++ "-> return";
  131. _ -> M
  132. end.
  133. get_procs(ProcCount) when ProcCount > 2500 ->
  134. warning("Tracing include all(~w) processes can be very risky", [ProcCount]),
  135. get_pids();
  136. get_procs(_ProcCount) ->
  137. all.
  138. get_pids() ->
  139. Str = io:get_line("<0.1.0>|<0.1.0>,<0.2.0>|all|new|running|RegName:"),
  140. try
  141. lists:map(fun parse_pid/1, string:tokens(Str, ", \n"))
  142. catch
  143. throw:{not_registered, Name} ->
  144. warning("~ts not registered~n", [Name]),
  145. get_pids();
  146. throw:new ->
  147. new;
  148. throw:running ->
  149. running;
  150. throw:quit ->
  151. throw(quit);
  152. throw:all ->
  153. all;
  154. _:_ ->
  155. warning("Invalid pid: ~ts~n:", [Str]),
  156. get_pids()
  157. end.
  158. parse_pid("<0." ++ _ = L) ->
  159. list_to_pid(L);
  160. parse_pid("all") ->
  161. throw(all);
  162. parse_pid("new") ->
  163. throw(new);
  164. parse_pid("running") ->
  165. throw(running);
  166. parse_pid("q") ->
  167. throw(quit);
  168. parse_pid(NameStr) ->
  169. case emqx_utils:safe_to_existing_atom(NameStr, utf8) of
  170. {ok, Name} ->
  171. case whereis(Name) of
  172. undefined -> throw({not_registered, NameStr});
  173. Pid -> Pid
  174. end;
  175. {error, _} ->
  176. throw({not_registered, NameStr})
  177. end.
  178. warning(Fmt, Args) -> ?ELOG("~s" ++ Fmt ++ ".~s~n", [?RED] ++ Args ++ [?RESET]).
  179. info(Fmt, Args) -> ?ELOG("~s" ++ Fmt ++ ".~s~n", [?GREEN] ++ Args ++ [?RESET]).
  180. ensure_redbug_stop() ->
  181. case redbug:stop() of
  182. not_started ->
  183. ok;
  184. stopped ->
  185. timer:sleep(80),
  186. ok
  187. end.