1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2002-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20-module(ttb_et). 21-author('siri@erix.ericsson.se'). 22 23-include("et.hrl"). 24-export([handler/4]). 25 26%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 27 28%%% ----------- TTB format handler ----------- 29 30handler(Out,Trace,Traci,initial) -> 31 S = self(), 32 spawn(fun() -> init_et(S) end), 33 receive {et_started,Collector} -> ok end, 34 handler(Out,Trace,Traci,Collector); 35handler(_,end_of_trace,_Traci,Col) -> 36 get_returns(Col), 37 ok; 38handler(_,Trace,_Traci,Col) -> 39 {ok,NewCol} = et_collector:report(Col,Trace), 40 NewCol. 41 42 43%%% ----------- Collector Filter ----------- 44 45collector(Event) when is_record(Event,event) -> 46 %% if collector is selected from viewer menu 47 true; 48collector(Trace) -> 49 et_selector:parse_event(undefined,Trace). 50 51%% After applying collector filter to all events, iterate over 52%% all events backwards and collect call/return information: 53%% 54%% MFA collected from return_to events is added to call and 55%% return_from events as {caller,MFA} and {return_to,MFA} respecively. 56%% MFA collected from call events is added to return_to events as 57%% {return_from,MFA} 58%% 59%% This information can then be used by any filter for generating to- 60%% and from fields. 61get_returns(Col) -> 62 Fun = fun(Event,Acc) -> collect_return_info(Event,Acc,Col) end, 63 et_collector:iterate(Col, last, '-infinity', Fun, dict:new()). 64 65collect_return_info(#event{label=L,from=Pid}=E,Acc,_Col) 66 when L==return_to;L==return_from-> 67 %% Stacking all return_to and return_from events 68 dict:update(Pid,fun(Old) -> [E|Old] end, [E], Acc); 69collect_return_info(#event{label=call,from=Pid,contents=Contents}=E,Acc,Col) -> 70 %% Popping return_from and return_to events 71 %% If both exist, return_from will _always_ be first!!! 72 MFA = get_mfarity(Contents), 73 {Caller,NewAcc} = 74 case dict:find(Pid,Acc) of 75 {ok,[#event{label=return_from}=RetFrom, 76 #event{label=return_to}=RetTo | Rets]} -> 77 RetToCont = RetTo#event.contents, 78 C = get_mfarity(RetToCont), 79 NewRetTo = RetTo#event{contents=RetToCont++[{return_from,MFA}]}, 80 RetFromCont = RetFrom#event.contents, 81 NewRetFrom = 82 RetFrom#event{contents=RetFromCont++[{return_to,C}]}, 83 et_collector:report(Col,NewRetTo), 84 et_collector:report(Col,NewRetFrom), 85 {C, dict:store(Pid,Rets,Acc)}; 86 {ok,[#event{label=return_to}=RetTo | Rets]} -> 87 %% No return_from 88 RetToCont = RetTo#event.contents, 89 C = get_mfarity(RetToCont), 90 NewRetTo = RetTo#event{contents=RetToCont++[{return_from,MFA}]}, 91 et_collector:report(Col,NewRetTo), 92 {C, dict:store(Pid,Rets,Acc)}; 93 {ok,[#event{label=return_from}=RetFrom | Rets]} -> 94 %% No return_to, check if caller(pam_result) is in call event 95 C = get_caller(Contents), 96 RetFromCont = RetFrom#event.contents, 97 NewRetFrom = 98 RetFrom#event{contents=RetFromCont++[{return_to,C}]}, 99 et_collector:report(Col,NewRetFrom), 100 {C, dict:store(Pid,Rets,Acc)}; 101 _noreturn -> 102 {nocaller,Acc} 103 end, 104 NewE = E#event{contents=Contents++[{caller,Caller}]}, 105 et_collector:report(Col,NewE), 106 NewAcc; 107collect_return_info(_E,Acc,_Col) -> 108 Acc. 109 110init_et(Parent) -> 111 process_flag(trap_exit,true), 112% ets:new(ttb_et_table,[set,named_table,public]), 113% ets:insert(ttb_et_table,{traci,Traci}), 114 EtOpt = [{active_filter,processes}, 115 {dict_insert, {filter, collector}, fun collector/1}, 116 {dict_insert, {filter, processes}, fun processes/1}, 117 {dict_insert, {filter, modules}, fun modules/1}, 118 {dict_insert, {filter, mods_and_procs}, fun mods_and_procs/1}, 119 {dict_insert, {filter, functions}, fun functions/1}, 120 {dict_insert, {filter, funcs_and_procs}, fun funcs_and_procs/1}, 121 {hide_actions, false}, 122 {max_events, infinity}, 123 {max_actors, infinity}], 124 {ok, Viewer} = et_viewer:start_link(EtOpt), 125 Collector = et_viewer:get_collector_pid(Viewer), 126 Parent ! {et_started,Collector}, 127 receive 128 {'EXIT',Viewer,shutdown} -> 129 ok 130 end. 131 132 133 134%%% ----------- Viewer Filters ----------- 135 136processes(E0) -> 137 E = label(E0), 138 {{FromProc,FromNode},{ToProc,ToNode}} = 139 get_actors(E#event.from,E#event.to), 140 {true,E#event{from = io_lib:format("~tw~n~w",[FromProc,FromNode]), 141 to = io_lib:format("~tw~n~w",[ToProc,ToNode])}}. 142 143 144mods_and_procs(E) -> 145 ActorFun = fun({M,_F,_A},{Proc,Node}) -> 146 io_lib:format("~w~n~tw~n~w",[M,Proc,Node]) 147 end, 148 calltrace_filter(E,ActorFun). 149 150modules(E) -> 151 ActorFun = fun({M,_F,_A},{_Proc,Node}) -> 152 io_lib:format("~w~n~w",[M,Node]) 153 end, 154 calltrace_filter(E,ActorFun). 155 156funcs_and_procs(E) -> 157 ActorFun = fun({M,F,A},{Proc,Node}) -> 158 io_lib:format("~ts~n~tw~n~w",[mfa(M,F,A),Proc,Node]) 159 end, 160 calltrace_filter(E,ActorFun). 161 162functions(E) -> 163 ActorFun = fun({M,F,A},{_Proc,Node}) -> 164 io_lib:format("~ts~n~w",[mfa(M,F,A),Node]) 165 end, 166 calltrace_filter(E,ActorFun). 167 168 169 170%% Common filter used by mods_and_procs/1 and modules/1 171calltrace_filter(E,ActorFun) -> 172 {From,To} = get_actors(E#event.from,E#event.to), 173 calltrace_filter(E,From,To,ActorFun). 174 175calltrace_filter(#event{label=call}=E,From,To,ActorFun) -> 176 Cont = E#event.contents, 177 MFA = get_mfarity(Cont), 178 case lists:keysearch(caller,1,Cont) of 179 {value,{_,{_CM,_CF,_CA}=Caller}} -> 180 {true, E#event{label = label(call,MFA), 181 from = ActorFun(Caller,From), 182 to = ActorFun(MFA,To)}}; 183 {value,{_, _}} -> 184 {true, E#event{label = label(call,MFA), 185 from = ActorFun(MFA,From), 186 to = ActorFun(MFA,To)}} 187 end; 188calltrace_filter(#event{label=return_from}=E,From,To,ActorFun) -> 189 Cont = E#event.contents, 190 MFA = get_mfarity(Cont), 191 case lists:keysearch(return_to,1,Cont) of 192 {value,{_,{_M2,_F2,_A2}=MFA2}} -> 193 {true, E#event{label = label(return_from,MFA), 194 from = ActorFun(MFA,From), 195 to = ActorFun(MFA2,To)}}; 196 {value,{_, _}} -> 197 {true, E#event{label = label(return_from,MFA), 198 from = ActorFun(MFA,From), 199 to = ActorFun(MFA,To)}} 200 end; 201calltrace_filter(#event{label=return_to}=E,From,To,ActorFun) -> 202 Cont = E#event.contents, 203 {value,{_,{_M2,_F2,_A2}=MFA2}} = lists:keysearch(return_from,1,Cont), 204 case get_mfarity(Cont) of 205 {_M,_F,_A}=MFA -> 206 {true, E#event{label = label(return_to,MFA), 207 from = ActorFun(MFA2,From), 208 to = ActorFun(MFA,To)}}; 209 undefined -> 210 {true, E#event{label = "return_to unknown", 211 from = ActorFun(MFA2,From), 212 to = ActorFun(MFA2,To)}} 213 end; 214calltrace_filter(_E,_From,_To,_ActorFun) -> 215 false. 216 217 218label(Event=#event{label=L,contents=C}) -> 219 case lists:keysearch(mfa,1,C) of 220 {value,{mfa,MFA}} -> Event#event{label=label(L,MFA)}; 221 false -> Event 222 end. 223label(L,{M,F,A}) -> label(L,M,F,A); 224label(L,Other) -> io_lib:format("~w ~tw",[L,Other]). 225label(call,M,F,A) -> "call " ++ mfa(M,F,A); 226label(return_from,M,F,A) -> "return_from " ++ mfa(M,F,A); 227label(return_to,M,F,A) -> "return_to " ++ mfa(M,F,A); 228label(spawn,M,F,A) -> "spawn " ++ mfa(M,F,A); 229label(out,M,F,A) -> "out " ++ mfa(M,F,A); 230label(in,M,F,A) -> "in " ++ mfa(M,F,A). 231 232mfa(M,F,A) -> atom_to_list(M) ++ ":" ++ fa(F,A). 233fa(F,A) -> atom_to_list(F) ++ "/" ++ integer_to_list(arity(A)). 234 235arity(L) when is_list(L) -> length(L); 236arity(I) when is_integer(I) -> I. 237 238get_actors(From,To) -> 239 case {get_proc(From),get_proc(To)} of 240 {{_FP,_FN},{_TP,_TN}}=R -> R; 241 {{FP,FN},T} -> {{FP,FN},{T,FN}}; 242 {F,{TP,TN}} -> {{F,TN},{TP,TN}}; 243 {F,T} -> {{F,unknown},{T,unknown}} 244 end. 245 246get_proc({_Pid,Name,Node}) when is_atom(Name) -> {Name,Node}; 247get_proc({Pid,_initfunc,Node}) -> {Pid,Node}; 248get_proc(P) when is_pid(P); is_port(P) -> {P,node(P)}; 249get_proc(P) -> P. 250 251get_mfarity(List) -> 252 case get_mfa(List) of 253 {M,F,A} -> {M,F,arity(A)}; 254 Other -> Other 255 end. 256get_mfa(List) -> 257 {value,{mfa,MFA}} = lists:keysearch(mfa,1,List), 258 MFA. 259 260get_caller(List) -> 261 case lists:keysearch(pam_result,1,List) of 262 {value,{pam_result,{M,F,A}}} -> {M,F,arity(A)}; 263 {value,{pam_result,undefined}} -> undefined; 264 {value,{pam_result,_Other}} -> nocaller; 265 false -> nocaller 266 end. 267 268 269