./ct_report/coverage/tr_util.COVER.html

1 %% @doc This module contains debug utilities intended for use with Erlang Doctor.
2 %% Beware that this tool has the potential of seriously impacting
3 %% your system, leading to various issues including system crash or data loss.
4 %% Therefore, it is intended for use in development or QA environments,
5 %% and using it in a production environment is risky.
6 %%
7 %% Example: let's capture and list all stanzas exchanged between Alice and Bob.
8 %%
9 %% ```
10 %% tr:start().
11 %% tr:trace([mongoose_c2s_hooks]).
12 %%
13 %% %% Exchange stanzas between users
14 %%
15 %% tr:stop_tracing().
16 %% tr_util:c2s_elements_between_jids([<<"alice@localhost">>, <<"bob@localhost">>]).
17 %%
18 %% %% You will get a list of `c2s_element_info' maps with the exchanged stanzas.
19 %% '''
20 %%
21 %% @reference See <a href="https://hexdocs.pm/erlang_doctor/readme.html">Hex Docs</a>
22 %% for more information about Erlang Doctor.
23
24 -module(tr_util).
25
26 %% Debugging API for mongoose_c2s events and XMPP traffic
27 -export([c2s_elements_between_jids/1,
28 c2s_hooks/0,
29 c2s_elements/0]).
30
31 %% Selectors for use with `tr:call_stat' etc.
32 -export([tr_to_element_info/1,
33 tr_to_hook_name_and_tag/1]).
34
35 %% Predicates for use with `tr:filter' etc.
36 -export([filter_c2s_hook/1]).
37
38 -include_lib("erlang_doctor/include/tr.hrl").
39 -include_lib("exml/include/exml.hrl").
40
41 -ignore_xref(?MODULE).
42
43 -type c2s_element_info() :: #{name := binary(),
44 contents := binary(),
45 ref := reference(),
46 hooks := [atom()],
47 jid := jid:literal_jid(),
48 from_jid := jid:literal_jid(),
49 to_jid := jid:literal_jid(),
50 id := binary(),
51 type := binary()}.
52
53 %% Complete utilities
54
55 %% @doc Get a list of XML elements (usually stanzas) exchanged between the listed JIDs.
56 %% The `to' and `from' attributes must match different JIDs from the list.
57 %% Matching starts from the beginning of the list. A bare JID matches any resource.
58 %%
59 %% Requires traces from modules: `[mongoose_c2s_hooks]'.
60 -spec c2s_elements_between_jids([jid:literal_jid()]) -> [c2s_element_info()].
61 c2s_elements_between_jids(TargetBinJids) ->
62 2 Targets = lists:map(fun jid:from_binary_noprep/1, TargetBinJids),
63 2 lists:filter(fun(#{from_jid := From, to_jid := To}) ->
64 24 case match_target_jids(From, Targets) of
65 4 [] -> false;
66 20 [H|_] -> match_target_jids(To, Targets -- [H]) =/= []
67 end
68 end, c2s_elements()).
69
70 %% @doc Get a list of all C2S hooks in the execution order, annotated by user JIDs,
71 %% for which they were executed.
72 %%
73 %% Requires traces from modules: `[mongoose_c2s_hooks]'.
74 -spec c2s_hooks() -> [{jid:literal_jid(), atom(), mongoose_acc:t()}].
75 c2s_hooks() ->
76 2 [{jid:to_binary(mongoose_c2s:get_jid(Data)), Hook, Acc} ||
77 #tr{mfa = {_, Hook, _}, data = [_HT, Acc, #{c2s_data := Data}]} <-
78 2 tr:filter(fun filter_c2s_hook/1)
79 ].
80
81 %% @doc Get information about XML elements, for which C2S hooks were executed.
82 %%
83 %% Requires traces from modules: `[mongoose_c2s_hooks]'.
84 -spec c2s_elements() -> [c2s_element_info()].
85 c2s_elements() ->
86 4 join_hooks(c2s_element_hooks()).
87
88 %% Selectors for use with `tr:call_stat' etc.
89
90 -spec tr_to_hook_name_and_tag(tr:tr()) -> {gen_hook:hook_name(), gen_hook:hook_tag()}.
91 tr_to_hook_name_and_tag(#tr{mfa = {gen_hook, run_fold, _}, data = [HookName, Tag | _]}) ->
92 35 {HookName, Tag}.
93
94 -spec tr_to_element_info(tr:tr()) -> c2s_element_info().
95 tr_to_element_info(#tr{mfa = {mongoose_c2s_hooks, Hook, _},
96 data = [_HT, #{stanza := ElementAcc}, #{c2s_data := Data}]}) ->
97 93 element_info(Data, Hook, ElementAcc).
98
99 %% Predicates for use with `tr:filter' etc.
100
101 -spec filter_c2s_hook(tr:tr()) -> boolean().
102 26 filter_c2s_hook(#tr{mfa = {mongoose_c2s_hooks, _, _}}) -> true.
103
104 -spec filter_c2s_hook_with_element(tr:tr()) -> boolean().
105 filter_c2s_hook_with_element(#tr{mfa = {mongoose_c2s_hooks, _, _},
106 data = [_HT, #{stanza := #{}}, _Data]}) ->
107 93 true.
108
109 %% Internal helpers
110
111 -spec c2s_element_hooks() -> [c2s_element_info()].
112 c2s_element_hooks() ->
113 4 lists:map(fun tr_to_element_info/1, tr:filter(fun filter_c2s_hook_with_element/1)).
114
115 -spec match_target_jids(jid:literal_jid(), [jid:jid()]) -> [jid:jid()].
116 match_target_jids(ActualBJid, Targets) ->
117 44 Actual = jid:from_binary_noprep(ActualBJid),
118 44 lists:filter(fun(Target) -> match_jid(Target, Actual) end, Targets).
119
120 -spec match_jid(jid:jid(), jid:jid()) -> boolean().
121 match_jid(Target, Actual) ->
122 68 case jid:lresource(Target) of
123 34 <<>> -> jid:are_bare_equal(Target, Actual);
124 34 _ -> jid:are_equal(Target, Actual)
125 end.
126
127 -spec join_hooks([c2s_element_info()]) -> [c2s_element_info()].
128 join_hooks([First | Rest]) ->
129 3 lists:reverse(lists:foldl(fun join_hooks_step/2, [First], Rest));
130 join_hooks([]) ->
131 1 [].
132
133 -spec join_hooks_step(c2s_element_info(), [c2s_element_info()]) -> [c2s_element_info()].
134 join_hooks_step(Cur, [Prev | Acc]) ->
135 90 case {maps:take(hooks, Cur), maps:take(hooks, Prev)} of
136 {{CurHooks, D}, {PrevHooks, D}} ->
137 57 [D#{hooks => PrevHooks ++ CurHooks} | Acc];
138 _ ->
139 33 [Cur, Prev | Acc]
140 end.
141
142 -spec element_info(mongoose_c2s:data(), gen_hook:hook_name(), mongoose_acc:stanza_metadata()) ->
143 c2s_element_info().
144 element_info(Data, Hook, #{element := Element, ref := Ref, from_jid := From, to_jid := To}) ->
145 93 Info = #{name => Element#xmlel.name,
146 contents => exml:to_binary(Element#xmlel.children),
147 ref => Ref,
148 hooks => [Hook],
149 jid => jid:to_binary(mongoose_c2s:get_jid(Data)),
150 from_jid => jid:to_binary(From),
151 to_jid => jid:to_binary(To)},
152 93 maps:merge(Info, element_attr_info(Element#xmlel.attrs)).
153
154 -spec element_attr_info([exml:attr()]) -> #{atom() => binary()}.
155 element_attr_info(Attrs) ->
156 93 AllowedAttrs = [<<"id">>, <<"type">>],
157 93 maps:from_list([{binary_to_existing_atom(Key), Value} || {Key, Value} <- Attrs,
158 219 lists:member(Key, AllowedAttrs)]).
Line Hits Source