diff options
Diffstat (limited to 'lib/observer/src/ttb_et.erl')
-rw-r--r-- | lib/observer/src/ttb_et.erl | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/lib/observer/src/ttb_et.erl b/lib/observer/src/ttb_et.erl new file mode 100644 index 0000000000..60769f1cc2 --- /dev/null +++ b/lib/observer/src/ttb_et.erl @@ -0,0 +1,267 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(ttb_et). +-author('[email protected]'). + +-include("et.hrl"). +-export([handler/4]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%% ----------- TTB format handler ----------- + +handler(Out,Trace,Traci,initial) -> + S = self(), + spawn(fun() -> init_et(S) end), + receive {et_started,Collector} -> ok end, + handler(Out,Trace,Traci,Collector); +handler(_,end_of_trace,_Traci,Col) -> + get_returns(Col), + ok; +handler(_,Trace,_Traci,Col) -> + {ok,NewCol} = et_collector:report(Col,Trace), + NewCol. + + +%%% ----------- Collector Filter ----------- + +collector(Event) when is_record(Event,event) -> + %% if collector is selected from viewer menu + true; +collector(Trace) -> + et_selector:parse_event(undefined,Trace). + +%% After applying collector filter to all events, iterate over +%% all events backwards and collect call/return information: +%% +%% MFA collected from return_to events is added to call and +%% return_from events as {caller,MFA} and {return_to,MFA} respecively. +%% MFA collected from call events is added to return_to events as +%% {return_from,MFA} +%% +%% This information can then be used by any filter for generating to- +%% and from fields. +get_returns(Col) -> + Fun = fun(Event,Acc) -> collect_return_info(Event,Acc,Col) end, + et_collector:iterate(Col, last, '-infinity', Fun, dict:new()). + +collect_return_info(#event{label=L,from=Pid}=E,Acc,_Col) + when L==return_to;L==return_from-> + %% Stacking all return_to and return_from events + dict:update(Pid,fun(Old) -> [E|Old] end, [E], Acc); +collect_return_info(#event{label=call,from=Pid,contents=Contents}=E,Acc,Col) -> + %% Popping return_from and return_to events + %% If both exist, return_from will _always_ be first!!! + MFA = get_mfarity(Contents), + {Caller,NewAcc} = + case dict:find(Pid,Acc) of + {ok,[#event{label=return_from}=RetFrom, + #event{label=return_to}=RetTo | Rets]} -> + RetToCont = RetTo#event.contents, + C = get_mfarity(RetToCont), + NewRetTo = RetTo#event{contents=RetToCont++[{return_from,MFA}]}, + RetFromCont = RetFrom#event.contents, + NewRetFrom = + RetFrom#event{contents=RetFromCont++[{return_to,C}]}, + et_collector:report(Col,NewRetTo), + et_collector:report(Col,NewRetFrom), + {C, dict:store(Pid,Rets,Acc)}; + {ok,[#event{label=return_to}=RetTo | Rets]} -> + %% No return_from + RetToCont = RetTo#event.contents, + C = get_mfarity(RetToCont), + NewRetTo = RetTo#event{contents=RetToCont++[{return_from,MFA}]}, + et_collector:report(Col,NewRetTo), + {C, dict:store(Pid,Rets,Acc)}; + {ok,[#event{label=return_from}=RetFrom | Rets]} -> + %% No return_to, check if caller(pam_result) is in call event + C = get_caller(Contents), + RetFromCont = RetFrom#event.contents, + NewRetFrom = + RetFrom#event{contents=RetFromCont++[{return_to,C}]}, + et_collector:report(Col,NewRetFrom), + {C, dict:store(Pid,Rets,Acc)}; + _noreturn -> + {nocaller,Acc} + end, + NewE = E#event{contents=Contents++[{caller,Caller}]}, + et_collector:report(Col,NewE), + NewAcc; +collect_return_info(_E,Acc,_Col) -> + Acc. + +init_et(Parent) -> + process_flag(trap_exit,true), +% ets:new(ttb_et_table,[set,named_table,public]), +% ets:insert(ttb_et_table,{traci,Traci}), + EtOpt = [{active_filter,processes}, + {dict_insert, {filter, collector}, fun collector/1}, + {dict_insert, {filter, processes}, fun processes/1}, + {dict_insert, {filter, modules}, fun modules/1}, + {dict_insert, {filter, mods_and_procs}, fun mods_and_procs/1}, + {dict_insert, {filter, functions}, fun functions/1}, + {dict_insert, {filter, funcs_and_procs}, fun funcs_and_procs/1}, + {hide_actions, false}, + {max_events, infinity}, + {max_actors, infinity}], + {ok, Viewer} = et_viewer:start_link(EtOpt), + Collector = et_viewer:get_collector_pid(Viewer), + Parent ! {et_started,Collector}, + receive + {'EXIT',Viewer,shutdown} -> + ok + end. + + + +%%% ----------- Viewer Filters ----------- + +processes(E0) -> + E = label(E0), + {{FromProc,FromNode},{ToProc,ToNode}} = + get_actors(E#event.from,E#event.to), + {true,E#event{from = io_lib:format("~w~n~w",[FromProc,FromNode]), + to = io_lib:format("~w~n~w",[ToProc,ToNode])}}. + + +mods_and_procs(E) -> + ActorFun = fun({M,_F,_A},{Proc,Node}) -> + io_lib:format("~w~n~w~n~w",[M,Proc,Node]) + end, + calltrace_filter(E,ActorFun). + +modules(E) -> + ActorFun = fun({M,_F,_A},{_Proc,Node}) -> + io_lib:format("~w~n~w",[M,Node]) + end, + calltrace_filter(E,ActorFun). + +funcs_and_procs(E) -> + ActorFun = fun({M,F,A},{Proc,Node}) -> + io_lib:format("~s~n~w~n~w",[mfa(M,F,A),Proc,Node]) + end, + calltrace_filter(E,ActorFun). + +functions(E) -> + ActorFun = fun({M,F,A},{_Proc,Node}) -> + io_lib:format("~s~n~w",[mfa(M,F,A),Node]) + end, + calltrace_filter(E,ActorFun). + + + +%% Common filter used by mods_and_procs/1 and modules/1 +calltrace_filter(E,ActorFun) -> + {From,To} = get_actors(E#event.from,E#event.to), + calltrace_filter(E,From,To,ActorFun). + +calltrace_filter(#event{label=call}=E,From,To,ActorFun) -> + Cont = E#event.contents, + MFA = get_mfarity(Cont), + case lists:keysearch(caller,1,Cont) of + {value,{_,{_CM,_CF,_CA}=Caller}} -> + {true, E#event{label = label(call,MFA), + from = ActorFun(Caller,From), + to = ActorFun(MFA,To)}}; + {value,{_, _}} -> + {true, E#event{label = label(call,MFA), + from = ActorFun(MFA,From), + to = ActorFun(MFA,To)}} + end; +calltrace_filter(#event{label=return_from}=E,From,To,ActorFun) -> + Cont = E#event.contents, + MFA = get_mfarity(Cont), + case lists:keysearch(return_to,1,Cont) of + {value,{_,{_M2,_F2,_A2}=MFA2}} -> + {true, E#event{label = label(return_from,MFA), + from = ActorFun(MFA,From), + to = ActorFun(MFA2,To)}}; + {value,{_, _}} -> + {true, E#event{label = label(return_from,MFA), + from = ActorFun(MFA,From), + to = ActorFun(MFA,To)}} + end; +calltrace_filter(#event{label=return_to}=E,From,To,ActorFun) -> + Cont = E#event.contents, + {value,{_,{_M2,_F2,_A2}=MFA2}} = lists:keysearch(return_from,1,Cont), + case get_mfarity(Cont) of + {_M,_F,_A}=MFA -> + {true, E#event{label = label(return_to,MFA), + from = ActorFun(MFA2,From), + to = ActorFun(MFA,To)}}; + undefined -> + {true, E#event{label = "return_to unknown", + from = ActorFun(MFA2,From), + to = ActorFun(MFA2,To)}} + end; +calltrace_filter(_E,_From,_To,_ActorFun) -> + false. + + +label(Event=#event{label=L,contents=C}) -> + case lists:keysearch(mfa,1,C) of + {value,{mfa,MFA}} -> Event#event{label=label(L,MFA)}; + false -> Event + end. +label(L,{M,F,A}) -> label(L,M,F,A); +label(L,Other) -> io_lib:format("~w ~w",[L,Other]). +label(call,M,F,A) -> "call " ++ mfa(M,F,A); +label(return_from,M,F,A) -> "return_from " ++ mfa(M,F,A); +label(return_to,M,F,A) -> "return_to " ++ mfa(M,F,A); +label(spawn,M,F,A) -> "spawn " ++ mfa(M,F,A); +label(out,M,F,A) -> "out " ++ mfa(M,F,A); +label(in,M,F,A) -> "in " ++ mfa(M,F,A). + +mfa(M,F,A) -> atom_to_list(M) ++ ":" ++ fa(F,A). +fa(F,A) -> atom_to_list(F) ++ "/" ++ integer_to_list(arity(A)). + +arity(L) when is_list(L) -> length(L); +arity(I) when is_integer(I) -> I. + +get_actors(From,To) -> + case {get_proc(From),get_proc(To)} of + {{_FP,_FN},{_TP,_TN}}=R -> R; + {{FP,FN},T} -> {{FP,FN},{T,FN}}; + {F,{TP,TN}} -> {{F,TN},{TP,TN}}; + {F,T} -> {{F,unknown},{T,unknown}} + end. + +get_proc({_Pid,Name,Node}) when is_atom(Name) -> {Name,Node}; +get_proc({Pid,_initfunc,Node}) -> {Pid,Node}; +get_proc(P) when is_pid(P); is_port(P) -> {P,node(P)}; +get_proc(P) -> P. + +get_mfarity(List) -> + case get_mfa(List) of + {M,F,A} -> {M,F,arity(A)}; + Other -> Other + end. +get_mfa(List) -> + {value,{mfa,MFA}} = lists:keysearch(mfa,1,List), + MFA. + +get_caller(List) -> + case lists:keysearch(pam_result,1,List) of + {value,{pam_result,{M,F,A}}} -> {M,F,arity(A)}; + {value,{pam_result,undefined}} -> undefined; + {value,{pam_result,_Other}} -> nocaller; + false -> nocaller + end. + + |