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