diff options
Diffstat (limited to 'lib/megaco/src/engine/megaco_filter.erl')
-rw-r--r-- | lib/megaco/src/engine/megaco_filter.erl | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/lib/megaco/src/engine/megaco_filter.erl b/lib/megaco/src/engine/megaco_filter.erl new file mode 100644 index 0000000000..23a91b1f1d --- /dev/null +++ b/lib/megaco/src/engine/megaco_filter.erl @@ -0,0 +1,350 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2000-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% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose : megaco/H.248 customization of generic event tracer +%%---------------------------------------------------------------------- + +-module(megaco_filter). + +-export([start/0, start/1, filter/1, + pretty_error/1, string_to_term/1]). + +-include_lib("megaco/include/megaco.hrl"). +-include_lib("megaco/include/megaco_message_v1.hrl"). +-include_lib("megaco/src/app/megaco_internal.hrl"). +-include_lib("et/include/et.hrl"). + +start() -> + start([]). + +start(ExtraOptions) -> + Options = + [{event_order, event_ts}, + {scale, 3}, + {max_actors, infinity}, + {trace_pattern, {megaco, max}}, + {trace_global, true}, + {dict_insert, {filter, ?MODULE}, fun filter/1}, + {active_filter, ?MODULE}, + {title, "Megaco tracer - Erlang/OTP"} | ExtraOptions], + et_viewer:start(Options). + +filter(E) when is_record(E, event) -> + From = filter_actor(E#event.from), + To = filter_actor(E#event.to), + E2 = E#event{from = From, to = To}, + E3 = filter_contents(E#event.contents, E2, []), + {true, E3}. + +filter_actors(From, To, E) + when (E#event.from =:= ?APPLICATION) andalso (E#event.to =:= ?APPLICATION) -> + Label = E#event.label, + case lists:prefix("callback:", Label) of + true -> + E#event{from = filter_actor(From), + to = filter_user_actor(From)}; + false -> + case lists:prefix("return:", Label) of + true -> + E#event{from = filter_user_actor(From), + to = filter_actor(From)}; + false -> + case lists:prefix("receive bytes", Label) of + true -> + E#event{from = filter_actor(To), + to = filter_actor(From)}; + false -> + E#event{from = filter_actor(From), + to = filter_actor(To)} + end + end + end; +filter_actors(_From, _To, E) -> + E. + +filter_actor(Actor) -> + String = do_filter_actor(Actor), + if + length(String) > 21 -> + string:substr(String, 1, 21) ++ [$*]; + true -> + String + end. + +filter_user_actor(Actor) -> + String = do_filter_actor(Actor) ++ "@user", + if + length(String) > 21 -> + string:substr(String, 1, 21) ++ [$*]; + true -> + String + end. + +do_filter_actor(CH) when is_record(CH, megaco_conn_handle) -> + Mid = CH#megaco_conn_handle.local_mid, + do_filter_actor(Mid); +do_filter_actor(Actor) -> + case Actor of + {ip4Address, {'IP4Address', [A1,A2,A3,A4], asn1_NOVALUE}} -> + integer_to_list(A1) ++ [$.] ++ + integer_to_list(A2) ++ [$.] ++ + integer_to_list(A3) ++ [$.] ++ + integer_to_list(A4); + {ip4Address, {'IP4Address', [A1,A2,A3,A4], Port}} -> + integer_to_list(A1) ++ [$.] ++ + integer_to_list(A2) ++ [$.] ++ + integer_to_list(A3) ++ [$.] ++ + integer_to_list(A4) ++ [$:] ++ + integer_to_list(Port); + {domainName, {'DomainName', Name, asn1_NOVALUE}} -> + Name; + {domainName, {'DomainName', Name, Port}} -> + Name ++ [$:] ++ integer_to_list(Port); + {deviceName, Name} -> + Name; + unknown_remote_mid -> + "preliminary_mid"; + preliminary_mid -> + "preliminary_mid"; + megaco -> + megaco; + _Other -> + "UNKNOWN" + end. + +filter_contents([], E, Contents) -> + E#event{contents = lists:flatten(lists:reverse(Contents))}; +filter_contents([H | T], E, Contents) -> + case H of + {line, _Mod, _Line} -> + filter_contents(T, E, Contents); + CD when is_record(CD, conn_data) -> + CH = CD#conn_data.conn_handle, + From = CH#megaco_conn_handle.local_mid, + To = CH#megaco_conn_handle.remote_mid, + E2 = filter_actors(From, To, E), + Serial = CD#conn_data.serial, + E3 = append_serial(Serial, E2), + filter_contents(T, E3, Contents); + CH when is_record(CH, megaco_conn_handle) -> + From = CH#megaco_conn_handle.local_mid, + To = CH#megaco_conn_handle.remote_mid, + E2 = filter_actors(From, To, E), + filter_contents(T, E2, Contents); + {orig_conn_handle, _CH} -> + filter_contents(T, E, Contents); + RH when is_record(RH, megaco_receive_handle) -> + Actor = RH#megaco_receive_handle.local_mid, + E2 = filter_actors(Actor, Actor, E), + filter_contents(T, E2, Contents); + {pid, Pid} when is_pid(Pid) -> + filter_contents(T, E, Contents); + pending -> + filter_contents(T, E, Contents); + reply -> + filter_contents(T, E, Contents); + {error, Reason} -> + Pretty = pretty_error({error, Reason}), + E2 = prepend_error(E), + filter_contents(T, E2, [[Pretty, "\n"], Contents]); + {'EXIT', Reason} -> + Pretty = pretty_error({'EXIT', Reason}), + E2 = prepend_error(E), + filter_contents(T, E2, [[Pretty, "\n"], Contents]); + ED when is_record(ED, 'ErrorDescriptor') -> + Pretty = pretty_error(ED), + E2 = prepend_error(E), + filter_contents(T, E2, [[Pretty, "\n"], Contents]); + Trans when is_record(Trans, 'TransactionRequest') -> + Pretty = pretty({trans, {transactionRequest, Trans}}), + filter_contents([], E, [[Pretty, "\n"], Contents]); + Trans when is_record(Trans, 'TransactionReply') -> + Pretty = pretty({trans, {transactionReply, Trans}}), + filter_contents([], E, [[Pretty, "\n"], Contents]); + Trans when is_record(Trans, 'TransactionPending') -> + Pretty = pretty({trans, {transactionPending, Trans}}), + filter_contents([], E, [[Pretty, "\n"], Contents]); + Trans when is_record(Trans, 'TransactionAck') -> + Pretty = pretty({trans, {transactionResponseAck, [Trans]}}), + case Trans#'TransactionAck'.lastAck of + asn1_NOVALUE -> + filter_contents([], E, [[Pretty, "\n"], Contents]); + Last -> + Label = term_to_string(E#event.label), + E2 = E#event{label = Label ++ ".." ++ integer_to_list(Last)}, + filter_contents([], E2, [[Pretty, "\n"], Contents]) + end; + {context_id, _ContextId} -> + Pretty = pretty(H), + filter_contents(T, E, [[Pretty, "\n"], Contents]); + {command_request, CmdReq} -> + Pretty = pretty(CmdReq), + filter_contents(T, E, [[Pretty, "\n"], Contents]); + {user_reply, {ok, ARS}} -> + Pretty = [[pretty(AR), "\n"] || AR <- ARS], + filter_contents(T, E, [["REPLY: \n", Pretty, "\n"], Contents]); + {user_reply, Error} -> + Pretty = pretty_error(Error), + filter_contents(T, E, [["REPLY: \n", Pretty, "\n"], Contents]); + {actionReplies, ARS} -> + Pretty = [[pretty(AR), "\n"] || AR <- ARS], + filter_contents(T, E, [["REPLY: \n", Pretty, "\n"], Contents]); + MegaMsg when is_record(MegaMsg, 'MegacoMessage') -> + Pretty = pretty(MegaMsg), + filter_contents(T, E, [["MESSAGE: \n", Pretty, "\n"], Contents]); + {bytes, Bin} when is_binary(Bin) -> + E2 = + case E#event.label of + [$s, $e, $n, $d, $ , $b, $y, $t, $e, $s | Tail] -> + L = lists:concat(["send ", size(Bin), " bytes", Tail]), + E#event{label = L}; + [$r, $e, $c, $e, $i, $v, $e, $ , $b, $y, $t, $e, $s | Tail] -> + L = lists:concat(["receive ", size(Bin), " bytes", Tail]), + E#event{label = L}; + _ -> + E + end, + CharList = erlang:binary_to_list(Bin), + filter_contents(T, E2, [[CharList , "\n"], Contents]); + [] -> + filter_contents(T, E, Contents); + {test_lib, _Mod, _Fun} -> + filter_contents(T, E, Contents); + Other -> + Pretty = pretty(Other), + filter_contents(T, E, [[Pretty, "\n"], Contents]) + end. + +append_serial(Serial, E) when is_integer(Serial) -> + Label = term_to_string(E#event.label), + E#event{label = Label ++ " #" ++ integer_to_list(Serial)}; +append_serial(_Serial, E) -> + E. + +prepend_error(E) -> + Label = term_to_string(E#event.label), + E#event{label = "<ERROR> " ++ Label}. + +pretty({context_id, ContextId}) -> + if + ContextId =:= ?megaco_null_context_id -> + ["CONTEXT ID: -\n"]; + ContextId =:= ?megaco_choose_context_id -> + ["CONTEXT ID: $\n"]; + ContextId =:= ?megaco_all_context_id -> + ["CONTEXT ID: *\n"]; + is_integer(ContextId) -> + ["CONTEXT ID: ",integer_to_list(ContextId), "\n"] + end; +pretty(MegaMsg) when is_record(MegaMsg, 'MegacoMessage') -> + case catch megaco_pretty_text_encoder:encode_message([], MegaMsg) of + {ok, Bin} -> + term_to_string(Bin); + _Bad -> + term_to_string(MegaMsg) + end; +pretty(CmdReq) when is_record(CmdReq, 'CommandRequest') -> + case catch megaco_pretty_text_encoder:encode_command_request(CmdReq) of + {ok, IoList} -> + IoList2 = lists:flatten(IoList), + term_to_string(IoList2); + _Bad -> + term_to_string(CmdReq) + end; +pretty({complete_success, ContextId, RepList} = Res) -> + ActRep = #'ActionReply'{contextId = ContextId, + commandReply = RepList}, + case catch megaco_pretty_text_encoder:encode_action_reply(ActRep) of + {ok, IoList} -> + IoList2 = lists:flatten(IoList), + term_to_string(IoList2); + _Bad -> + term_to_string(Res) + end; +pretty(AR) when is_record(AR, 'ActionReply') -> + case catch megaco_pretty_text_encoder:encode_action_reply(AR) of + {ok, IoList} -> + IoList2 = lists:flatten(IoList), + term_to_string(IoList2); + _Bad -> + term_to_string(AR) + end; +pretty({partial_failure, ContextId, RepList} = Res) -> + ActRep = #'ActionReply'{contextId = ContextId, + commandReply = RepList}, + case catch megaco_pretty_text_encoder:encode_action_reply(ActRep) of + {ok, IoList} -> + IoList2 = lists:flatten(IoList), + term_to_string(IoList2); + _Bad -> + term_to_string(Res) + end; +pretty({trans, Trans}) -> + case catch megaco_pretty_text_encoder:encode_transaction(Trans) of + {ok, Bin} when is_binary(Bin) -> + IoList2 = lists:flatten(binary_to_list(Bin)), + term_to_string(IoList2); + {ok, IoList} -> + IoList2 = lists:flatten(IoList), + term_to_string(IoList2); + _Bad -> + term_to_string(Trans) + end; +pretty(Other) -> + term_to_string(Other). + +pretty_error({error, Reason}) -> + ["ERROR: ", pretty_error(Reason)]; +pretty_error({'EXIT', Reason}) -> + ["EXIT: ", pretty_error(Reason)]; +pretty_error({'ErrorDescriptor', Code, Reason}) -> + ["CODE: ", integer_to_list(Code), " TEXT: ", pretty_error(Reason)]; +pretty_error(Ugly) -> + case string_to_term(Ugly) of + {ok, Pretty} -> ["\n", Pretty]; + _ -> ["\n", term_to_string(Ugly)] + end. + +string_to_term(Chars) -> + do_string_to_term([], Chars, 1). + +do_string_to_term(Cont, Chars, Line) -> + case catch erl_scan:tokens(Cont, Chars, Line) of + {done, {ok, Tokens, _EndLine}, _Rest} -> + case erl_parse:parse_term(Tokens) of + {ok, Term} -> + {ok, Term}; + {error, Reason} -> + {error, Reason} + end; + {more, Cont2} -> + do_string_to_term(Cont2, ". ", Line); + Other -> + {error, Other} + end. + +term_to_string(Bin) when is_binary(Bin) -> + binary_to_list(Bin); +term_to_string(Term) -> + case catch io_lib:format("~s", [Term]) of + {'EXIT', _} -> lists:flatten(io_lib:format("~p", [Term])); + GoodString -> lists:flatten(GoodString) + end. |