aboutsummaryrefslogtreecommitdiffstats
path: root/lib/observer/src/ttb_et.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/observer/src/ttb_et.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/observer/src/ttb_et.erl')
-rw-r--r--lib/observer/src/ttb_et.erl267
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.
+
+