From 483aceacd063d0a9b787bf468e795617d52e0507 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Wed, 14 Jun 2017 16:53:01 +0200 Subject: Exercise example RFC 4005 dictionary in traffic suite Only exercising the standard dictionaries has missed some problems in the past. --- lib/diameter/test/diameter_traffic_SUITE.erl | 269 ++++++++++++++++++++------- 1 file changed, 202 insertions(+), 67 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 84b41f14b7..0a93033d83 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -35,7 +35,8 @@ end_per_testcase/2]). %% testcases --export([start/1, +-export([rfc4005/1, + start/1, start_services/1, add_transports/1, result_codes/1, @@ -125,7 +126,7 @@ -define(L, atom_to_list). %% Don't use is_record/2 since dictionary hrl's aren't included. -%% (Since they define conflicting reqcords with the same names.) +%% (Since they define conflicting records with the same names.) -define(is_record(Rec, Name), (Name == element(1, Rec))). -define(ADDR, {127,0,0,1}). @@ -144,8 +145,8 @@ %% How to send answers, in a diameter_packet or not. -define(CONTAINERS, [pkt, msg]). -%% Which common dictionary to use in the clients. --define(RFCS, [rfc3588, rfc6733]). +%% Which dictionary to use in the clients. +-define(RFCS, [rfc3588, rfc6733, rfc4005]). %% Whether to decode stringish Diameter types to strings, or leave %% them as binary. @@ -165,7 +166,7 @@ strings, client_service, client_encoding, - client_dict0, + client_dict, client_sender, server_service, server_encoding, @@ -198,8 +199,8 @@ {'Host-IP-Address', [?ADDR]}, {'Vendor-Id', 12345}, {'Product-Name', "OTP/diameter"}, - {'Auth-Application-Id', [?DIAMETER_APP_ID_COMMON]}, - {'Acct-Application-Id', [?DIAMETER_APP_ID_ACCOUNTING]}, + {'Auth-Application-Id', [0]}, %% common messages + {'Acct-Application-Id', [3]}, %% base accounting {restrict_connections, false}, {string_decode, Decode}, {incoming_maxlen, 1 bsl 21} @@ -209,7 +210,9 @@ || D <- [diameter_gen_base_rfc3588, diameter_gen_base_accounting, diameter_gen_base_rfc6733, - diameter_gen_acct_rfc6733]]]). + diameter_gen_acct_rfc6733, + nas4005], + D /= nas4005 orelse have_nas()]]). -define(SUCCESS, ?'DIAMETER_BASE_RESULT-CODE_SUCCESS'). @@ -254,7 +257,7 @@ suite() -> [{timetrap, {seconds, 10}}]. all() -> - [start, result_codes, {group, traffic}, empty, stop]. + [rfc4005, start, result_codes, {group, traffic}, empty, stop]. groups() -> [{P, [P], Ts} || Ts <- [tc(tc())], P <- [shuffle, parallel]] @@ -300,9 +303,11 @@ tc(L) -> %% -------------------- init_per_suite(Config) -> - [{sctp, ?util:have_sctp()} | Config]. + [{rfc4005, compile_and_load()}, {sctp, ?util:have_sctp()} | Config]. end_per_suite(_Config) -> + code:delete(nas4005), + code:purge(nas4005), ok. %% -------------------- @@ -322,13 +327,16 @@ init_per_group(sctp = Name, Config) -> end; init_per_group(Name, Config) -> + Nas = proplists:get_value(rfc4005, Config, false), case ?util:name(Name) of + [_,_,D,_,_,_, _, _, _] when D == rfc4005, true /= Nas -> + {skip, rfc4005}; [T,R,D,A,C,S,SS,ST,CS] -> G = #group{transport = T, strings = S, client_service = [$C|?util:unique_string()], client_encoding = R, - client_dict0 = dict0(D), + client_dict = appdict(D), client_sender = CS, server_service = [$S|?util:unique_string()], server_encoding = A, @@ -463,26 +471,45 @@ add_transports(Config) -> | [{packet, hd(?util:scramble([false, raw]))} || T == sctp andalso CS]], [{capabilities_cb, fun capx/2}, - {pool_size, 8}, - {applications, apps(rfc3588)}] + {pool_size, 8} + | server_apps()] ++ [{spawn_opt, {erlang, spawn, []}} || CS]), Cs = [?util:connect(CN, [T, {sender, CS}], LRef, - [{id, Id}, - {capabilities, [{'Origin-State-Id', origin(Id)}]}, - {applications, apps(D)}]) + [{id, Id} + | client_apps(D, [{'Origin-State-Id', origin(Id)}])]) || A <- ?ENCODINGS, C <- ?CONTAINERS, D <- ?RFCS, + D /= rfc4005 orelse have_nas(), Id <- [{A,C}]], %% The server uses the client's Origin-State-Id to decide how to %% answer. ?util:write_priv(Config, "transport", [LRef | Cs]). -apps(D0) -> - D = dict0(D0), - [acct(D), D]. +server_apps() -> + B = have_nas(), + [{applications, [diameter_gen_base_rfc3588, + diameter_gen_base_accounting] + ++ [nas4005 || B]}, + {capabilities, [{'Auth-Application-Id', [0] ++ [1 || B]}, %% common, NAS + {'Acct-Application-Id', [3]}]}]. %% accounting + +client_apps(D, Caps) -> + if D == rfc4005 -> + [{applications, [nas4005]}, + {capabilities, [{'Auth-Application-Id', [1]}, %% NAS + {'Acct-Application-Id', []} + | Caps]}]; + true -> + D0 = dict0(D), + [{applications, [acct(D0), D0]}, + {capabilities, Caps}] + end. + +have_nas() -> + false /= code:is_loaded(nas4005). remove_transports(Config) -> #group{client_service = CN, @@ -515,6 +542,11 @@ capx(_, #diameter_caps{origin_host = {OH,DH}}) -> %% =========================================================================== +%% Fail only this testcase if the RFC 4005 dictionary hasn't been +%% successfully compiled and loaded. +rfc4005(Config) -> + true = proplists:get_value(rfc4005, Config). + %% Ensure that result codes have the expected values. result_codes(_Config) -> {2001, 3001, 3002, 3003, 3004, 3007, 3008, 3009, 5001, 5011, 5014} @@ -615,12 +647,10 @@ send_unknown_short(Config, M, RC) -> data = <<17>>}]}], ['ASA', {'Session-Id', _}, {'Result-Code', RC} | Avps] = call(Config, Req), - [#'diameter_base_Failed-AVP'{'AVP' = As}] - = proplists:get_value('Failed-AVP', Avps), - [#diameter_avp{code = 999, - is_mandatory = M, - data = <<17, _/binary>>}] %% extra bits from padding - = As. + [[#diameter_avp{code = 999, + is_mandatory = M, + data = <<17, _/binary>>}]] %% extra bits from padding + = failed_avps(Avps, Config). %% Ditto but set the M flag. send_unknown_mandatory(Config) -> @@ -629,12 +659,10 @@ send_unknown_mandatory(Config) -> data = <<17>>}]}], ['ASA', {'Session-Id', _}, {'Result-Code', ?AVP_UNSUPPORTED} | Avps] = call(Config, Req), - [#'diameter_base_Failed-AVP'{'AVP' = As}] - = proplists:get_value('Failed-AVP', Avps), - [#diameter_avp{code = 999, - is_mandatory = true, - data = <<17>>}] - = As. + [[#diameter_avp{code = 999, + is_mandatory = true, + data = <<17>>}]] + = failed_avps(Avps, Config). %% Ditto, and point the AVP length past the end of the message. Expect %% 5014 instead of 5001. @@ -649,13 +677,11 @@ send_unexpected_mandatory_decode(Config) -> data = <<12:32>>}]}], ['ASA', {'Session-Id', _}, {'Result-Code', ?AVP_UNSUPPORTED} | Avps] = call(Config, Req), - [#'diameter_base_Failed-AVP'{'AVP' = As}] - = proplists:get_value('Failed-AVP', Avps), - [#diameter_avp{code = 27, - is_mandatory = true, - value = 12, - data = <<12:32>>}] - = As. + [[#diameter_avp{code = 27, + is_mandatory = true, + value = 12, + data = <<12:32>>}]] + = failed_avps(Avps, Config). %% Send an containing a faulty Grouped AVP (empty Proxy-Host in %% Proxy-Info) and expect that only the faulty AVP is sent in @@ -667,13 +693,9 @@ send_grouped_error(Config) -> {'Proxy-State', ""}]]}], ['ASA', {'Session-Id', _}, {'Result-Code', ?INVALID_AVP_LENGTH} | Avps] = call(Config, Req), - [#'diameter_base_Failed-AVP'{'AVP' = As}] - = proplists:get_value('Failed-AVP', Avps), - [#diameter_avp{name = 'Proxy-Info', - value = #'diameter_base_Proxy-Info' - {'Proxy-Host' = Empty, - 'Proxy-State' = undefined}}] - = As, + [[#diameter_avp{name = 'Proxy-Info', value = V}]] + = failed_avps(Avps, Config), + {Empty, undefined, []} = proxy_info(V, Config), <<0>> = iolist_to_binary(Empty). %% Send an STR that the server ignores. @@ -729,10 +751,10 @@ send_invalid_avp_length(Config) -> {'User-Name', _}, {'Class', _}, {'Error-Message', _}, - {'Error-Reporting-Host', _}, - {'Failed-AVP', [#'diameter_base_Failed-AVP'{'AVP' = [_]}]} - | _] - = call(Config, Req). + {'Error-Reporting-Host', _} + | Avps] + = call(Config, Req), + [[_]] = failed_avps(Avps, Config). %% Send a request containing 5xxx errors that the server rejects with %% 3xxx. @@ -935,6 +957,29 @@ send_anything(Config) -> %% =========================================================================== +failed_avps(Avps, Config) -> + #group{client_dict = D} = proplists:get_value(group, Config), + [failed_avp(D, T) || T <- proplists:get_value('Failed-AVP', Avps)]. + +failed_avp(nas4005, {'nas_Failed-AVP', As}) -> + As; +failed_avp(_, #'diameter_base_Failed-AVP'{'AVP' = As}) -> + As. + +proxy_info(Rec, Config) -> + #group{client_dict = D} = proplists:get_value(group, Config), + if D == nas4005 -> + {'nas_Proxy-Info', H, S, As} + = Rec, + {H,S,As}; + true -> + #'diameter_base_Proxy-Info'{'Proxy-Host' = H, + 'Proxy-State' = S, + 'AVP' = As} + = Rec, + {H,S,As} + end. + group(Config) -> #group{} = proplists:get_value(group, Config). @@ -956,7 +1001,7 @@ call(Config, Req, Opts) -> Name = proplists:get_value(testcase, Config), #group{client_service = CN, client_encoding = ReqEncoding, - client_dict0 = Dict0} + client_dict = Dict0} = Group = group(Config), diameter:call(CN, @@ -996,9 +1041,21 @@ msg([H|T], record, Dict) -> msg(Msg, _, _) -> Msg. +appdict(rfc4005) -> + nas4005; +appdict(D) -> + dict0(D). + dict0(D) -> ?A("diameter_gen_base_" ++ ?L(D)). +dict(Msg, nas4005 = D) -> + if 'answer-message' == hd(Msg); + ?is_record(Msg, 'diameter_base_answer-message') -> + diameter_gen_base_rfc3588; + true -> + D + end; dict(Msg, Dict0) when 'ACR' == hd(Msg); 'ACA' == hd(Msg); @@ -1016,7 +1073,7 @@ acct(diameter_gen_base_rfc6733) -> %% Set only values that aren't already. set(_, [H|T], Vs) -> [H | Vs ++ T]; -set(#group{client_dict0 = Dict0} = _Group, Rec, Vs) -> +set(#group{client_dict = Dict0} = _Group, Rec, Vs) -> Dict = dict(Rec, Dict0), lists:foldl(fun({F,_} = FV, A) -> set(Dict, Dict:'#get-'(F, A), FV, A) @@ -1086,7 +1143,7 @@ log(#diameter_packet{bin = Bin} = P, T) %% prepare/4 -prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group) +prepare(Pkt, Caps, N, #group{client_dict = Dict0} = Group) when N == send_unknown_short_mandatory; N == send_unknown_short -> Req = prepare(Pkt, Caps, Group), @@ -1106,7 +1163,7 @@ prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group) <> = Bin, E#diameter_packet{bin = <>}; -prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group) +prepare(Pkt, Caps, N, #group{client_dict = Dict0} = Group) when N == send_long_avp_length; N == send_short_avp_length; N == send_zero_avp_length -> @@ -1132,7 +1189,7 @@ prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group) T/binary, Hdr/binary, AL:24, Data/binary>>}; -prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group) +prepare(Pkt, Caps, N, #group{client_dict = Dict0} = Group) when N == send_invalid_avp_length; N == send_invalid_reject -> Req = prepare(Pkt, Caps, Group), @@ -1147,7 +1204,7 @@ prepare(Pkt, Caps, N, #group{client_dict0 = Dict0} = Group) <> = H0, %% assert E#diameter_packet{bin = <>}; -prepare(Pkt, Caps, send_unexpected_mandatory, #group{client_dict0 = Dict0} +prepare(Pkt, Caps, send_unexpected_mandatory, #group{client_dict = Dict0} = Group) -> Req = prepare(Pkt, Caps, Group), #diameter_packet{bin = <>} @@ -1157,7 +1214,7 @@ prepare(Pkt, Caps, send_unexpected_mandatory, #group{client_dict0 = Dict0} Avp = <>, E#diameter_packet{bin = <>}; -prepare(Pkt, Caps, send_grouped_error, #group{client_dict0 = Dict0} +prepare(Pkt, Caps, send_grouped_error, #group{client_dict = Dict0} = Group) -> Req = prepare(Pkt, Caps, Group), #diameter_packet{bin = Bin} @@ -1189,14 +1246,14 @@ prepare(Pkt, Caps, send_grouped_error, #group{client_dict0 = Dict0} Payload/binary, T/binary>>}; -prepare(Pkt, Caps, send_unsupported, #group{client_dict0 = Dict0} = Group) -> +prepare(Pkt, Caps, send_unsupported, #group{client_dict = Dict0} = Group) -> Req = prepare(Pkt, Caps, Group), #diameter_packet{bin = <>} = E = diameter_codec:encode(Dict0, Pkt#diameter_packet{msg = Req}), E#diameter_packet{bin = <>}; -prepare(Pkt, Caps, send_unsupported_app, #group{client_dict0 = Dict0} +prepare(Pkt, Caps, send_unsupported_app, #group{client_dict = Dict0} = Group) -> Req = prepare(Pkt, Caps, Group), #diameter_packet{bin = <>} @@ -1225,6 +1282,7 @@ prepare(Pkt, Caps, _Name, Group) -> prepare(#diameter_packet{msg = Req}, Caps, Group) when ?is_record(Req, diameter_base_accounting_ACR); + ?is_record(Req, nas_ACR); 'ACR' == hd(Req) -> #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, DR}} @@ -1237,6 +1295,7 @@ prepare(#diameter_packet{msg = Req}, Caps, Group) prepare(#diameter_packet{msg = Req}, Caps, Group) when ?is_record(Req, diameter_base_ASR); + ?is_record(Req, nas_ASR); 'ASR' == hd(Req) -> #diameter_caps{origin_host = {OH, DH}, origin_realm = {OR, DR}} @@ -1250,6 +1309,7 @@ prepare(#diameter_packet{msg = Req}, Caps, Group) prepare(#diameter_packet{msg = Req}, Caps, Group) when ?is_record(Req, diameter_base_STR); + ?is_record(Req, nas_STR); 'STR' == hd(Req) -> #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, DR}} @@ -1262,6 +1322,7 @@ prepare(#diameter_packet{msg = Req}, Caps, Group) prepare(#diameter_packet{msg = Req}, Caps, Group) when ?is_record(Req, diameter_base_RAR); + ?is_record(Req, nas_RAR); 'RAR' == hd(Req) -> #diameter_caps{origin_host = {OH, DH}, origin_realm = {OR, DR}} @@ -1287,7 +1348,7 @@ handle_answer(Pkt, Req, [$C|_], Peer, {send_detach = Name, Group}, _, X) -> {Pid, Ref} = X, Pid ! {Ref, answer(Pkt, Req, Peer, Name, Group)}. -answer(Pkt, Req, _Peer, Name, #group{client_dict0 = Dict0}) -> +answer(Pkt, Req, _Peer, Name, #group{client_dict = Dict0}) -> #diameter_packet{header = H, msg = Ans, errors = Es} = Pkt, ApplId = app(Req, Name, Dict0), #diameter_header{application_id = ApplId} = H, %% assert @@ -1343,20 +1404,68 @@ handle_request(#diameter_packet{header = H, msg = M, avps = As}, V = EI bsr B, %% assert V = HI bsr B, %% #diameter_caps{origin_state_id = {_,[Id]}} = Caps, - answer(origin(Id), request(M, [H|As], Caps)). + wrap(origin(Id), H, request(nas_to_base(M, H), [H|As], Caps)). -answer(T, {Tag, Action, Post}) -> - {Tag, answer(T, Action), Post}; -answer(_, {reply, [#diameter_header{} | _]} = T) -> +wrap(T, H, {Tag, Action, Post}) -> + {Tag, wrap(T, H, Action), Post}; +wrap(_, _, {reply, [#diameter_header{} | _]} = T) -> T; -answer({A,C}, {reply, Ans}) -> - answer(C, {reply, msg(Ans, A, diameter_gen_base_rfc3588)}); -answer(pkt, {reply, Ans}) +wrap({A,C}, H, {reply, Ans}) -> + Msg = msg(Ans, A, diameter_gen_base_rfc3588), + wrap(C, H, {reply, base_to_nas(Msg, H)}); +wrap(pkt, _, {reply, Ans}) when not is_record(Ans, diameter_packet) -> {reply, #diameter_packet{msg = Ans}}; -answer(_, T) -> +wrap(_, _, T) -> T. +%% nas_to_base/1 +%% +%% Map an RFC 4005 message to RFC 3588, to return the same answer in +%% both cases. + +nas_to_base(Rec, #diameter_header{application_id = 1}) -> + [R | Values] = nas4005:'#get-'(Rec), + "nas_" ++ Name = ?L(R), + {D, RN} = case Name of + "ACR" -> + {diameter_gen_base_accounting, + diameter_base_accounting_ACR}; + _ -> + {diameter_gen_base_rfc3588, + ?A("diameter_base_" ++ Name)} + end, + Fs = D:'#info-'(RN), + D:'#new-'([RN | [T || {F,_} = T <- Values, lists:member(F, Fs)]]); + +nas_to_base(Rec, _) -> + Rec. + +%% base_to_nas/2 + +base_to_nas(#diameter_packet{msg = Msg} = Pkt, H) -> + Pkt#diameter_packet{msg = base_to_nas(Msg, H)}; + +base_to_nas(Rec, #diameter_header{application_id = 1}) + when is_tuple(Rec), not ?is_record(Rec, 'diameter_base_answer-message') -> + D = case element(1, Rec) of + diameter_base_accounting_ACA -> + diameter_gen_base_accounting; + _ -> + diameter_gen_base_rfc3588 + end, + [R | Values] = D:'#get-'(Rec), + "diameter_base_" ++ N = ?L(R), + Name = ?A("nas_" ++ if N == "accounting_ACA" -> + "ACA"; + true -> + N + end), + nas4005:'#new-'([Name | Values]); + +base_to_nas(Msg, _) -> + Msg. + %% request/3 %% send_experimental_result @@ -1523,3 +1632,29 @@ message(ack, <<_:32, 1, _/bits>>, _) -> %% sent answer or discarded request message(ack, _, N) -> [0 =< N, fun ?MODULE:message/3, N+1]. + +%% ------------------------------------------------------------------------ + +compile_and_load() -> + try + Path = hd([P || H <- [[here(), ".."], [code:lib_dir(diameter)]], + P <- [filename:join(H ++ ["examples", + "dict", + "rfc4005_nas.dia"])], + {ok, _} <- [file:read_file_info(P)]]), + {ok, [Forms]} + = diameter_make:codec(Path, [return, + forms, + {name, "nas4005"}, + {prefix, "nas"}, + {inherits, "common/diameter_gen_base_rfc3588"}]), + {ok, nas4005, Bin, []} = compile:forms(Forms, [debug_info, return]), + {module, nas4005} = code:load_binary(nas4005, "nas4005", Bin), + true + catch + E:R -> + {E, R, erlang:get_stacktrace()} + end. + +here() -> + filename:dirname(code:which(?MODULE)). -- cgit v1.2.3 From c63f31deeff7eea054ff3b84b73c1b5b03d5aec6 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Wed, 26 Jul 2017 14:17:37 +0200 Subject: Fix message_cb in traffic suite Matched a byte instead of a bit, and increment/decrement wasn't symmetric. Allow more requests since some requests timeout. Bungled in commit 09089872. --- lib/diameter/test/diameter_traffic_SUITE.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 0a93033d83..dd0dfc776d 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -467,7 +467,7 @@ add_transports(Config) -> LRef = ?util:listen(SN, [T, {sender, SS}, - {message_cb, ST andalso {?MODULE, message, [4]}} + {message_cb, ST andalso {?MODULE, message, [0]}} | [{packet, hd(?util:scramble([false, raw]))} || T == sctp andalso CS]], [{capabilities_cb, fun capx/2}, @@ -1614,8 +1614,8 @@ message(Dir, #diameter_packet{bin = Bin}, N) -> message(Dir, Bin, N); %% incoming request -message(recv, <<_:32, 1, _/bits>> = Bin, N) -> - [Bin, 1 < N, fun ?MODULE:message/3, N-1]; +message(recv, <<_:32, 1:1, _/bits>> = Bin, N) -> + [Bin, N < 16, fun ?MODULE:message/3, N+1]; %% incoming answer message(recv, Bin, _) -> @@ -1626,12 +1626,12 @@ message(send, Bin, _) -> [Bin]; %% sent request -message(ack, <<_:32, 1, _/bits>>, _) -> +message(ack, <<_:32, 1:1, _/bits>>, _) -> []; %% sent answer or discarded request message(ack, _, N) -> - [0 =< N, fun ?MODULE:message/3, N+1]. + [N =< 16, fun ?MODULE:message/3, N-1]. %% ------------------------------------------------------------------------ -- cgit v1.2.3 From b9f2d5a867806563c11f8b13e7977f8d03b64a54 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 3 Jul 2017 11:49:23 +0200 Subject: Fix obsolete diameter_gen.hrl comments Most of the contents were moved to module diameter_gen in commit 205521d3. --- lib/diameter/src/base/diameter_codec.erl | 2 +- lib/diameter/src/compiler/diameter_codegen.erl | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index 82fa796e69..93c1b28f4c 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -29,7 +29,7 @@ msg_name/2, msg_id/1]). -%% Towards generated encoders (from diameter_gen.hrl). +%% towards diameter_gen -export([pack_data/2, pack_avp/2]). diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl index f56e4a5249..4e6fe32d69 100644 --- a/lib/diameter/src/compiler/diameter_codegen.erl +++ b/lib/diameter/src/compiler/diameter_codegen.erl @@ -21,15 +21,14 @@ -module(diameter_codegen). %% -%% This module generates erl/hrl files for encode/decode modules -%% from the orddict parsed from a dictionary file (.dia) by -%% diameter_dict_util. The generated code is simple (one-liners), -%% the generated functions being called by code included iin the -%% generated modules from diameter_gen.hrl. The orddict itself is -%% returned by dict/0 in the generated module and diameter_dict_util -%% calls this function when importing dictionaries as a consequence -%% of @inherits sections. That is, @inherits introduces a dependency -%% on the beam file of another dictionary. +%% This module generates erl/hrl files for encode/decode modules from +%% the orddict parsed from a dictionary file by diameter_dict_util. +%% The generated code is simple (one-liners), and is called from +%% diameter_gen. The orddict itself is returned by dict/0 in the +%% generated module and diameter_dict_util calls this function when +%% importing dictionaries as a consequence of @inherits sections. That +%% is, @inherits introduces a dependency on the beam file of another +%% dictionary. %% -export([from_dict/4, -- cgit v1.2.3 From e30c38a44bbe2872e5b9b0ad46774c19b6af5292 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 3 Jul 2017 16:40:51 +0200 Subject: Count AVP arities during decode Instead of after, during the check that AVPs have sufficient arity. This makes the arity checks independent of the record decode, which will allow the latter to be made optional. --- lib/diameter/src/base/diameter_gen.erl | 246 ++++++++++++++++++--------------- 1 file changed, 132 insertions(+), 114 deletions(-) diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index e832832876..6f11583868 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -172,14 +172,17 @@ enc_AVP(_Name, {_Dict, _AvpName, _Data} = T, Opts, _) -> when Failed :: [{5000..5999, #diameter_avp{}}]. decode_avps(Name, Recs, #{module := Mod} = Opts) -> - {Avps, {Rec, Failed}} + {Avps, {Rec, AM, Failed}} = mapfoldl(fun(T,A) -> decode(Name, Opts, Mod, T, A) end, - {newrec(Mod, Name), []}, + {newrec(Mod, Name), #{}, []}, Recs), - {Rec, Avps, Failed ++ missing(Rec, Name, Failed, Opts, Mod)}. + %% AM counts the number of top-level AVPs, which missing/4 then + %% uses when adding 5005 errors. + {Rec, Avps, Failed ++ missing(Name, Opts, Mod, AM)}. + %% Append 5005 errors so that errors are reported in the order %% encountered. Failed-AVP should typically contain the first -%% encountered error accordg to the RFC. +%% error encountered. %% mapfoldl/3 %% @@ -194,6 +197,8 @@ mapfoldl(F, Acc0, [T|Rest], List) -> mapfoldl(_, Acc, [], List) -> {List, Acc}. +%% missing/4 + %% 3588: %% %% DIAMETER_MISSING_AVP 5005 @@ -204,57 +209,41 @@ mapfoldl(_, Acc, [], List) -> %% Vendor-Id if applicable. The value field of the missing AVP %% should be of correct minimum length and contain zeros. -missing(Rec, Name, Failed, Opts, Mod) -> - Avps = lists:foldl(fun({_, #diameter_avp{code = C, vendor_id = V}}, A) -> - maps:put({C,V}, true, A) - end, - maps:new(), - Failed), - missing(Mod:avp_arity(Name), tl(tuple_to_list(Rec)), Avps, Opts, Mod, []). - -missing([{Name, Arity} | As], [Value | Vs], Avps, Opts, Mod, Acc) -> - missing(As, - Vs, - Avps, - Opts, - Mod, - case - [H || missing_arity(Arity, Value), - {C,_,V} = H <- [Mod:avp_header(Name)], - not maps:is_key({C,V}, Avps)] - of - [H] -> - [{5005, empty_avp(Name, H, Opts, Mod)} | Acc]; - [] -> - Acc - end); - -missing([], [], _, _, _, Acc) -> - Acc. - -%% Maximum arities have already been checked in building the record. - -missing_arity(1, V) -> - V == undefined; -missing_arity({0, _}, _) -> - false; -missing_arity({1, _}, L) -> - [] == L; -missing_arity({Min, _}, L) -> - not has_prefix(Min, L). - -%% Compare a non-negative integer and the length of a list without -%% computing the length. -has_prefix(0, _) -> - true; -has_prefix(_, []) -> +missing(Name, Opts, Mod, AM) -> + lists:foldl(fun(T,A) -> missing(T, AM, Opts, Mod, A) end, + [], + Mod:avp_arity(Name)). + +%% missing/5 + +missing({Name, Arity}, AM, Opts, Mod, Acc) -> + case missing(Name, Arity, AM) of + true -> [{5005, empty_avp(Name, Opts, Mod)} | Acc]; + false -> Acc + end. + +%% missing/3 + +missing(Name, Arity, AM) -> + 'AVP' /= Name andalso too_few(Name, Arity, AM). + +%% too_few/3 +%% +%% Maximum arities have already been checked during the decode. + +too_few(_, {0, _}, _) -> false; -has_prefix(N, [_|L]) -> - has_prefix(N-1, L). -%% empty_avp/4 +too_few(FieldName, 1, AM) -> + not maps:is_key(FieldName, AM); + +too_few(FieldName, {M, _}, AM) -> + maps:get(FieldName, AM, 0) < M. -empty_avp(Name, {Code, Flags, VId}, Opts, Mod) -> +%% empty_avp/3 + +empty_avp(Name, Opts, Mod) -> + {Code, Flags, VId} = Mod:avp_header(Name), {Name, Type} = Mod:avp_name(Code, VId), #diameter_avp{name = Name, code = Code, @@ -280,8 +269,27 @@ decode(Name, Mod, #diameter_avp{code = Code, vendor_id = Vid} = Avp, - Acc) -> - decode(Name, Opts, Mod, Mod:avp_name(Code, Vid), Avp, Acc). + {Rec, AM, Failed}) -> + T = Mod:avp_name(Code, Vid), + decode(Name, Opts, Mod, T, Avp, {Rec, incr(field(T), AM), Failed}). + +%% field/1 + +field({AvpName, _Type}) -> + AvpName; + +field('AVP' = A) -> + A. + +%% incr/2 + +incr(Key, Map) -> + maps:update_with(Key, fun incr/1, 1, Map). + +%% incr/1 + +incr(N) -> + N + 1. %% decode/6 @@ -352,15 +360,13 @@ decode(Name, Opts0, Mod, {AvpName, Type}, Avp, Acc) -> %% decode is packed into 'AVP'. %% Reset the dictionary for best-effort decode of Failed-AVP. - DecMod = if Failed -> - AppMod; - true -> - Mod + DecMod = if Failed -> AppMod; + true -> Mod end, - %% On decode, a Grouped AVP is represented as a #diameter_avp{} - %% list with AVP as head and component AVPs as tail. On encode, - %% data can be a list of component AVPs. + %% A Grouped AVP is represented as a #diameter_avp{} list with AVP + %% as head and component AVPs as tail. On encode, data can be a + %% list of component AVPs. try avp_decode(Data, AvpName, Opts, DecMod, Mod) of {Rec, As} when Type == 'Grouped' -> @@ -425,7 +431,7 @@ trim(Avp) -> %% decode_error/7 -decode_error(Name, [_ | Rec], _, #{failed_avp := true} = Opts, Mod, Avp, Acc) -> +decode_error(Name, [_|Rec], _, #{failed_avp := true} = Opts, Mod, Avp, Acc) -> decode_AVP(Name, Avp#diameter_avp{value = Rec}, Opts, Mod, Acc); decode_error(Name, _, _, #{failed_avp := true} = Opts, Mod, Avp, Acc) -> @@ -437,24 +443,25 @@ decode_error(_, [Error | _], ComponentAvps, _, _, Avp, Acc) -> decode_error(_, Error, ComponentAvps, _, _, Avp, Acc) -> decode_error(Error, Avp, Acc, ComponentAvps). -%% decode_error/5 +%% decode_error/6 decode_error(Name, _Reason, #{failed_avp := true} = Opts, Mod, Avp, Acc) -> decode_AVP(Name, Avp, Opts, Mod, Acc); -decode_error(Name, Reason, Opts, Mod, Avp, {Rec, Failed}) -> +decode_error(Name, Reason, Opts, Mod, Avp, {Rec, AM, Failed}) -> Stack = diameter_lib:get_stacktrace(), + AvpName = Avp#diameter_avp.name, diameter_lib:log(decode_error, ?MODULE, ?LINE, - {Reason, Name, Avp#diameter_avp.name, Mod, Stack}), - {Avp, {Rec, [rc(Reason, Avp, Opts, Mod) | Failed]}}. + {Reason, Name, AvpName, Mod, Stack}), + {Avp, {Rec, AM, [rc(Reason, Avp, Opts, Mod) | Failed]}}. %% decode_error/4 -decode_error({RC, ErrorData}, Avp, {Rec, Failed}, ComponentAvps) -> +decode_error({RC, ErrorData}, Avp, {Rec, AM, Failed}, ComponentAvps) -> E = Avp#diameter_avp{data = [ErrorData]}, - {[Avp | trim(ComponentAvps)], {Rec, [{RC, E} | Failed]}}. + {[Avp | trim(ComponentAvps)], {Rec, AM, [{RC, E} | Failed]}}. %% set_strict/3 @@ -490,8 +497,8 @@ decode_AVP(Name, Avp, Opts, Mod, Acc) -> %% DIAMETER_INVALID_AVP_LENGTH (5014). A module specified to a %% @custom_types tag in a dictionary file can also raise an error of %% this form. -rc({'DIAMETER', 5014 = RC, _}, #diameter_avp{name = AvpName} = Avp, Opts, Mod) -> - {RC, Avp#diameter_avp{data = Mod:empty_value(AvpName, Opts)}}; +rc({'DIAMETER', 5014 = RC, _}, #diameter_avp{name = AvpName} = A, Opts, Mod) -> + {RC, A#diameter_avp{data = Mod:empty_value(AvpName, Opts)}}; %% 3588: %% @@ -531,20 +538,33 @@ pack_avp(_, Arity, #diameter_avp{name = AvpName} = Avp, _Opts, Mod, Acc) -> %% type. pack_AVP(_, #diameter_avp{data = {5014 = RC, Data}} = Avp, _, _, Acc) -> - {Rec, Failed} = Acc, - {Rec, [{RC, Avp#diameter_avp{data = Data}} | Failed]}; + {Rec, AM, Failed} = Acc, + {Rec, AM, [{RC, Avp#diameter_avp{data = Data}} | Failed]}; pack_AVP(Name, Avp, Opts, Mod, Acc) -> - pack_arity(Name, pack_arity(Name, Opts, Mod, Avp), Avp, Mod, Acc). - -%% pack_arity/5 + Arity = pack_arity(Name, Opts, Mod, Avp), + if 0 == Arity -> + M = Avp#diameter_avp.is_mandatory, + {Rec, AM, Failed} = Acc, + {Rec, AM, [{if M -> 5001; true -> 5008 end, Avp} | Failed]}; + true -> + pack(Arity, 'AVP', Avp, Mod, Acc) + end. -pack_arity(_, 0, #diameter_avp{is_mandatory = M} = Avp, _, Acc) -> - {Rec, Failed} = Acc, - {Rec, [{if M -> 5001; true -> 5008 end, Avp} | Failed]}; +%% 3588: +%% +%% DIAMETER_AVP_UNSUPPORTED 5001 +%% The peer received a message that contained an AVP that is not +%% recognized or supported and was marked with the Mandatory bit. A +%% Diameter message with this error MUST contain one or more Failed- +%% AVP AVP containing the AVPs that caused the failure. +%% +%% DIAMETER_AVP_NOT_ALLOWED 5008 +%% A message was received with an AVP that MUST NOT be present. The +%% Failed-AVP AVP MUST be included and contain a copy of the +%% offending AVP. -pack_arity(_, Arity, Avp, Mod, Acc) -> - pack(Arity, 'AVP', Avp, Mod, Acc). +%% pack_arity/4 %% Give Failed-AVP special treatment since (1) it'll contain any %% unrecognized mandatory AVP's and (2) the RFC 3588 grammar failed to @@ -573,31 +593,13 @@ pack_arity(Name, 0 end. -%% 3588: -%% -%% DIAMETER_AVP_UNSUPPORTED 5001 -%% The peer received a message that contained an AVP that is not -%% recognized or supported and was marked with the Mandatory bit. A -%% Diameter message with this error MUST contain one or more Failed- -%% AVP AVP containing the AVPs that caused the failure. -%% -%% DIAMETER_AVP_NOT_ALLOWED 5008 -%% A message was received with an AVP that MUST NOT be present. The -%% Failed-AVP AVP MUST be included and contain a copy of the -%% offending AVP. - %% pack/5 -pack(Arity, FieldName, Avp, Mod, {Rec, _} = Acc) -> - pack(Mod:'#get-'(FieldName, Rec), Arity, FieldName, Avp, Mod, Acc). - -%% pack/6 - -pack(undefined, 1, 'AVP' = F, Avp, Mod, {Rec, Failed}) -> %% unlikely - {Mod:'#set-'({F, Avp}, Rec), Failed}; - -pack(undefined, 1, F, #diameter_avp{value = V}, Mod, {Rec, Failed}) -> - {Mod:'#set-'({F, V}, Rec), Failed}; +pack(Arity, F, Avp, Mod, {Rec, AM, Failed}) -> + case too_many(F, Arity, AM) of + true -> {Rec, AM, [{5009, Avp} | Failed]}; + false -> {set(Arity, F, value(F, Avp), Mod, Rec), AM, Failed} + end. %% 3588: %% @@ -608,18 +610,34 @@ pack(undefined, 1, F, #diameter_avp{value = V}, Mod, {Rec, Failed}) -> %% the offending AVP that exceeded the maximum number of occurrences %% -pack(_, 1, _, Avp, _, {Rec, Failed}) -> - {Rec, [{5009, Avp} | Failed]}; +%% too_many/3 -pack(L, {_, Max}, F, Avp, Mod, {Rec, Failed}) -> - case '*' /= Max andalso has_prefix(Max+1, L) of - true -> - {Rec, [{5009, Avp} | Failed]}; - false when F == 'AVP' -> - {Mod:'#set-'({F, [Avp | L]}, Rec), Failed}; - false -> - {Mod:'#set-'({F, [Avp#diameter_avp.value | L]}, Rec), Failed} - end. +too_many(_, {_, '*'}, _) -> + false; + +too_many(FieldName, {_, M}, Map) -> + too_many(FieldName, M, Map); + +too_many(FieldName, M, Map) -> + #{FieldName := N} = Map, + M < N. + +%% set/5 + +set(1, F, Value, Mod, Rec) -> + Mod:'#set-'({F, Value}, Rec); + +set(_, F, V, Mod, Rec) -> + Vs = Mod:'#get-'(F, Rec), + Mod:'#set-'({F, [V|Vs]}, Rec). + +%% value/2 + +value('AVP', Avp) -> + Avp; + +value(_, #diameter_avp{value = V}) -> + V. %% --------------------------------------------------------------------------- %% # grouped_avp/3 -- cgit v1.2.3 From 722fa41564381dff0b7aa2b465193db30bb2f02f Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 6 Jul 2017 09:58:07 +0200 Subject: Add service_opt() record_decode To control whether or not messages and grouped AVPs are decoded to records, in #diameter_packet.msg and #diameter_avp.value respectively. The decode became unnecessary for diameter's needs in parent commit, which decoupled it from the checking of AVP arities. --- lib/diameter/doc/src/diameter.xml | 23 ++++++++++++++++++++++ lib/diameter/src/base/diameter.erl | 1 + lib/diameter/src/base/diameter_codec.erl | 3 ++- lib/diameter/src/base/diameter_config.erl | 3 +++ lib/diameter/src/base/diameter_gen.erl | 15 +++++++++++++- lib/diameter/src/base/diameter_peer_fsm.erl | 9 ++++++--- lib/diameter/src/base/diameter_service.erl | 1 + lib/diameter/src/base/diameter_traffic.erl | 9 ++++++--- lib/diameter/src/base/diameter_watchdog.erl | 9 ++++++--- lib/diameter/test/diameter_codec_SUITE.erl | 1 + .../diameter_test_unknown.erl | 1 + lib/diameter/test/diameter_codec_test.erl | 3 ++- 12 files changed, 66 insertions(+), 12 deletions(-) diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index 2cbe48ecce..663c9cc785 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -969,6 +969,29 @@ occur in the message in question.

+ +{record_decode, boolean()} + +

+Whether or not to decode message and grouped AVPs to records in the +msg field of diameter_packet records and value field of +diameter_avp records respectively. +If false then the fields are set to the same value.

+ +

+Defaults to true.

+ + +

+Disabling the record is useful for applications in which the records +aren't used/needed. +AVP values are available in the avps field of +diameter_packet records regardless of whether or not there is a record +decode.

+
+ +
+ {string_decode, boolean()} diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl index bd92e16fba..f411656e90 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -338,6 +338,7 @@ call(SvcName, App, Message) -> | {restrict_connections, restriction()} | {sequence, sequence() | evaluable()} | {share_peers, remotes()} + | {record_decode, boolean()} | {string_decode, boolean()} | {strict_mbit, boolean()} | {incoming_maxlen, message_length()} diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index 93c1b28f4c..5e4c6e6d8f 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -287,7 +287,8 @@ rec2msg(Mod, Rec) -> %% longer *the* decode. decode(Mod, Pkt) -> - Opts = #{string_decode => true, + Opts = #{record_decode => true, + string_decode => true, strict_mbit => true, rfc => 6733}, decode(Mod, Opts, Pkt). diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index 34018ae6d3..46a3e362ac 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -713,6 +713,7 @@ make_config(SvcName, Opts) -> {nodes, restrict_connections}, {16#FFFFFF, incoming_maxlen}, {true, strict_mbit}, + {true, record_decode}, {true, string_decode}, {[], spawn_opt}]), @@ -756,6 +757,7 @@ opt(K, false = B) K == monitor; K == restrict_connections; K == strict_mbit; + K == record_decode; K == string_decode -> B; @@ -763,6 +765,7 @@ opt(K, true = B) when K == share_peers; K == use_shared_peers; K == strict_mbit; + K == record_decode; K == string_decode -> B; diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index 6f11583868..4879ad8f6c 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -174,7 +174,7 @@ enc_AVP(_Name, {_Dict, _AvpName, _Data} = T, Opts, _) -> decode_avps(Name, Recs, #{module := Mod} = Opts) -> {Avps, {Rec, AM, Failed}} = mapfoldl(fun(T,A) -> decode(Name, Opts, Mod, T, A) end, - {newrec(Mod, Name), #{}, []}, + {newrec(Mod, Name, Opts), #{}, []}, Recs), %% AM counts the number of top-level AVPs, which missing/4 then %% uses when adding 5005 errors. @@ -624,6 +624,9 @@ too_many(FieldName, M, Map) -> %% set/5 +set(_, _, _, _, undefined = No) -> + No; + set(1, F, Value, Mod, Rec) -> Mod:'#set-'({F, Value}, Rec); @@ -723,5 +726,15 @@ empty(Name, #{module := Mod} = Opts) -> %% ------------------------------------------------------------------------------ +%% newrec/3 + +newrec(_, _, #{record_decode := false}) -> + undefined; + +newrec(Mod, Name, _) -> + newrec(Mod, Name). + +%% newrec/2 + newrec(Mod, Name) -> Mod:'#new-'(Mod:name2rec(Name)). diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index 1b0dc417e5..f2fbb70270 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -128,7 +128,8 @@ %% outgoing DPR; boolean says whether or not %% the request was sent explicitly with %% diameter:call/4. - codec :: #{string_decode := boolean(), + codec :: #{record_decode := true, + string_decode := boolean(), strict_mbit := boolean(), rfc := 3588 | 6733, ordered_encode := false}, @@ -253,11 +254,13 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) -> length_errors = LengthErr, strict = Strictness, incoming_maxlen = Maxlen, - codec = maps:with([string_decode, + codec = maps:with([record_decode, + string_decode, strict_mbit, rfc, ordered_encode], - SvcOpts#{ordered_encode => false})}. + SvcOpts#{ordered_encode => false, + record_decode => true})}. %% The transport returns its local ip addresses so that different %% transports on the same service can use different local addresses. %% The local addresses are put into Host-IP-Address avps here when diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index a976a8b998..6dc4889c82 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -113,6 +113,7 @@ restrict_connections := diameter:restriction(), incoming_maxlen := diameter:message_length(), strict_mbit := boolean(), + record_decode := boolean(), string_decode := boolean(), spawn_opt := list() | {module(), atom(), list()}}}). diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index 85378babea..228d9802ad 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -76,7 +76,8 @@ service_name :: diameter:service_name(), apps :: [#diameter_app{}], sequence :: diameter:sequence(), - codec :: #{string_decode := boolean(), + codec :: #{record_decode := boolean(), + string_decode := boolean(), strict_mbit := boolean(), incoming_maxlen := diameter:message_length()}}). %% Note that incoming_maxlen is currently handled in diameter_peer_fsm, @@ -102,7 +103,8 @@ make_recvdata([SvcName, PeerT, Apps, SvcOpts | _]) -> peerT = PeerT, apps = Apps, sequence = Mask, - codec = maps:with([string_decode, + codec = maps:with([record_decode, + string_decode, strict_mbit, ordered_encode, incoming_maxlen], @@ -1933,7 +1935,8 @@ choose(false, _, X) -> X. %% Decode options sufficient for AVP extraction. decode_opts(Dict) -> - #{string_decode => false, + #{record_decode => true, + string_decode => false, strict_mbit => false, failed_avp => false, dictionary => Dict}. diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl index a63425d92a..c3dc8c3bf0 100644 --- a/lib/diameter/src/base/diameter_watchdog.erl +++ b/lib/diameter/src/base/diameter_watchdog.erl @@ -72,7 +72,8 @@ restrict := boolean(), suspect := non_neg_integer(), %% OKAY -> SUSPECT okay := non_neg_integer()}, %% REOPEN -> OKAY - codec :: #{string_decode := false, + codec :: #{record_decode := false, + string_decode := false, strict_mbit := boolean(), failed_avp := false, rfc := 3588 | 6733, @@ -135,7 +136,8 @@ i({Ack, T, Pid, {Opts, putr(restart, {T, Opts, Svc, SvcOpts}), %% save seeing it in trace putr(dwr, dwr(Caps)), %% Nodes = restrict_nodes(Restrict), - CodecKeys = [string_decode, + CodecKeys = [record_decode, + string_decode, strict_mbit, incoming_maxlen, spawn_opt, @@ -155,7 +157,8 @@ i({Ack, T, Pid, {Opts, suspect => 1, okay => 3}, Opts)), - codec = maps:with(CodecKeys, SvcOpts#{string_decode := false, + codec = maps:with(CodecKeys, SvcOpts#{record_decode := false, + string_decode := false, ordered_encode => false})}. wait(Ref, Pid) -> diff --git a/lib/diameter/test/diameter_codec_SUITE.erl b/lib/diameter/test/diameter_codec_SUITE.erl index 9f08f49f9f..31332537e9 100644 --- a/lib/diameter/test/diameter_codec_SUITE.erl +++ b/lib/diameter/test/diameter_codec_SUITE.erl @@ -292,6 +292,7 @@ recode(Msg, Dict) -> opts(Mod) -> #{dictionary => Mod, + record_decode => true, string_decode => false, strict_mbit => true, rfc => 6733, diff --git a/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl b/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl index 700910878c..fe602c9ee5 100644 --- a/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl +++ b/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl @@ -78,6 +78,7 @@ dec('BR', #diameter_packet opts(Mod) -> #{dictionary => Mod, + record_decode => true, string_decode => true, strict_mbit => true, rfc => 6733, diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl index b548f85cb8..7595e7edfc 100644 --- a/lib/diameter/test/diameter_codec_test.erl +++ b/lib/diameter/test/diameter_codec_test.erl @@ -219,7 +219,8 @@ opts(Mod) -> dictionary => Mod}. opts() -> - #{string_decode => true, + #{record_decode => true, + string_decode => true, strict_mbit => true, rfc => 6733, failed_avp => false}. -- cgit v1.2.3 From 1b3b64af3d9a5441b6da37cf4e97b59cb043f33b Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 6 Jul 2017 11:02:31 +0200 Subject: Let messages and grouped AVPs be encoded/decoded from/to maps With {record_decode, map}. The option name is arguably a bit misleading now, but not too objectionable given that the encode/decode in question has historically only been of records. One advantage of the map decode is that the map only contains values for those AVPs existing in the message or grouped AVP in question. The name of the message or grouped AVP is stored in with key ':name', the leading colon ensuring that the key isn't a diameter-name. Decoding to maps makes the hrl files generated from dictionary files largely irrelevant. There are value defines generated into these, but they're typically so long as to be unusable. --- lib/diameter/doc/src/diameter.xml | 9 ++-- lib/diameter/doc/src/diameter_codec.xml | 9 +++- lib/diameter/src/base/diameter.erl | 2 +- lib/diameter/src/base/diameter_codec.erl | 3 ++ lib/diameter/src/base/diameter_config.erl | 3 ++ lib/diameter/src/base/diameter_gen.erl | 28 +++++++++- lib/diameter/src/base/diameter_service.erl | 2 +- lib/diameter/src/base/diameter_traffic.erl | 87 ++++++++++++++++++++++-------- 8 files changed, 111 insertions(+), 32 deletions(-) diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index 663c9cc785..bfb6da41b5 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -539,7 +539,7 @@ that matches no peer.

The host and realm filters cause the Destination-Host and Destination-Realm AVPs to be extracted from the -outgoing request, assuming it to be a record- or list-valued +outgoing request, assuming it to be a record-, list- or map-valued &codec_message;, and assuming at most one of each AVP. If this is not the case then the {host|realm, &dict_DiameterIdentity;} filters must be used to achieve the desired result. @@ -970,13 +970,14 @@ occur in the message in question.

-{record_decode, boolean()} +{record_decode, boolean() | map}

Whether or not to decode message and grouped AVPs to records in the msg field of diameter_packet records and value field of -diameter_avp records respectively. -If false then the fields are set to the same value.

+diameter_avp records respectively, or to an alternate format. +If false then the fields are set to the same value. +See also &codec_message;.

Defaults to true.

diff --git a/lib/diameter/doc/src/diameter_codec.xml b/lib/diameter/doc/src/diameter_codec.xml index 0117c1c88a..6262dbf00d 100644 --- a/lib/diameter/doc/src/diameter_codec.xml +++ b/lib/diameter/doc/src/diameter_codec.xml @@ -230,7 +230,7 @@ header.

-message() = record() | list() +message() = record() | list() | map()

The representation of a Diameter message as passed to @@ -240,7 +240,12 @@ a message as defined in a dictionary file is encoded as a record with one field for each component AVP. Equivalently, a message can also be encoded as a list whose head is the atom-valued message name (as specified in the relevant dictionary -file) and whose tail is a list of {AvpName, AvpValue} pairs.

+file) and whose tail is a list of {AvpName, AvpValues} pairs, +or as a map with values keyed on AVP names and the message name in key +:name. +The format at decode is determined by &mod_service_opt; +record_decode. +Any of the formats is accepted at encode.

Another list-valued representation allows a message to be specified diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl index f411656e90..4269c84cd2 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -338,7 +338,7 @@ call(SvcName, App, Message) -> | {restrict_connections, restriction()} | {sequence, sequence() | evaluable()} | {share_peers, remotes()} - | {record_decode, boolean()} + | {record_decode, boolean() | map} | {string_decode, boolean()} | {strict_mbit, boolean()} | {incoming_maxlen, message_length()} diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index 5e4c6e6d8f..0c43d52093 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -275,6 +275,9 @@ rec2msg(_, [Name|_]) when is_atom(Name) -> Name; +rec2msg(_, #{':name' := Name}) -> + Name; + rec2msg(Mod, Rec) -> Mod:rec2msg(element(1, Rec)). diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index 46a3e362ac..a790b946c1 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -769,6 +769,9 @@ opt(K, true = B) K == string_decode -> B; +opt(record_decode, map = T) -> + T; + opt(restrict_connections, T) when T == node; T == nodes -> diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index 4879ad8f6c..be2e221b7e 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -50,7 +50,9 @@ %% # encode_avps/3 %% --------------------------------------------------------------------------- --spec encode_avps(parent_name(), parent_record() | avp_values(), map()) +-spec encode_avps(parent_name(), + parent_record() | avp_values() | map(), + map()) -> iolist() | no_return(). @@ -83,6 +85,11 @@ encode(Name, Vals, Opts, Mod) when is_list(Vals) -> encode(Name, Mod:'#set-'(Vals, newrec(Mod, Name)), Opts, Mod); +encode(Name, Map, Opts, Mod) + when is_map(Map) -> + [enc(Name, F, A, V, Opts, Mod) || {F,A} <- Mod:avp_arity(Name), + V <- [maps:get(F, Map, def(A))]]; + encode(Name, Rec, Opts, Mod) -> [encode(Name, F, V, Opts, Mod) || {F,V} <- Mod:'#get-'(Rec)]. @@ -627,6 +634,14 @@ too_many(FieldName, M, Map) -> set(_, _, _, _, undefined = No) -> No; +set(1, F, Value, _, Map) + when is_map(Map) -> + maps:put(F, Value, Map); + +set(_, F, V, _, Map) + when is_map(Map) -> + maps:update_with(F, fun(Vs) -> [V|Vs] end, [V], Map); + set(1, F, Value, Mod, Rec) -> Mod:'#set-'({F, Value}, Rec); @@ -731,6 +746,9 @@ empty(Name, #{module := Mod} = Opts) -> newrec(_, _, #{record_decode := false}) -> undefined; +newrec(_, Name, #{record_decode := map}) -> + #{':name' => Name}; + newrec(Mod, Name, _) -> newrec(Mod, Name). @@ -738,3 +756,11 @@ newrec(Mod, Name, _) -> newrec(Mod, Name) -> Mod:'#new-'(Mod:name2rec(Name)). + +%% def/1 + +def(1) -> + undefined; + +def(_) -> + []. diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 6dc4889c82..3e555f6263 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -113,7 +113,7 @@ restrict_connections := diameter:restriction(), incoming_maxlen := diameter:message_length(), strict_mbit := boolean(), - record_decode := boolean(), + record_decode := boolean() | map, string_decode := boolean(), spawn_opt := list() | {module(), atom(), list()}}}). diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index 228d9802ad..f7dec2441f 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -76,7 +76,7 @@ service_name :: diameter:service_name(), apps :: [#diameter_app{}], sequence :: diameter:sequence(), - codec :: #{record_decode := boolean(), + codec :: #{record_decode := boolean() | map, string_decode := boolean(), strict_mbit := boolean(), incoming_maxlen := diameter:message_length()}}). @@ -625,6 +625,12 @@ is_answer_message([#diameter_header{is_request = R, is_error = E} | _], _) -> is_answer_message([Name | _], _) -> Name == 'answer-message'; +%% Message sent as a map. +is_answer_message(Map, _) + when is_map(Map) -> + #{':name' := Name} = Map, + Name == 'answer-message'; + %% Message sent as a record. is_answer_message(Rec, Dict) -> try @@ -873,6 +879,11 @@ reset(Msg, [RC | Avps], Dict) -> set([_|_] = Ans, Avps, _) -> Ans ++ Avps; %% Values nearer tail take precedence. +%% ... a map ... +set(Ans, Avps, _) + when is_map(Ans) -> + maps:merge(Ans, maps:from_list(Avps)); + %% ... or record. set(Rec, Avps, Dict) -> Dict:'#set-'(Avps, Rec). @@ -891,6 +902,9 @@ rc([MsgName | _], RC, Dict) -> _ -> [] end; +rc(#{':name' := Name}, RC, Dict) -> + rc([Name], RC, Dict); + rc(Rec, RC, Dict) -> rc([Dict:rec2msg(element(1, Rec))], RC, Dict). @@ -902,34 +916,50 @@ failed_avp(_, [] = No, _) -> failed_avp(Msg, [_|_] = Avps, Dict) -> [failed(Msg, [{'AVP', Avps}], Dict)]. -%% Reply as name and tuple list ... -failed([MsgName | Values], FailedAvp, Dict) -> - RecName = Dict:msg2rec(MsgName), - try - Dict:'#info-'(RecName, {index, 'Failed-AVP'}), - {'Failed-AVP', [FailedAvp]} - catch - error: _ -> - Avps = proplists:get_value('AVP', Values, []), - A = #diameter_avp{name = 'Failed-AVP', - value = FailedAvp}, - {'AVP', [A|Avps]} - end; +%% failed/3 -%% ... or record. -failed(Rec, FailedAvp, Dict) -> +failed(Msg, FailedAvp, Dict) -> + RecName = msg2rec(Msg, Dict), try - RecName = element(1, Rec), - Dict:'#info-'(RecName, {index, 'Failed-AVP'}), + Dict:'#info-'(RecName, {index, 'Failed-AVP'}), %% assert existence {'Failed-AVP', [FailedAvp]} catch error: _ -> - Avps = Dict:'#get-'('AVP', Rec), + Avps = values(Msg, 'AVP', Dict), A = #diameter_avp{name = 'Failed-AVP', value = FailedAvp}, {'AVP', [A|Avps]} end. +%% msg2rec/2 + +%% Message as name/values list ... +msg2rec([MsgName | _], Dict) -> + Dict:msg2rec(MsgName); + +%% ... map ... +msg2rec(#{':name' := MsgName}, Dict) -> + Dict:msg2rec(MsgName); + +%% ... or record. +msg2rec(Rec, _) -> + element(1, Rec). + +%% values/2 + +%% Message as name/values list ... +values([_ | Avps], F, _) -> + proplists:get_value(F, Avps, []); + +%% ... map ... +values(Msg, F, _) + when is_map(Msg) -> + maps:get(F, Msg, []); + +%% ... or record. +values(Rec, F, Dict) -> + Dict:'#get-'(F, Rec). + %% 3. Diameter Header %% %% E(rror) - If set, the message contains a protocol error, @@ -1861,7 +1891,7 @@ str(T) -> get_avp(?RELAY, Name, Msg) -> get_avp(?BASE, Name, Msg); -%% Message as a header/avps list. +%% Message is a header/avps list. get_avp(Dict, Name, [#diameter_header{} | Avps]) -> try {Code, _, VId} = Dict:avp_header(Name), @@ -1874,7 +1904,7 @@ get_avp(Dict, Name, [#diameter_header{} | Avps]) -> undefined end; -%% Outgoing message as a name/values list. +%% Message as name/values list ... get_avp(_, Name, [_MsgName | Avps]) -> case lists:keyfind(Name, 1, Avps) of {_, V} -> @@ -1883,7 +1913,17 @@ get_avp(_, Name, [_MsgName | Avps]) -> undefined end; -%% Message is typically a record but not necessarily. +%% ... map ... +get_avp(_, Name, Map) + when is_map(Map) -> + case maps:find(Name, Map) of + {ok, V} -> + #diameter_avp{name = Name, value = V}; + error -> + undefined + end; + +%% ... or record (but not necessarily). get_avp(Dict, Name, Rec) -> try #diameter_avp{name = Name, value = Dict:'#get-'(Name, Rec)} @@ -1913,7 +1953,8 @@ ungroup(Avp) -> avp_decode(Dict, Name, #diameter_avp{value = undefined, data = Bin} - = Avp) -> + = Avp) + when is_binary(Bin) -> try Dict:avp(decode, Bin, Name, decode_opts(Dict)) of V -> Avp#diameter_avp{value = V} -- cgit v1.2.3 From d52611e9bd0628affa7b4f56a6126e4a99b69a7a Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 6 Jul 2017 12:07:36 +0200 Subject: Let messages and grouped AVPs be decoded to lists That is, decode to the same format that encode already accepts. Only a message has its name at the head of the list since AVPs are already name/value pairs. --- lib/diameter/doc/src/diameter.xml | 3 ++- lib/diameter/src/base/diameter.erl | 2 +- lib/diameter/src/base/diameter_codec.erl | 8 ++++++- lib/diameter/src/base/diameter_config.erl | 4 +++- lib/diameter/src/base/diameter_gen.erl | 35 ++++++++++++++++++++---------- lib/diameter/src/base/diameter_service.erl | 2 +- lib/diameter/src/base/diameter_traffic.erl | 2 +- 7 files changed, 38 insertions(+), 18 deletions(-) diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index bfb6da41b5..e525ab3345 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -970,7 +970,8 @@ occur in the message in question.

-{record_decode, boolean() | map} + +{record_decode, boolean() | list | map}

Whether or not to decode message and grouped AVPs to records in the diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl index 4269c84cd2..b033632c47 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -338,7 +338,7 @@ call(SvcName, App, Message) -> | {restrict_connections, restriction()} | {sequence, sequence() | evaluable()} | {share_peers, remotes()} - | {record_decode, boolean() | map} + | {record_decode, boolean() | list | map} | {string_decode, boolean()} | {strict_mbit, boolean()} | {incoming_maxlen, message_length()} diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index 0c43d52093..9043af145e 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -379,10 +379,16 @@ decode_avps(MsgName, Mod, AppMod, Opts, Pkt, Avps) -> %% ... or not Opts#{dictionary => AppMod, failed_avp => false}), ?LOGC([] /= Errors, decode_errors, Pkt#diameter_packet.header), - Pkt#diameter_packet{msg = Rec, + Pkt#diameter_packet{msg = reformat(MsgName, Rec, Opts), errors = Errors, avps = As}. +reformat(MsgName, Avps, #{decode_format := list}) -> + [MsgName | Avps]; + +reformat(_, Msg, _) -> + Msg. + %%% --------------------------------------------------------------------------- %%% # decode_header/1 %%% --------------------------------------------------------------------------- diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index a790b946c1..8f958a67b4 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -769,7 +769,9 @@ opt(K, true = B) K == string_decode -> B; -opt(record_decode, map = T) -> +opt(record_decode, T) + when T == list; + T == map -> T; opt(restrict_connections, T) diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index be2e221b7e..2381b73d07 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -178,14 +178,17 @@ enc_AVP(_Name, {_Dict, _AvpName, _Data} = T, Opts, _) -> -> {parent_record(), [avp()], Failed} when Failed :: [{5000..5999, #diameter_avp{}}]. -decode_avps(Name, Recs, #{module := Mod} = Opts) -> +decode_avps(Name, Recs, #{module := Mod, record_decode := Fmt} = Opts) -> {Avps, {Rec, AM, Failed}} = mapfoldl(fun(T,A) -> decode(Name, Opts, Mod, T, A) end, - {newrec(Mod, Name, Opts), #{}, []}, + {newrec(Mod, Name, Fmt), #{}, []}, Recs), %% AM counts the number of top-level AVPs, which missing/4 then %% uses when adding 5005 errors. - {Rec, Avps, Failed ++ missing(Name, Opts, Mod, AM)}. + Arities = Mod:avp_arity(Name), + {reformat(Rec, Arities, Fmt), + Avps, + Failed ++ missing(Arities, Opts, Mod, AM)}. %% Append 5005 errors so that errors are reported in the order %% encountered. Failed-AVP should typically contain the first @@ -216,10 +219,10 @@ mapfoldl(_, Acc, [], List) -> %% Vendor-Id if applicable. The value field of the missing AVP %% should be of correct minimum length and contain zeros. -missing(Name, Opts, Mod, AM) -> +missing(Arities, Opts, Mod, AM) -> lists:foldl(fun(T,A) -> missing(T, AM, Opts, Mod, A) end, [], - Mod:avp_arity(Name)). + Arities). %% missing/5 @@ -631,7 +634,7 @@ too_many(FieldName, M, Map) -> %% set/5 -set(_, _, _, _, undefined = No) -> +set(_, _, _, _, false = No) -> No; set(1, F, Value, _, Map) @@ -743,20 +746,28 @@ empty(Name, #{module := Mod} = Opts) -> %% newrec/3 -newrec(_, _, #{record_decode := false}) -> - undefined; +newrec(_, _, false = No) -> + No; -newrec(_, Name, #{record_decode := map}) -> - #{':name' => Name}; +newrec(Mod, Name, true) -> + newrec(Mod, Name); -newrec(Mod, Name, _) -> - newrec(Mod, Name). +newrec(_, Name, _) -> + #{':name' => Name}. %% newrec/2 newrec(Mod, Name) -> Mod:'#new-'(Mod:name2rec(Name)). +%% reformat/3 + +reformat(Map, Arities, list) -> + [{F,V} || {F,_} <- Arities, #{F := V} <- [Map]]; + +reformat(Rec, _, _) -> + Rec. + %% def/1 def(1) -> diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 3e555f6263..7f7e3e3a3f 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -113,7 +113,7 @@ restrict_connections := diameter:restriction(), incoming_maxlen := diameter:message_length(), strict_mbit := boolean(), - record_decode := boolean() | map, + record_decode := boolean() | map | list, string_decode := boolean(), spawn_opt := list() | {module(), atom(), list()}}}). diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index f7dec2441f..6594994cfa 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -76,7 +76,7 @@ service_name :: diameter:service_name(), apps :: [#diameter_app{}], sequence :: diameter:sequence(), - codec :: #{record_decode := boolean() | map, + codec :: #{record_decode := boolean() | map | list, string_decode := boolean(), strict_mbit := boolean(), incoming_maxlen := diameter:message_length()}}). -- cgit v1.2.3 From f0465811faf9085cfbd82b0bc1f91e2f0da07590 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sat, 8 Jul 2017 00:15:08 +0200 Subject: Test map encoding in traffic suite --- lib/diameter/test/diameter_traffic_SUITE.erl | 110 ++++++++++++++++++--------- 1 file changed, 76 insertions(+), 34 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index dd0dfc776d..26bc96490a 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -139,8 +139,8 @@ %% Sequence mask for End-to-End and Hop-by-Hop identifiers. -define(CLIENT_MASK, {1,26}). %% 1 in top 6 bits -%% How to construct messages, as record or list. --define(ENCODINGS, [list, record]). +%% How to construct messages, as record, list, or map. +-define(ENCODINGS, [list, record, map]). %% How to send answers, in a diameter_packet or not. -define(CONTAINERS, [pkt, msg]). @@ -1013,15 +1013,17 @@ origin({A,C}) -> 2*codec(A) + container(C); origin(N) -> - {codec(N band 2), container(N rem 2)}. + {codec(N div 2), container(N rem 2)}. -%% Map booleans, but the readable atoms are part of (constructed) -%% group names, so it's good that they're readable. +%% Map atoms. The atoms are part of (constructed) group names, so it's +%% good that they're readable. codec(record) -> 0; codec(list) -> 1; +codec(map) -> 2; codec(0) -> record; -codec(_) -> list. +codec(1) -> list; +codec(2) -> map. container(pkt) -> 0; container(msg) -> 1; @@ -1032,12 +1034,19 @@ msg([H|_] = Msg, record = E, diameter_gen_base_rfc3588) when H == 'ACR'; H == 'ACA' -> msg(Msg, E, diameter_gen_base_accounting); + msg([H|_] = Msg, record = E, diameter_gen_base_rfc6733) when H == 'ACR'; H == 'ACA' -> msg(Msg, E, diameter_gen_acct_rfc6733); + msg([H|T], record, Dict) -> Dict:'#new-'(Dict:msg2rec(H), T); + +msg([H|T], map, _) -> + Map = maps:from_list(T), + Map#{':name' => H}; + msg(Msg, _, _) -> Msg. @@ -1049,20 +1058,26 @@ appdict(D) -> dict0(D) -> ?A("diameter_gen_base_" ++ ?L(D)). -dict(Msg, nas4005 = D) -> - if 'answer-message' == hd(Msg); - ?is_record(Msg, 'diameter_base_answer-message') -> +dict(Msg, Dict) -> + d(name(Msg), Dict). + +d(N, nas4005 = D) -> + if N == {list, 'answer-message'}; + N == {map, 'answer-message'}; + N == {record, 'diameter_base_answer-message'} -> diameter_gen_base_rfc3588; true -> D end; -dict(Msg, Dict0) - when 'ACR' == hd(Msg); - 'ACA' == hd(Msg); - ?is_record(Msg, diameter_base_accounting_ACR); - ?is_record(Msg, diameter_base_accounting_ACA) -> +d(N, Dict0) + when N == {list, 'ACR'}; + N == {list, 'ACA'}; + N == {map, 'ACR'}; + N == {map, 'ACA'}; + N == {record, diameter_base_accounting_ACR}; + N == {record, diameter_base_accounting_ACA} -> acct(Dict0); -dict(_, Dict0) -> +d(_, Dict0) -> Dict0. acct(diameter_gen_base_rfc3588) -> @@ -1071,21 +1086,28 @@ acct(diameter_gen_base_rfc6733) -> diameter_gen_acct_rfc6733. %% Set only values that aren't already. + set(_, [H|T], Vs) -> [H | Vs ++ T]; + +set(_, Map, Vs) + when is_map(Map) -> + maps:merge(maps:from_list(Vs), Map); + set(#group{client_dict = Dict0} = _Group, Rec, Vs) -> Dict = dict(Rec, Dict0), lists:foldl(fun({F,_} = FV, A) -> - set(Dict, Dict:'#get-'(F, A), FV, A) + reset(Dict, Dict:'#get-'(F, A), FV, A) end, Rec, Vs). -set(Dict, E, FV, Rec) +reset(Dict, E, FV, Rec) when E == undefined; E == [] -> Dict:'#set-'(FV, Rec); -set(_, _, _, Rec) -> + +reset(_, _, _, Rec) -> Rec. %% =========================================================================== @@ -1280,10 +1302,16 @@ prepare(Pkt, Caps, _Name, Group) -> %% prepare/3 -prepare(#diameter_packet{msg = Req}, Caps, Group) - when ?is_record(Req, diameter_base_accounting_ACR); - ?is_record(Req, nas_ACR); - 'ACR' == hd(Req) -> +prepare(#diameter_packet{msg = Req} = Pkt, Caps, Group) -> + set(name(Req), Pkt, Caps, Group). + +%% set/4 + +set(N, #diameter_packet{msg = Req}, Caps, Group) + when N == {record, diameter_base_accounting_ACR}; + N == {record, nas_ACR}; + N == {map, 'ACR'}; + N == {list, 'ACR'} -> #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, DR}} = Caps, @@ -1293,10 +1321,11 @@ prepare(#diameter_packet{msg = Req}, Caps, Group) {'Origin-Realm', OR}, {'Destination-Realm', DR}]); -prepare(#diameter_packet{msg = Req}, Caps, Group) - when ?is_record(Req, diameter_base_ASR); - ?is_record(Req, nas_ASR); - 'ASR' == hd(Req) -> +set(N, #diameter_packet{msg = Req}, Caps, Group) + when N == {record, diameter_base_ASR}; + N == {record, nas_ASR}; + N == {map, 'ASR'}; + N == {list, 'ASR'} -> #diameter_caps{origin_host = {OH, DH}, origin_realm = {OR, DR}} = Caps, @@ -1307,10 +1336,11 @@ prepare(#diameter_packet{msg = Req}, Caps, Group) {'Destination-Realm', DR}, {'Auth-Application-Id', ?APP_ID}]); -prepare(#diameter_packet{msg = Req}, Caps, Group) - when ?is_record(Req, diameter_base_STR); - ?is_record(Req, nas_STR); - 'STR' == hd(Req) -> +set(N, #diameter_packet{msg = Req}, Caps, Group) + when N == {record, diameter_base_STR}; + N == {record, nas_STR}; + N == {map, 'STR'}; + N == {list, 'STR'} -> #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, DR}} = Caps, @@ -1320,10 +1350,11 @@ prepare(#diameter_packet{msg = Req}, Caps, Group) {'Destination-Realm', DR}, {'Auth-Application-Id', ?APP_ID}]); -prepare(#diameter_packet{msg = Req}, Caps, Group) - when ?is_record(Req, diameter_base_RAR); - ?is_record(Req, nas_RAR); - 'RAR' == hd(Req) -> +set(N, #diameter_packet{msg = Req}, Caps, Group) + when N == {record, diameter_base_RAR}; + N == {record, nas_RAR}; + N == {map, 'RAR'}; + N == {list, 'RAR'} -> #diameter_caps{origin_host = {OH, DH}, origin_realm = {OR, DR}} = Caps, @@ -1334,6 +1365,17 @@ prepare(#diameter_packet{msg = Req}, Caps, Group) {'Destination-Realm', DR}, {'Auth-Application-Id', ?APP_ID}]). +%% name/1 + +name([H|_]) -> + {list, H}; + +name(#{} = Map) -> + {map, maps:get(':name', Map)}; + +name(Rec) -> + {record, element(1, Rec)}. + %% prepare_retransmit/5 prepare_retransmit(_Pkt, false, _Peer, _Name, _Group) -> -- cgit v1.2.3 From e7e1cda23e60afd807669ab417f45e7faa62a5b8 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 6 Jul 2017 16:38:03 +0200 Subject: Map answers to maps in traffic suite Instead of to lists, to simplify matching. --- lib/diameter/test/diameter_traffic_SUITE.erl | 146 ++++++++++++++++++--------- 1 file changed, 98 insertions(+), 48 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 26bc96490a..07d1c4937b 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -183,14 +183,16 @@ %% A common match when receiving answers in a client. -define(answer_message(SessionId, ResultCode), - ['answer-message', - {'Session-Id', SessionId}, - {'Origin-Host', _}, - {'Origin-Realm', _}, - {'Result-Code', ResultCode} - | _]). + #{':name' := 'answer-message', + 'Session-Id' := SessionId, + 'Origin-Host' := _, + 'Origin-Realm' := _, + 'Result-Code' := ResultCode}). -define(answer_message(ResultCode), - ?answer_message(_, ResultCode)). + #{':name' := 'answer-message', + 'Origin-Host' := _, + 'Origin-Realm' := _, + 'Result-Code' := ResultCode}). %% Config for diameter:start_service/2. -define(SERVICE(Name, Decode), @@ -567,7 +569,9 @@ send_ok(Config) -> Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD}, {'Accounting-Record-Number', 1}], - ['ACA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _] + #{':name' := 'ACA', + 'Result-Code' := ?SUCCESS, + 'Session-Id' := _} = call(Config, Req). %% Send an accounting ACR that the server answers badly to. @@ -583,7 +587,9 @@ send_eval(Config) -> Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD}, {'Accounting-Record-Number', 3}], - ['ACA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _] + #{':name' := 'ACA', + 'Result-Code' := ?SUCCESS, + 'Session-Id' := _} = call(Config, Req). %% Send an accounting ACR that the server tries to answer with an @@ -609,7 +615,8 @@ send_protocol_error(Config) -> send_experimental_result(Config) -> Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD}, {'Accounting-Record-Number', 5}], - ['ACA', {'Session-Id', _} | _] + #{':name' := 'ACA', + 'Session-Id' := _} = call(Config, Req). %% Send an ASR with an arbitrary non-mandatory AVP and expect success @@ -617,11 +624,12 @@ send_experimental_result(Config) -> send_arbitrary(Config) -> Req = ['ASR', {'AVP', [#diameter_avp{name = 'Product-Name', value = "XXX"}]}], - ['ASA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | Avps] + #{':name' := 'ASA', + 'Session-Id' := _, + 'Result-Code' := ?SUCCESS, + 'AVP' := [#diameter_avp{name = 'Product-Name', + value = V}]} = call(Config, Req), - {'AVP', [#diameter_avp{name = 'Product-Name', - value = V}]} - = lists:last(Avps), "XXX" = string(V, Config). %% Send an unknown AVP (to some client) and check that it comes back. @@ -629,12 +637,13 @@ send_unknown(Config) -> Req = ['ASR', {'AVP', [#diameter_avp{code = 999, is_mandatory = false, data = <<17>>}]}], - ['ASA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | Avps] - = call(Config, Req), - {'AVP', [#diameter_avp{code = 999, - is_mandatory = false, - data = <<17>>}]} - = lists:last(Avps). + #{':name' := 'ASA', + 'Session-Id' := _, + 'Result-Code' := ?SUCCESS, + 'AVP' := [#diameter_avp{code = 999, + is_mandatory = false, + data = <<17>>}]} + = call(Config, Req). %% Ditto, and point the AVP length past the end of the message. Expect %% 5014. @@ -645,7 +654,10 @@ send_unknown_short(Config, M, RC) -> Req = ['ASR', {'AVP', [#diameter_avp{code = 999, is_mandatory = M, data = <<17>>}]}], - ['ASA', {'Session-Id', _}, {'Result-Code', RC} | Avps] + #{':name' := 'ASA', + 'Session-Id' := _, + 'Result-Code' := RC, + 'Failed-AVP' := Avps} = call(Config, Req), [[#diameter_avp{code = 999, is_mandatory = M, @@ -657,7 +669,10 @@ send_unknown_mandatory(Config) -> Req = ['ASR', {'AVP', [#diameter_avp{code = 999, is_mandatory = true, data = <<17>>}]}], - ['ASA', {'Session-Id', _}, {'Result-Code', ?AVP_UNSUPPORTED} | Avps] + #{':name' := 'ASA', + 'Session-Id' := _, + 'Result-Code' := ?AVP_UNSUPPORTED, + 'Failed-AVP' := Avps} = call(Config, Req), [[#diameter_avp{code = 999, is_mandatory = true, @@ -675,7 +690,10 @@ send_unexpected_mandatory_decode(Config) -> Req = ['ASR', {'AVP', [#diameter_avp{code = 27, %% Session-Timeout is_mandatory = true, data = <<12:32>>}]}], - ['ASA', {'Session-Id', _}, {'Result-Code', ?AVP_UNSUPPORTED} | Avps] + #{':name' := 'ASA', + 'Session-Id' := _, + 'Result-Code' := ?AVP_UNSUPPORTED, + 'Failed-AVP' := Avps} = call(Config, Req), [[#diameter_avp{code = 27, is_mandatory = true, @@ -691,7 +709,10 @@ send_unexpected_mandatory_decode(Config) -> send_grouped_error(Config) -> Req = ['ASR', {'Proxy-Info', [[{'Proxy-Host', "abcd"}, {'Proxy-State', ""}]]}], - ['ASA', {'Session-Id', _}, {'Result-Code', ?INVALID_AVP_LENGTH} | Avps] + #{':name' := 'ASA', + 'Session-Id' := _, + 'Result-Code' := ?INVALID_AVP_LENGTH, + 'Failed-AVP' := Avps} = call(Config, Req), [[#diameter_avp{name = 'Proxy-Info', value = V}]] = failed_avps(Avps, Config), @@ -724,7 +745,9 @@ send_error_bit(Config) -> %% Send a bad version and check that we get 5011. send_unsupported_version(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], - ['STA', {'Session-Id', _}, {'Result-Code', ?UNSUPPORTED_VERSION} | _] + #{':name' := 'STA', + 'Session-Id' := _, + 'Result-Code' := ?UNSUPPORTED_VERSION} = call(Config, Req). %% Send a request containing an AVP length > data size. @@ -744,15 +767,16 @@ send_zero_avp_length(Config) -> send_invalid_avp_length(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], - ['STA', {'Session-Id', _}, - {'Result-Code', ?INVALID_AVP_LENGTH}, - {'Origin-Host', _}, - {'Origin-Realm', _}, - {'User-Name', _}, - {'Class', _}, - {'Error-Message', _}, - {'Error-Reporting-Host', _} - | Avps] + #{':name' := 'STA', + 'Session-Id' := _, + 'Result-Code' := ?INVALID_AVP_LENGTH, + 'Origin-Host' := _, + 'Origin-Realm' := _, + 'User-Name' := _, + 'Class' := _, + 'Error-Message' := _, + 'Error-Reporting-Host' := _, + 'Failed-AVP' := Avps} = call(Config, Req), [[_]] = failed_avps(Avps, Config). @@ -769,14 +793,18 @@ send_invalid_reject(Config) -> send_unexpected_mandatory(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], - ['STA', {'Session-Id', _}, {'Result-Code', ?AVP_UNSUPPORTED} | _] + #{':name' := 'STA', + 'Session-Id' := _, + 'Result-Code' := ?AVP_UNSUPPORTED} = call(Config, Req). %% Send something long that will be fragmented by TCP. send_long(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}, {'User-Name', [binary:copy(<<$X>>, 1 bsl 20)]}], - ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _] + #{':name' := 'STA', + 'Session-Id' := _, + 'Result-Code' := ?SUCCESS} = call(Config, Req). %% Send something longer than the configure incoming_maxlen. @@ -819,7 +847,9 @@ send_any_2(Config) -> send_all_1(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], Realm = lists:foldr(fun(C,A) -> [C,A] end, [], ?REALM), - ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _] + #{':name' := 'STA', + 'Session-Id' := _, + 'Result-Code' := ?SUCCESS} = call(Config, Req, [{filter, {all, [{host, any}, {realm, Realm}]}}]). send_all_2(Config) -> @@ -848,9 +878,10 @@ send_detach(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], Ref = make_ref(), ok = call(Config, Req, [{extra, [{self(), Ref}]}, detach]), - Ans = receive {Ref, T} -> T end, - ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _] - = Ans. + #{':name' := 'STA', + 'Session-Id' := _, + 'Result-Code' := ?SUCCESS} + = receive {Ref, T} -> T end. %% Send a request which can't be encoded and expect {error, encode}. send_encode_error(Config) -> @@ -862,11 +893,15 @@ send_destination_1(Config) -> = group(Config), Req = ['STR', {'Termination-Cause', ?LOGOUT}, {'Destination-Host', [?HOST(SN, ?REALM)]}], - ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _] + #{':name' := 'STA', + 'Session-Id' := _, + 'Result-Code' := ?SUCCESS} = call(Config, Req, [{filter, {all, [host, realm]}}]). send_destination_2(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], - ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _] + #{':name' := 'STA', + 'Session-Id' := _, + 'Result-Code' := ?SUCCESS} = call(Config, Req, [{filter, {all, [host, realm]}}]). %% Send with filtering on and expect failure when specifying an @@ -930,7 +965,9 @@ send_bad_filter(Config, F) -> %% Specify multiple filter options and expect them be conjunctive. send_multiple_filters_1(Config) -> Fun = fun(#diameter_caps{}) -> true end, - ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _] + #{':name' := 'STA', + 'Session-Id' := _, + 'Result-Code' := ?SUCCESS} = send_multiple_filters(Config, [host, {eval, Fun}]). send_multiple_filters_2(Config) -> E = {erlang, is_tuple, []}, @@ -941,7 +978,9 @@ send_multiple_filters_3(Config) -> E2 = {erlang, is_tuple, []}, E3 = {erlang, is_record, [diameter_caps]}, E4 = [{erlang, is_record, []}, diameter_caps], - ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _] + #{':name' := 'STA', + 'Session-Id' := _, + 'Result-Code' := ?SUCCESS} = send_multiple_filters(Config, [{eval, E} || E <- [E1,E2,E3,E4]]). send_multiple_filters(Config, Fs) -> @@ -952,14 +991,16 @@ send_multiple_filters(Config, Fs) -> %% only the return value from the prepare_request callback being %% significant. send_anything(Config) -> - ['STA', {'Session-Id', _}, {'Result-Code', ?SUCCESS} | _] + #{':name' := 'STA', + 'Session-Id' := _, + 'Result-Code' := ?SUCCESS} = call(Config, anything). %% =========================================================================== failed_avps(Avps, Config) -> #group{client_dict = D} = proplists:get_value(group, Config), - [failed_avp(D, T) || T <- proplists:get_value('Failed-AVP', Avps)]. + [failed_avp(D, T) || T <- Avps]. failed_avp(nas4005, {'nas_Failed-AVP', As}) -> As; @@ -1374,7 +1415,12 @@ name(#{} = Map) -> {map, maps:get(':name', Map)}; name(Rec) -> - {record, element(1, Rec)}. + try + {record, element(1, Rec)} + catch + error: badarg -> + false + end. %% prepare_retransmit/5 @@ -1396,7 +1442,11 @@ answer(Pkt, Req, _Peer, Name, #group{client_dict = Dict0}) -> #diameter_header{application_id = ApplId} = H, %% assert Dict = dict(Ans, Dict0), [R | Vs] = Dict:'#get-'(answer(Ans, Es, Name)), - [Dict:rec2msg(R) | Vs]. + maps:put(':name', + Dict:rec2msg(R), + maps:from_list([T || {_,V} = T <- Vs, + V /= undefined, + V /= []])). %% Missing Result-Code and inappropriate Experimental-Result-Code. answer(Rec, Es, send_experimental_result) -> -- cgit v1.2.3 From f87aeb3fb2c84790b37796d5e57b4a09ac2f4ed7 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 6 Jul 2017 17:55:48 +0200 Subject: Test record_decode in traffic suite --- lib/diameter/test/diameter_traffic_SUITE.erl | 237 ++++++++++++++++----------- 1 file changed, 140 insertions(+), 97 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 07d1c4937b..47f83676bb 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -139,9 +139,12 @@ %% Sequence mask for End-to-End and Hop-by-Hop identifiers. -define(CLIENT_MASK, {1,26}). %% 1 in top 6 bits -%% How to construct messages, as record, list, or map. +%% How to construct outgoing messages. -define(ENCODINGS, [list, record, map]). +%% How to set record_decode. +-define(DECODINGS, [true, false, map, list]). + %% How to send answers, in a diameter_packet or not. -define(CONTAINERS, [pkt, msg]). @@ -169,6 +172,7 @@ client_dict, client_sender, server_service, + server_decoding, server_encoding, server_container, server_sender, @@ -264,25 +268,27 @@ all() -> groups() -> [{P, [P], Ts} || Ts <- [tc(tc())], P <- [shuffle, parallel]] ++ - [{?util:name([T,R,D,A,C,S,SS,ST,CS]), + [{?util:name([T,CE,D,SE,SD,SC,S,SS,ST,CS]), [], [{group, if S -> shuffle; not S -> parallel end}]} - || T <- ?TRANSPORTS, - R <- ?ENCODINGS, - D <- ?RFCS, - A <- ?ENCODINGS, - C <- ?CONTAINERS, - S <- ?STRING_DECODES, + || T <- ?TRANSPORTS, + CE <- ?ENCODINGS, + D <- ?RFCS, + SE <- ?ENCODINGS, + SD <- ?DECODINGS, + SC <- ?CONTAINERS, + S <- ?STRING_DECODES, SS <- ?SENDERS, ST <- ?CALLBACKS, CS <- ?SENDERS] ++ - [{T, [], groups([[T,R,D,A,C,S,SS,ST,CS] - || R <- ?ENCODINGS, - D <- ?RFCS, - A <- ?ENCODINGS, - C <- ?CONTAINERS, - S <- ?STRING_DECODES, + [{T, [], groups([[T,CE,D,SE,SD,SC,S,SS,ST,CS] + || CE <- ?ENCODINGS, + D <- ?RFCS, + SE <- ?ENCODINGS, + SD <- ?DECODINGS, + SC <- ?CONTAINERS, + S <- ?STRING_DECODES, SS <- ?SENDERS, ST <- ?CALLBACKS, CS <- ?SENDERS, @@ -292,7 +298,7 @@ groups() -> [{traffic, [], [{group, T} || T <- ?TRANSPORTS]}]. %groups(_) -> %% debug -% Name = [sctp,record,rfc6733,record,pkt,false,false,false,false], +% Name = [tcp,record,rfc6733,record,map,pkt,false,false,false,false], % [{group, ?util:name(Name)}]; groups(Names) -> [{group, ?util:name(L)} || L <- Names]. @@ -331,18 +337,19 @@ init_per_group(sctp = Name, Config) -> init_per_group(Name, Config) -> Nas = proplists:get_value(rfc4005, Config, false), case ?util:name(Name) of - [_,_,D,_,_,_, _, _, _] when D == rfc4005, true /= Nas -> + [_,_,D,_,_,_,_,_,_,_] when D == rfc4005, true /= Nas -> {skip, rfc4005}; - [T,R,D,A,C,S,SS,ST,CS] -> + [T,CE,D,SE,SD,SC,S,SS,ST,CS] -> G = #group{transport = T, strings = S, client_service = [$C|?util:unique_string()], - client_encoding = R, + client_encoding = CE, client_dict = appdict(D), client_sender = CS, server_service = [$S|?util:unique_string()], - server_encoding = A, - server_container = C, + server_decoding = SD, + server_encoding = SE, + server_container = SC, server_sender = SS, server_throttle = ST}, %% Limit the number of testcase, since the number of @@ -452,9 +459,11 @@ start(_Config) -> start_services(Config) -> #group{strings = S, client_service = CN, - server_service = SN} + server_service = SN, + server_decoding = SD} = group(Config), - ok = diameter:start_service(SN, ?SERVICE(SN, S)), + ok = diameter:start_service(SN, [{record_decode, SD} + | ?SERVICE(SN, S)]), ok = diameter:start_service(CN, [{sequence, ?CLIENT_MASK} | ?SERVICE(CN, S)]). @@ -480,12 +489,13 @@ add_transports(Config) -> [T, {sender, CS}], LRef, [{id, Id} - | client_apps(D, [{'Origin-State-Id', origin(Id)}])]) - || A <- ?ENCODINGS, + | client_apps(R, [{'Origin-State-Id', origin(Id)}])]) + || D <- ?DECODINGS, + E <- ?ENCODINGS, C <- ?CONTAINERS, - D <- ?RFCS, - D /= rfc4005 orelse have_nas(), - Id <- [{A,C}]], + R <- ?RFCS, + R /= rfc4005 orelse have_nas(), + Id <- [{D,E,C}]], %% The server uses the client's Origin-State-Id to decide how to %% answer. ?util:write_priv(Config, "transport", [LRef | Cs]). @@ -772,10 +782,6 @@ send_invalid_avp_length(Config) -> 'Result-Code' := ?INVALID_AVP_LENGTH, 'Origin-Host' := _, 'Origin-Realm' := _, - 'User-Name' := _, - 'Class' := _, - 'Error-Message' := _, - 'Error-Reporting-Host' := _, 'Failed-AVP' := Avps} = call(Config, Req), [[_]] = failed_avps(Avps, Config). @@ -1050,21 +1056,30 @@ call(Config, Req, Opts) -> msg(Req, ReqEncoding, Dict0), [{extra, [{Name, Group}, diameter_lib:now()]} | Opts]). -origin({A,C}) -> - 2*codec(A) + container(C); +origin({D,E,C}) -> + 8*decode(D) + 2*encode(E) + container(C); origin(N) -> - {codec(N div 2), container(N rem 2)}. + {decode(N bsr 3), encode((N bsr 1) rem 4), container(N rem 2)}. %% Map atoms. The atoms are part of (constructed) group names, so it's %% good that they're readable. -codec(record) -> 0; -codec(list) -> 1; -codec(map) -> 2; -codec(0) -> record; -codec(1) -> list; -codec(2) -> map. +decode(true) -> 0; %% record +decode(list) -> 1; +decode(map) -> 2; +decode(false) -> 3; +decode(0) -> true; +decode(1) -> list; +decode(2) -> map; +decode(3) -> false. + +encode(record) -> 0; +encode(list) -> 1; +encode(map) -> 2; +encode(0) -> record; +encode(1) -> list; +encode(2) -> map. container(pkt) -> 0; container(msg) -> 1; @@ -1091,6 +1106,46 @@ msg([H|T], map, _) -> msg(Msg, _, _) -> Msg. +to_map(map, #diameter_packet{msg = Msg}) + when is_map(Msg) -> + Msg; + +to_map(list, #diameter_packet{msg = [MsgName | Avps]}) -> + maps:put(':name', MsgName, maps:from_list(Avps)); + +to_map(true, #diameter_packet{header = H, msg = Rec}) -> + rec_to_map(Rec, dict(H)); + +%% No record decode: do it ourselves. +to_map(false, #diameter_packet{header = H, + msg = false, + bin = Bin}) -> + Opts = #{record_decode => map, + string_decode => false, + strict_mbit => true, + rfc => 6733}, + #diameter_packet{msg = Msg} + = diameter_codec:decode(dict(H), Opts, Bin), + Msg. + +dict(#diameter_header{application_id = Id, + cmd_code = Code}) -> + if Id == 1 -> + nas4005; + Code == 271 -> + diameter_gen_base_accounting; + true -> + diameter_gen_base_rfc3588 + end. + +rec_to_map(Rec, Dict) -> + [R | Vs] = Dict:'#get-'(Rec), + maps:put(':name', + Dict:rec2msg(R), + maps:from_list([T || {_,V} = T <- Vs, + V /= undefined, + V /= []])). + appdict(rfc4005) -> nas4005; appdict(D) -> @@ -1177,10 +1232,11 @@ pick_peer(Peers, _, [$C|_], _State, {send_detach, Group}, _, {_,_}) -> find(Group, Peers). find(#group{client_service = CN, - server_encoding = A, + server_decoding = D, + server_encoding = E, server_container = C}, [_|_] = Peers) -> - Id = {A,C}, + Id = {D,E,C}, [P] = [P || P <- Peers, id(Id, P, CN)], {ok, P}. @@ -1441,12 +1497,7 @@ answer(Pkt, Req, _Peer, Name, #group{client_dict = Dict0}) -> ApplId = app(Req, Name, Dict0), #diameter_header{application_id = ApplId} = H, %% assert Dict = dict(Ans, Dict0), - [R | Vs] = Dict:'#get-'(answer(Ans, Es, Name)), - maps:put(':name', - Dict:rec2msg(R), - maps:from_list([T || {_,V} = T <- Vs, - V /= undefined, - V /= []])). + rec_to_map(answer(Ans, Es, Name), Dict). %% Missing Result-Code and inappropriate Experimental-Result-Code. answer(Rec, Es, send_experimental_result) -> @@ -1486,7 +1537,8 @@ handle_error(Reason, _Req, [$C|_], _Peer, _, _Time) -> %% Note that diameter will set Result-Code and Failed-AVPs if %% #diameter_packet.errors is non-null. -handle_request(#diameter_packet{header = H, msg = M, avps = As}, +handle_request(#diameter_packet{header = H, avps = As} + = Pkt, _, {_Ref, Caps}) -> #diameter_header{end_to_end_id = EI, @@ -1496,14 +1548,15 @@ handle_request(#diameter_packet{header = H, msg = M, avps = As}, V = EI bsr B, %% assert V = HI bsr B, %% #diameter_caps{origin_state_id = {_,[Id]}} = Caps, - wrap(origin(Id), H, request(nas_to_base(M, H), [H|As], Caps)). + {D,_,_} = T = origin(Id), + wrap(T, H, request(to_map(D, Pkt), [H|As], Caps)). -wrap(T, H, {Tag, Action, Post}) -> - {Tag, wrap(T, H, Action), Post}; +wrap(Id, H, {Tag, Action, Post}) -> + {Tag, wrap(Id, H, Action), Post}; wrap(_, _, {reply, [#diameter_header{} | _]} = T) -> T; -wrap({A,C}, H, {reply, Ans}) -> - Msg = msg(Ans, A, diameter_gen_base_rfc3588), +wrap({_,E,C}, H, {reply, Ans}) -> + Msg = msg(Ans, E, diameter_gen_base_rfc3588), wrap(C, H, {reply, base_to_nas(Msg, H)}); wrap(pkt, _, {reply, Ans}) when not is_record(Ans, diameter_packet) -> @@ -1511,28 +1564,6 @@ wrap(pkt, _, {reply, Ans}) wrap(_, _, T) -> T. -%% nas_to_base/1 -%% -%% Map an RFC 4005 message to RFC 3588, to return the same answer in -%% both cases. - -nas_to_base(Rec, #diameter_header{application_id = 1}) -> - [R | Values] = nas4005:'#get-'(Rec), - "nas_" ++ Name = ?L(R), - {D, RN} = case Name of - "ACR" -> - {diameter_gen_base_accounting, - diameter_base_accounting_ACR}; - _ -> - {diameter_gen_base_rfc3588, - ?A("diameter_base_" ++ Name)} - end, - Fs = D:'#info-'(RN), - D:'#new-'([RN | [T || {F,_} = T <- Values, lists:member(F, Fs)]]); - -nas_to_base(Rec, _) -> - Rec. - %% base_to_nas/2 base_to_nas(#diameter_packet{msg = Msg} = Pkt, H) -> @@ -1561,7 +1592,8 @@ base_to_nas(Msg, _) -> %% request/3 %% send_experimental_result -request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 5}, +request(#{':name' := 'ACR', + 'Accounting-Record-Number' := 5}, [Hdr | Avps], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> @@ -1594,14 +1626,16 @@ request(Msg, _Avps, Caps) -> %% request/2 %% send_nok -request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 0}, +request(#{':name' := 'ACR', + 'Accounting-Record-Number' := 0}, _) -> {eval_packet, {protocol_error, ?INVALID_AVP_BITS}, [fun log/2, invalid]}; %% send_bad_answer -request(#diameter_base_accounting_ACR{'Session-Id' = SId, - 'Accounting-Record-Type' = RT, - 'Accounting-Record-Number' = 2 = RN}, +request(#{':name' := 'ACR', + 'Session-Id' := SId, + 'Accounting-Record-Type' := RT, + 'Accounting-Record-Number' := 2 = RN}, #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> Ans = ['ACA', {'Result-Code', ?SUCCESS}, @@ -1615,9 +1649,10 @@ request(#diameter_base_accounting_ACR{'Session-Id' = SId, msg = Ans}}; %% send_eval -request(#diameter_base_accounting_ACR{'Session-Id' = SId, - 'Accounting-Record-Type' = RT, - 'Accounting-Record-Number' = 3 = RN}, +request(#{':name' := 'ACR', + 'Session-Id' := SId, + 'Accounting-Record-Type' := RT, + 'Accounting-Record-Number' := 3 = RN}, #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> Ans = ['ACA', {'Result-Code', ?SUCCESS}, @@ -1629,9 +1664,10 @@ request(#diameter_base_accounting_ACR{'Session-Id' = SId, {eval, {reply, Ans}, {erlang, now, []}}; %% send_ok -request(#diameter_base_accounting_ACR{'Session-Id' = SId, - 'Accounting-Record-Type' = RT, - 'Accounting-Record-Number' = 1 = RN}, +request(#{':name' := 'ACR', + 'Session-Id' := SId, + 'Accounting-Record-Type' := RT, + 'Accounting-Record-Number' := 1 = RN}, #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> {reply, ['ACA', {'Result-Code', ?SUCCESS}, @@ -1642,7 +1678,8 @@ request(#diameter_base_accounting_ACR{'Session-Id' = SId, {'Accounting-Record-Number', RN}]}; %% send_protocol_error -request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 4}, +request(#{':name' := 'ACR', + 'Accounting-Record-Number' := 4}, #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> Ans = ['answer-message', {'Result-Code', ?TOO_BUSY}, @@ -1650,40 +1687,46 @@ request(#diameter_base_accounting_ACR{'Accounting-Record-Number' = 4}, {'Origin-Realm', OR}], {reply, Ans}; -request(#diameter_base_ASR{'Session-Id' = SId, - 'AVP' = Avps}, +request(#{':name' := 'ASR', + 'Session-Id' := SId} + = Req, #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> {reply, ['ASA', {'Result-Code', ?SUCCESS}, {'Session-Id', SId}, {'Origin-Host', OH}, {'Origin-Realm', OR}, - {'AVP', Avps}]}; + {'AVP', maps:get('AVP', Req, [])}]}; %% send_invalid_reject -request(#diameter_base_STR{'Termination-Cause' = ?USER_MOVED}, +request(#{':name' := 'STR', + 'Termination-Cause' := ?USER_MOVED}, _Caps) -> {protocol_error, ?TOO_BUSY}; %% send_noreply -request(#diameter_base_STR{'Termination-Cause' = T}, +request(#{':name' := 'STR', + 'Termination-Cause' := T}, _Caps) when T /= ?LOGOUT -> discard; %% send_destination_5 -request(#diameter_base_STR{'Destination-Realm' = R}, +request(#{':name' := 'STR', + 'Destination-Realm' := R}, #diameter_caps{origin_realm = {OR, _}}) when R /= undefined, R /= OR -> {protocol_error, ?REALM_NOT_SERVED}; %% send_destination_6 -request(#diameter_base_STR{'Destination-Host' = [H]}, +request(#{':name' := 'STR', + 'Destination-Host' := [H]}, #diameter_caps{origin_host = {OH, _}}) when H /= OH -> {protocol_error, ?UNABLE_TO_DELIVER}; -request(#diameter_base_STR{'Session-Id' = SId}, +request(#{':name' := 'STR', + 'Session-Id' := SId}, #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> {reply, ['STA', {'Result-Code', ?SUCCESS}, @@ -1692,7 +1735,7 @@ request(#diameter_base_STR{'Session-Id' = SId}, {'Origin-Realm', OR}]}; %% send_error/send_timeout -request(#diameter_base_RAR{}, _Caps) -> +request(#{':name' := 'RAR'}, _Caps) -> receive after 2000 -> {protocol_error, ?TOO_BUSY} end. %% message/3 -- cgit v1.2.3 From df12f634bec4b784f4c8d16846f2c24297b0e1ac Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Fri, 7 Jul 2017 23:58:19 +0200 Subject: Create fewer client connections in traffic suite One for each server decoding/encoding/container combination is overkill. Just want a few from which one can be chosen in the pick_peer callback. --- lib/diameter/test/diameter_traffic_SUITE.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 47f83676bb..adf8bf4d66 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -472,6 +472,8 @@ add_transports(Config) -> client_service = CN, client_sender = CS, server_service = SN, + server_encoding = SE, + server_container = SC, server_sender = SS, server_throttle = ST} = group(Config), @@ -490,12 +492,10 @@ add_transports(Config) -> LRef, [{id, Id} | client_apps(R, [{'Origin-State-Id', origin(Id)}])]) - || D <- ?DECODINGS, - E <- ?ENCODINGS, - C <- ?CONTAINERS, + || D <- ?DECODINGS, %% for multiple candidate peers R <- ?RFCS, R /= rfc4005 orelse have_nas(), - Id <- [{D,E,C}]], + Id <- [{D,SE,SC}]], %% The server uses the client's Origin-State-Id to decide how to %% answer. ?util:write_priv(Config, "transport", [LRef | Cs]). -- cgit v1.2.3 From 55e65b262cdf0b794ab443928676720a323cf6b0 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sat, 8 Jul 2017 00:48:07 +0200 Subject: Rename record_decode -> decode_format {record_decode, map} is a bit too quirky. --- lib/diameter/doc/src/diameter.xml | 48 +++++++++++----------- lib/diameter/doc/src/diameter_codec.xml | 2 +- lib/diameter/src/base/diameter.erl | 6 ++- lib/diameter/src/base/diameter_codec.erl | 2 +- lib/diameter/src/base/diameter_config.erl | 10 ++--- lib/diameter/src/base/diameter_gen.erl | 4 +- lib/diameter/src/base/diameter_peer_fsm.erl | 6 +-- lib/diameter/src/base/diameter_service.erl | 2 +- lib/diameter/src/base/diameter_traffic.erl | 6 +-- lib/diameter/src/base/diameter_watchdog.erl | 6 +-- lib/diameter/test/diameter_codec_SUITE.erl | 2 +- .../diameter_test_unknown.erl | 2 +- lib/diameter/test/diameter_codec_test.erl | 2 +- lib/diameter/test/diameter_traffic_SUITE.erl | 14 +++---- 14 files changed, 57 insertions(+), 55 deletions(-) diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index e525ab3345..a7f001e096 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -797,6 +797,29 @@ be matched by corresponding &capability; configuration, of + + +{decode_format, record | list | map | false} + +

+The type of decoded messages and grouped AVPs in the msg field +of diameter_packet records and value field of diameter_avp +records respectively. +If false then the fields are set this value. +See also &codec_message;.

+ +

+Defaults to record.

+ + +

+AVPs are decoded into a list of diameter_avp records in avps +field of diameter_packet records independently of +decode_format.

+
+ +
+ {incoming_maxlen, 0..16777215} @@ -969,31 +992,6 @@ occur in the message in question.

- - -{record_decode, boolean() | list | map} - -

-Whether or not to decode message and grouped AVPs to records in the -msg field of diameter_packet records and value field of -diameter_avp records respectively, or to an alternate format. -If false then the fields are set to the same value. -See also &codec_message;.

- -

-Defaults to true.

- - -

-Disabling the record is useful for applications in which the records -aren't used/needed. -AVP values are available in the avps field of -diameter_packet records regardless of whether or not there is a record -decode.

-
- -
- {string_decode, boolean()} diff --git a/lib/diameter/doc/src/diameter_codec.xml b/lib/diameter/doc/src/diameter_codec.xml index 6262dbf00d..4df8e788b7 100644 --- a/lib/diameter/doc/src/diameter_codec.xml +++ b/lib/diameter/doc/src/diameter_codec.xml @@ -244,7 +244,7 @@ file) and whose tail is a list of {AvpName, AvpValues} pairs, or as a map with values keyed on AVP names and the message name in key :name. The format at decode is determined by &mod_service_opt; -record_decode. +decode_format. Any of the formats is accepted at encode.

diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl index b033632c47..75deaad511 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -47,6 +47,7 @@ stop/0]). -export_type([evaluable/0, + decode_format/0, restriction/0, message_length/0, remotes/0, @@ -330,6 +331,9 @@ call(SvcName, App, Message) -> -type message_length() :: 0..16#FFFFFF. +-type decode_format() + :: record | list | map | false. + %% Options passed to start_service/2 -type service_opt() @@ -338,7 +342,7 @@ call(SvcName, App, Message) -> | {restrict_connections, restriction()} | {sequence, sequence() | evaluable()} | {share_peers, remotes()} - | {record_decode, boolean() | list | map} + | {decode_format, decode_format()} | {string_decode, boolean()} | {strict_mbit, boolean()} | {incoming_maxlen, message_length()} diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index 9043af145e..275e80b9bb 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -290,7 +290,7 @@ rec2msg(Mod, Rec) -> %% longer *the* decode. decode(Mod, Pkt) -> - Opts = #{record_decode => true, + Opts = #{decode_format => record, string_decode => true, strict_mbit => true, rfc => 6733}, diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index 8f958a67b4..09018308d5 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -713,7 +713,7 @@ make_config(SvcName, Opts) -> {nodes, restrict_connections}, {16#FFFFFF, incoming_maxlen}, {true, strict_mbit}, - {true, record_decode}, + {record, decode_format}, {true, string_decode}, {[], spawn_opt}]), @@ -757,7 +757,7 @@ opt(K, false = B) K == monitor; K == restrict_connections; K == strict_mbit; - K == record_decode; + K == decode_format; K == string_decode -> B; @@ -765,12 +765,12 @@ opt(K, true = B) when K == share_peers; K == use_shared_peers; K == strict_mbit; - K == record_decode; K == string_decode -> B; -opt(record_decode, T) - when T == list; +opt(decode_format, T) + when T == record; + T == list; T == map -> T; diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index 2381b73d07..239d4a535f 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -178,7 +178,7 @@ enc_AVP(_Name, {_Dict, _AvpName, _Data} = T, Opts, _) -> -> {parent_record(), [avp()], Failed} when Failed :: [{5000..5999, #diameter_avp{}}]. -decode_avps(Name, Recs, #{module := Mod, record_decode := Fmt} = Opts) -> +decode_avps(Name, Recs, #{module := Mod, decode_format := Fmt} = Opts) -> {Avps, {Rec, AM, Failed}} = mapfoldl(fun(T,A) -> decode(Name, Opts, Mod, T, A) end, {newrec(Mod, Name, Fmt), #{}, []}, @@ -749,7 +749,7 @@ empty(Name, #{module := Mod} = Opts) -> newrec(_, _, false = No) -> No; -newrec(Mod, Name, true) -> +newrec(Mod, Name, record) -> newrec(Mod, Name); newrec(_, Name, _) -> diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index f2fbb70270..abf948593f 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -128,7 +128,7 @@ %% outgoing DPR; boolean says whether or not %% the request was sent explicitly with %% diameter:call/4. - codec :: #{record_decode := true, + codec :: #{decode_format := record, string_decode := boolean(), strict_mbit := boolean(), rfc := 3588 | 6733, @@ -254,13 +254,13 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) -> length_errors = LengthErr, strict = Strictness, incoming_maxlen = Maxlen, - codec = maps:with([record_decode, + codec = maps:with([decode_format, string_decode, strict_mbit, rfc, ordered_encode], SvcOpts#{ordered_encode => false, - record_decode => true})}. + decode_format => record})}. %% The transport returns its local ip addresses so that different %% transports on the same service can use different local addresses. %% The local addresses are put into Host-IP-Address avps here when diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 7f7e3e3a3f..43be4d889a 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -113,7 +113,7 @@ restrict_connections := diameter:restriction(), incoming_maxlen := diameter:message_length(), strict_mbit := boolean(), - record_decode := boolean() | map | list, + decode_format := diameter:decode_format(), string_decode := boolean(), spawn_opt := list() | {module(), atom(), list()}}}). diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index 6594994cfa..f684f60cb7 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -76,7 +76,7 @@ service_name :: diameter:service_name(), apps :: [#diameter_app{}], sequence :: diameter:sequence(), - codec :: #{record_decode := boolean() | map | list, + codec :: #{decode_format := diameter:decode_format(), string_decode := boolean(), strict_mbit := boolean(), incoming_maxlen := diameter:message_length()}}). @@ -103,7 +103,7 @@ make_recvdata([SvcName, PeerT, Apps, SvcOpts | _]) -> peerT = PeerT, apps = Apps, sequence = Mask, - codec = maps:with([record_decode, + codec = maps:with([decode_format, string_decode, strict_mbit, ordered_encode, @@ -1976,7 +1976,7 @@ choose(false, _, X) -> X. %% Decode options sufficient for AVP extraction. decode_opts(Dict) -> - #{record_decode => true, + #{decode_format => record, string_decode => false, strict_mbit => false, failed_avp => false, diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl index c3dc8c3bf0..60baf1e8a4 100644 --- a/lib/diameter/src/base/diameter_watchdog.erl +++ b/lib/diameter/src/base/diameter_watchdog.erl @@ -72,7 +72,7 @@ restrict := boolean(), suspect := non_neg_integer(), %% OKAY -> SUSPECT okay := non_neg_integer()}, %% REOPEN -> OKAY - codec :: #{record_decode := false, + codec :: #{decode_format := false, string_decode := false, strict_mbit := boolean(), failed_avp := false, @@ -136,7 +136,7 @@ i({Ack, T, Pid, {Opts, putr(restart, {T, Opts, Svc, SvcOpts}), %% save seeing it in trace putr(dwr, dwr(Caps)), %% Nodes = restrict_nodes(Restrict), - CodecKeys = [record_decode, + CodecKeys = [decode_format, string_decode, strict_mbit, incoming_maxlen, @@ -157,7 +157,7 @@ i({Ack, T, Pid, {Opts, suspect => 1, okay => 3}, Opts)), - codec = maps:with(CodecKeys, SvcOpts#{record_decode := false, + codec = maps:with(CodecKeys, SvcOpts#{decode_format := false, string_decode := false, ordered_encode => false})}. diff --git a/lib/diameter/test/diameter_codec_SUITE.erl b/lib/diameter/test/diameter_codec_SUITE.erl index 31332537e9..c79b642c09 100644 --- a/lib/diameter/test/diameter_codec_SUITE.erl +++ b/lib/diameter/test/diameter_codec_SUITE.erl @@ -292,7 +292,7 @@ recode(Msg, Dict) -> opts(Mod) -> #{dictionary => Mod, - record_decode => true, + decode_format => record, string_decode => false, strict_mbit => true, rfc => 6733, diff --git a/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl b/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl index fe602c9ee5..735339ebb9 100644 --- a/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl +++ b/lib/diameter/test/diameter_codec_SUITE_data/diameter_test_unknown.erl @@ -78,7 +78,7 @@ dec('BR', #diameter_packet opts(Mod) -> #{dictionary => Mod, - record_decode => true, + decode_format => record, string_decode => true, strict_mbit => true, rfc => 6733, diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl index 7595e7edfc..22fb0550ea 100644 --- a/lib/diameter/test/diameter_codec_test.erl +++ b/lib/diameter/test/diameter_codec_test.erl @@ -219,7 +219,7 @@ opts(Mod) -> dictionary => Mod}. opts() -> - #{record_decode => true, + #{decode_format => record, string_decode => true, strict_mbit => true, rfc => 6733, diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index adf8bf4d66..aa0098ccd6 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -142,8 +142,8 @@ %% How to construct outgoing messages. -define(ENCODINGS, [list, record, map]). -%% How to set record_decode. --define(DECODINGS, [true, false, map, list]). +%% How to decode incoming messages. +-define(DECODINGS, [record, false, map, list]). %% How to send answers, in a diameter_packet or not. -define(CONTAINERS, [pkt, msg]). @@ -462,7 +462,7 @@ start_services(Config) -> server_service = SN, server_decoding = SD} = group(Config), - ok = diameter:start_service(SN, [{record_decode, SD} + ok = diameter:start_service(SN, [{decode_format, SD} | ?SERVICE(SN, S)]), ok = diameter:start_service(CN, [{sequence, ?CLIENT_MASK} | ?SERVICE(CN, S)]). @@ -1065,11 +1065,11 @@ origin(N) -> %% Map atoms. The atoms are part of (constructed) group names, so it's %% good that they're readable. -decode(true) -> 0; %% record +decode(record) -> 0; decode(list) -> 1; decode(map) -> 2; decode(false) -> 3; -decode(0) -> true; +decode(0) -> record; decode(1) -> list; decode(2) -> map; decode(3) -> false. @@ -1113,14 +1113,14 @@ to_map(map, #diameter_packet{msg = Msg}) to_map(list, #diameter_packet{msg = [MsgName | Avps]}) -> maps:put(':name', MsgName, maps:from_list(Avps)); -to_map(true, #diameter_packet{header = H, msg = Rec}) -> +to_map(record, #diameter_packet{header = H, msg = Rec}) -> rec_to_map(Rec, dict(H)); %% No record decode: do it ourselves. to_map(false, #diameter_packet{header = H, msg = false, bin = Bin}) -> - Opts = #{record_decode => map, + Opts = #{decode_format => map, string_decode => false, strict_mbit => true, rfc => 6733}, -- cgit v1.2.3 From fa2f0572aa0604bf03d4d3eaa358719ffd877545 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sat, 8 Jul 2017 02:06:11 +0200 Subject: Add decode_format record_from_map Undocumented, for transforming a map decode to record. The record decode becomes more expensive the larger the number of AVPs in the message definition in question, since the record is recreated each time an AVP value is set in it. The map decode can potentially do better. --- lib/diameter/src/base/diameter.erl | 6 +++++- lib/diameter/src/base/diameter_config.erl | 3 ++- lib/diameter/src/base/diameter_gen.erl | 13 +++++++++---- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl index 75deaad511..85a54c8e61 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -332,7 +332,11 @@ call(SvcName, App, Message) -> :: 0..16#FFFFFF. -type decode_format() - :: record | list | map | false. + :: record + | list + | map + | false + | record_from_map. %% Options passed to start_service/2 diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index 09018308d5..d591fa903e 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -771,7 +771,8 @@ opt(K, true = B) opt(decode_format, T) when T == record; T == list; - T == map -> + T == map; + T == record_from_map -> T; opt(restrict_connections, T) diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index 239d4a535f..78d8bd2fa3 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -186,7 +186,7 @@ decode_avps(Name, Recs, #{module := Mod, decode_format := Fmt} = Opts) -> %% AM counts the number of top-level AVPs, which missing/4 then %% uses when adding 5005 errors. Arities = Mod:avp_arity(Name), - {reformat(Rec, Arities, Fmt), + {reformat(Rec, Arities, Mod, Fmt), Avps, Failed ++ missing(Arities, Opts, Mod, AM)}. @@ -760,12 +760,17 @@ newrec(_, Name, _) -> newrec(Mod, Name) -> Mod:'#new-'(Mod:name2rec(Name)). -%% reformat/3 +%% reformat/4 -reformat(Map, Arities, list) -> +reformat(Map, Arities, _Mod, list) -> [{F,V} || {F,_} <- Arities, #{F := V} <- [Map]]; -reformat(Rec, _, _) -> +reformat(Map, Arities, Mod, record_from_map) -> + #{':name' := Name} = Map, + RecName = Mod:name2rec(Name), + list_to_tuple([RecName | [maps:get(F, Map, def(A)) || {F,A} <- Arities]]); + +reformat(Rec, _, _, _) -> Rec. %% def/1 -- cgit v1.2.3 From 2984749c093cc4b599190e2dcdaad4335899ecd4 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sat, 8 Jul 2017 02:17:42 +0200 Subject: Don't exercise client/server encoding independently in traffic suite To reduce the number of config combinations that are tested. The encoding is the format in which messages are provided to diameter for encode (to binary), and if there is any difference in the end result then the peer will detect this at decode, independently of its encoding format. --- lib/diameter/test/diameter_traffic_SUITE.erl | 36 +++++++++++++--------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index aa0098ccd6..eb2a9833f7 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -167,13 +167,12 @@ -record(group, {transport, strings, + encoding, client_service, - client_encoding, client_dict, client_sender, server_service, server_decoding, - server_encoding, server_container, server_sender, server_throttle}). @@ -268,13 +267,12 @@ all() -> groups() -> [{P, [P], Ts} || Ts <- [tc(tc())], P <- [shuffle, parallel]] ++ - [{?util:name([T,CE,D,SE,SD,SC,S,SS,ST,CS]), + [{?util:name([T,E,D,SD,SC,S,SS,ST,CS]), [], [{group, if S -> shuffle; not S -> parallel end}]} || T <- ?TRANSPORTS, - CE <- ?ENCODINGS, + E <- ?ENCODINGS, D <- ?RFCS, - SE <- ?ENCODINGS, SD <- ?DECODINGS, SC <- ?CONTAINERS, S <- ?STRING_DECODES, @@ -282,10 +280,9 @@ groups() -> ST <- ?CALLBACKS, CS <- ?SENDERS] ++ - [{T, [], groups([[T,CE,D,SE,SD,SC,S,SS,ST,CS] - || CE <- ?ENCODINGS, + [{T, [], groups([[T,E,D,SD,SC,S,SS,ST,CS] + || E <- ?ENCODINGS, D <- ?RFCS, - SE <- ?ENCODINGS, SD <- ?DECODINGS, SC <- ?CONTAINERS, S <- ?STRING_DECODES, @@ -298,7 +295,7 @@ groups() -> [{traffic, [], [{group, T} || T <- ?TRANSPORTS]}]. %groups(_) -> %% debug -% Name = [tcp,record,rfc6733,record,map,pkt,false,false,false,false], +% Name = [tcp,record,rfc6733,map,pkt,false,false,false,false], % [{group, ?util:name(Name)}]; groups(Names) -> [{group, ?util:name(L)} || L <- Names]. @@ -337,18 +334,17 @@ init_per_group(sctp = Name, Config) -> init_per_group(Name, Config) -> Nas = proplists:get_value(rfc4005, Config, false), case ?util:name(Name) of - [_,_,D,_,_,_,_,_,_,_] when D == rfc4005, true /= Nas -> + [_,_,D,_,_,_,_,_,_] when D == rfc4005, true /= Nas -> {skip, rfc4005}; - [T,CE,D,SE,SD,SC,S,SS,ST,CS] -> + [T,E,D,SD,SC,S,SS,ST,CS] -> G = #group{transport = T, strings = S, + encoding = E, client_service = [$C|?util:unique_string()], - client_encoding = CE, client_dict = appdict(D), client_sender = CS, server_service = [$S|?util:unique_string()], server_decoding = SD, - server_encoding = SE, server_container = SC, server_sender = SS, server_throttle = ST}, @@ -469,10 +465,10 @@ start_services(Config) -> add_transports(Config) -> #group{transport = T, + encoding = E, client_service = CN, client_sender = CS, server_service = SN, - server_encoding = SE, server_container = SC, server_sender = SS, server_throttle = ST} @@ -495,7 +491,7 @@ add_transports(Config) -> || D <- ?DECODINGS, %% for multiple candidate peers R <- ?RFCS, R /= rfc4005 orelse have_nas(), - Id <- [{D,SE,SC}]], + Id <- [{D,E,SC}]], %% The server uses the client's Origin-State-Id to decide how to %% answer. ?util:write_priv(Config, "transport", [LRef | Cs]). @@ -1046,14 +1042,14 @@ call(Config, Req) -> call(Config, Req, Opts) -> Name = proplists:get_value(testcase, Config), - #group{client_service = CN, - client_encoding = ReqEncoding, + #group{encoding = Enc, + client_service = CN, client_dict = Dict0} = Group = group(Config), diameter:call(CN, dict(Req, Dict0), - msg(Req, ReqEncoding, Dict0), + msg(Req, Enc, Dict0), [{extra, [{Name, Group}, diameter_lib:now()]} | Opts]). origin({D,E,C}) -> @@ -1231,9 +1227,9 @@ pick_peer(_Peers, _, [$C|_], _State, {send_nopeer, _}, _, ?EXTRA) -> pick_peer(Peers, _, [$C|_], _State, {send_detach, Group}, _, {_,_}) -> find(Group, Peers). -find(#group{client_service = CN, +find(#group{encoding = E, + client_service = CN, server_decoding = D, - server_encoding = E, server_container = C}, [_|_] = Peers) -> Id = {D,E,C}, -- cgit v1.2.3 From 6c30cae16dcb1dc320c51e38c5cc477d52b46078 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 10 Jul 2017 15:08:28 +0200 Subject: Randomly wrap answers in diameter_packet in transport suite To reduce the number of combinations tested, as in the parent commit. --- lib/diameter/test/diameter_traffic_SUITE.erl | 61 +++++++++++++--------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index eb2a9833f7..8533dcaf77 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -145,9 +145,6 @@ %% How to decode incoming messages. -define(DECODINGS, [record, false, map, list]). -%% How to send answers, in a diameter_packet or not. --define(CONTAINERS, [pkt, msg]). - %% Which dictionary to use in the clients. -define(RFCS, [rfc3588, rfc6733, rfc4005]). @@ -173,7 +170,6 @@ client_sender, server_service, server_decoding, - server_container, server_sender, server_throttle}). @@ -267,24 +263,22 @@ all() -> groups() -> [{P, [P], Ts} || Ts <- [tc(tc())], P <- [shuffle, parallel]] ++ - [{?util:name([T,E,D,SD,SC,S,SS,ST,CS]), + [{?util:name([T,E,D,SD,S,SS,ST,CS]), [], [{group, if S -> shuffle; not S -> parallel end}]} || T <- ?TRANSPORTS, E <- ?ENCODINGS, D <- ?RFCS, SD <- ?DECODINGS, - SC <- ?CONTAINERS, S <- ?STRING_DECODES, SS <- ?SENDERS, ST <- ?CALLBACKS, CS <- ?SENDERS] ++ - [{T, [], groups([[T,E,D,SD,SC,S,SS,ST,CS] + [{T, [], groups([[T,E,D,SD,S,SS,ST,CS] || E <- ?ENCODINGS, D <- ?RFCS, SD <- ?DECODINGS, - SC <- ?CONTAINERS, S <- ?STRING_DECODES, SS <- ?SENDERS, ST <- ?CALLBACKS, @@ -295,7 +289,7 @@ groups() -> [{traffic, [], [{group, T} || T <- ?TRANSPORTS]}]. %groups(_) -> %% debug -% Name = [tcp,record,rfc6733,map,pkt,false,false,false,false], +% Name = [tcp,record,rfc6733,map,false,false,false,false], % [{group, ?util:name(Name)}]; groups(Names) -> [{group, ?util:name(L)} || L <- Names]. @@ -334,9 +328,9 @@ init_per_group(sctp = Name, Config) -> init_per_group(Name, Config) -> Nas = proplists:get_value(rfc4005, Config, false), case ?util:name(Name) of - [_,_,D,_,_,_,_,_,_] when D == rfc4005, true /= Nas -> + [_,_,D,_,_,_,_,_] when D == rfc4005, true /= Nas -> {skip, rfc4005}; - [T,E,D,SD,SC,S,SS,ST,CS] -> + [T,E,D,SD,S,SS,ST,CS] -> G = #group{transport = T, strings = S, encoding = E, @@ -345,7 +339,6 @@ init_per_group(Name, Config) -> client_sender = CS, server_service = [$S|?util:unique_string()], server_decoding = SD, - server_container = SC, server_sender = SS, server_throttle = ST}, %% Limit the number of testcase, since the number of @@ -469,7 +462,6 @@ add_transports(Config) -> client_service = CN, client_sender = CS, server_service = SN, - server_container = SC, server_sender = SS, server_throttle = ST} = group(Config), @@ -491,7 +483,7 @@ add_transports(Config) -> || D <- ?DECODINGS, %% for multiple candidate peers R <- ?RFCS, R /= rfc4005 orelse have_nas(), - Id <- [{D,E,SC}]], + Id <- [{D,E}]], %% The server uses the client's Origin-State-Id to decide how to %% answer. ?util:write_priv(Config, "transport", [LRef | Cs]). @@ -1052,11 +1044,11 @@ call(Config, Req, Opts) -> msg(Req, Enc, Dict0), [{extra, [{Name, Group}, diameter_lib:now()]} | Opts]). -origin({D,E,C}) -> - 8*decode(D) + 2*encode(E) + container(C); +origin({D,E}) -> + 4*decode(D) + encode(E); origin(N) -> - {decode(N bsr 3), encode((N bsr 1) rem 4), container(N rem 2)}. + {decode(N bsr 2), encode(N rem 4)}. %% Map atoms. The atoms are part of (constructed) group names, so it's %% good that they're readable. @@ -1077,11 +1069,6 @@ encode(0) -> record; encode(1) -> list; encode(2) -> map. -container(pkt) -> 0; -container(msg) -> 1; -container(0) -> pkt; -container(_) -> msg. - msg([H|_] = Msg, record = E, diameter_gen_base_rfc3588) when H == 'ACR'; H == 'ACA' -> @@ -1229,10 +1216,9 @@ pick_peer(Peers, _, [$C|_], _State, {send_detach, Group}, _, {_,_}) -> find(#group{encoding = E, client_service = CN, - server_decoding = D, - server_container = C}, + server_decoding = D}, [_|_] = Peers) -> - Id = {D,E,C}, + Id = {D,E}, [P] = [P || P <- Peers, id(Id, P, CN)], {ok, P}. @@ -1544,22 +1530,33 @@ handle_request(#diameter_packet{header = H, avps = As} V = EI bsr B, %% assert V = HI bsr B, %% #diameter_caps{origin_state_id = {_,[Id]}} = Caps, - {D,_,_} = T = origin(Id), + {D,_} = T = origin(Id), wrap(T, H, request(to_map(D, Pkt), [H|As], Caps)). wrap(Id, H, {Tag, Action, Post}) -> {Tag, wrap(Id, H, Action), Post}; + wrap(_, _, {reply, [#diameter_header{} | _]} = T) -> T; -wrap({_,E,C}, H, {reply, Ans}) -> - Msg = msg(Ans, E, diameter_gen_base_rfc3588), - wrap(C, H, {reply, base_to_nas(Msg, H)}); -wrap(pkt, _, {reply, Ans}) - when not is_record(Ans, diameter_packet) -> - {reply, #diameter_packet{msg = Ans}}; + +wrap({_,E}, H, {reply, Ans}) -> + Msg = base_to_nas(msg(Ans, E, diameter_gen_base_rfc3588), H), + {reply, wrap(Msg)}; + wrap(_, _, T) -> T. +%% Randomly wrap the answer in a diameter_packet. + +wrap(#diameter_packet{} = Pkt) -> + Pkt; + +wrap(Msg) -> + case rand:uniform(2) of + 1 -> #diameter_packet{msg = Msg}; + 2 -> Msg + end. + %% base_to_nas/2 base_to_nas(#diameter_packet{msg = Msg} = Pkt, H) -> -- cgit v1.2.3 From 58f9d631df0c256f7bc4ff3de2670b3b04e265f7 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sat, 8 Jul 2017 09:35:59 +0200 Subject: Rearrange group names in traffic suite For slightly better readability in the ct logs --- lib/diameter/test/diameter_traffic_SUITE.erl | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 8533dcaf77..100a4eebc9 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -263,25 +263,25 @@ all() -> groups() -> [{P, [P], Ts} || Ts <- [tc(tc())], P <- [shuffle, parallel]] ++ - [{?util:name([T,E,D,SD,S,SS,ST,CS]), + [{?util:name([T,R,E,D,S,ST,SS,CS]), [], [{group, if S -> shuffle; not S -> parallel end}]} || T <- ?TRANSPORTS, + R <- ?RFCS, E <- ?ENCODINGS, - D <- ?RFCS, - SD <- ?DECODINGS, + D <- ?DECODINGS, S <- ?STRING_DECODES, - SS <- ?SENDERS, ST <- ?CALLBACKS, + SS <- ?SENDERS, CS <- ?SENDERS] ++ - [{T, [], groups([[T,E,D,SD,S,SS,ST,CS] - || E <- ?ENCODINGS, - D <- ?RFCS, - SD <- ?DECODINGS, + [{T, [], groups([[T,R,E,D,S,ST,SS,CS] + || R <- ?RFCS, + E <- ?ENCODINGS, + D <- ?DECODINGS, S <- ?STRING_DECODES, - SS <- ?SENDERS, ST <- ?CALLBACKS, + SS <- ?SENDERS, CS <- ?SENDERS, SS orelse CS])} %% avoid deadlock || T <- ?TRANSPORTS] @@ -289,7 +289,7 @@ groups() -> [{traffic, [], [{group, T} || T <- ?TRANSPORTS]}]. %groups(_) -> %% debug -% Name = [tcp,record,rfc6733,map,false,false,false,false], +% Name = [tcp,rfc6733,record,map,false,false,false,false], % [{group, ?util:name(Name)}]; groups(Names) -> [{group, ?util:name(L)} || L <- Names]. @@ -328,17 +328,17 @@ init_per_group(sctp = Name, Config) -> init_per_group(Name, Config) -> Nas = proplists:get_value(rfc4005, Config, false), case ?util:name(Name) of - [_,_,D,_,_,_,_,_] when D == rfc4005, true /= Nas -> + [_,R,_,_,_,_,_,_] when R == rfc4005, true /= Nas -> {skip, rfc4005}; - [T,E,D,SD,S,SS,ST,CS] -> + [T,R,E,D,S,ST,SS,CS] -> G = #group{transport = T, strings = S, encoding = E, client_service = [$C|?util:unique_string()], - client_dict = appdict(D), + client_dict = appdict(R), client_sender = CS, server_service = [$S|?util:unique_string()], - server_decoding = SD, + server_decoding = D, server_sender = SS, server_throttle = ST}, %% Limit the number of testcase, since the number of -- cgit v1.2.3 From e0603ba18a67c1ef33f60122fe6f00393c0c0203 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 13 Jul 2017 01:28:06 +0200 Subject: Tweak map-valued decode Use the same [MsgName | Avps] representation as for the list decode, but with Avps a map instead of a AVP name/values list. As a result, don't set the message/AVP name on an additional key in the map, which felt a bit odd. Messages are [MsgName :: atom() | map()], Grouped AVPs are just map(). Fix at least one problem in the traffic suite along the way: with decode_format false, the own decode in to_map/2 didn't know whether or not to decode strings, resulting on some failures. --- lib/diameter/doc/src/diameter.xml | 12 +- lib/diameter/doc/src/diameter_codec.xml | 8 +- lib/diameter/src/base/diameter_codec.erl | 7 +- lib/diameter/src/base/diameter_gen.erl | 13 +- lib/diameter/src/base/diameter_traffic.erl | 58 ++--- lib/diameter/test/diameter_traffic_SUITE.erl | 332 ++++++++++++--------------- 6 files changed, 193 insertions(+), 237 deletions(-) diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index a7f001e096..43cb3a538c 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -539,7 +539,7 @@ that matches no peer.

The host and realm filters cause the Destination-Host and Destination-Realm AVPs to be extracted from the -outgoing request, assuming it to be a record-, list- or map-valued +outgoing request, assuming it to be a record- or list-valued &codec_message;, and assuming at most one of each AVP. If this is not the case then the {host|realm, &dict_DiameterIdentity;} filters must be used to achieve the desired result. @@ -802,10 +802,16 @@ be matched by corresponding &capability; configuration, of {decode_format, record | list | map | false}

-The type of decoded messages and grouped AVPs in the msg field +The format of decoded messages and grouped AVPs in the msg field of diameter_packet records and value field of diameter_avp records respectively. -If false then the fields are set this value. +If record then a record whose definition is generated from the +dictionary file in question. +If list or map then a [Name | Avps] pair where +Avps is either a list of AVP name/values pairs or a map keyed on +AVP names respectively. +If false then the representation is omitted and msg and +value fields are set to false. See also &codec_message;.

diff --git a/lib/diameter/doc/src/diameter_codec.xml b/lib/diameter/doc/src/diameter_codec.xml index 4df8e788b7..0846334d23 100644 --- a/lib/diameter/doc/src/diameter_codec.xml +++ b/lib/diameter/doc/src/diameter_codec.xml @@ -230,7 +230,8 @@ header.

-message() = record() | list() | map() +message() = record() + | maybe_improper_list()

The representation of a Diameter message as passed to @@ -240,9 +241,8 @@ a message as defined in a dictionary file is encoded as a record with one field for each component AVP. Equivalently, a message can also be encoded as a list whose head is the atom-valued message name (as specified in the relevant dictionary -file) and whose tail is a list of {AvpName, AvpValues} pairs, -or as a map with values keyed on AVP names and the message name in key -:name. +file) and whose tail is either a list of AVP name/values +pairs or a map with values keyed on AVP names. The format at decode is determined by &mod_service_opt; decode_format. Any of the formats is accepted at encode.

diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index 275e80b9bb..9b21bf4141 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -275,9 +275,6 @@ rec2msg(_, [Name|_]) when is_atom(Name) -> Name; -rec2msg(_, #{':name' := Name}) -> - Name; - rec2msg(Mod, Rec) -> Mod:rec2msg(element(1, Rec)). @@ -383,7 +380,9 @@ decode_avps(MsgName, Mod, AppMod, Opts, Pkt, Avps) -> %% ... or not errors = Errors, avps = As}. -reformat(MsgName, Avps, #{decode_format := list}) -> +reformat(MsgName, Avps, #{decode_format := T}) + when T == map; + T == list -> [MsgName | Avps]; reformat(_, Msg, _) -> diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index 78d8bd2fa3..597ec143a8 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -186,7 +186,7 @@ decode_avps(Name, Recs, #{module := Mod, decode_format := Fmt} = Opts) -> %% AM counts the number of top-level AVPs, which missing/4 then %% uses when adding 5005 errors. Arities = Mod:avp_arity(Name), - {reformat(Rec, Arities, Mod, Fmt), + {reformat(Name, Rec, Arities, Mod, Fmt), Avps, Failed ++ missing(Arities, Opts, Mod, AM)}. @@ -752,8 +752,8 @@ newrec(_, _, false = No) -> newrec(Mod, Name, record) -> newrec(Mod, Name); -newrec(_, Name, _) -> - #{':name' => Name}. +newrec(_, _, _) -> + #{}. %% newrec/2 @@ -762,15 +762,14 @@ newrec(Mod, Name) -> %% reformat/4 -reformat(Map, Arities, _Mod, list) -> +reformat(_, Map, Arities, _Mod, list) -> [{F,V} || {F,_} <- Arities, #{F := V} <- [Map]]; -reformat(Map, Arities, Mod, record_from_map) -> - #{':name' := Name} = Map, +reformat(Name, Map, Arities, Mod, record_from_map) -> RecName = Mod:name2rec(Name), list_to_tuple([RecName | [maps:get(F, Map, def(A)) || {F,A} <- Arities]]); -reformat(Rec, _, _, _) -> +reformat(_, Rec, _, _, _) -> Rec. %% def/1 diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index f684f60cb7..e9c422d6ab 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -621,16 +621,10 @@ is_answer_message(#diameter_packet{msg = Msg}, Dict0) -> is_answer_message([#diameter_header{is_request = R, is_error = E} | _], _) -> E andalso not R; -%% Message sent as a tagged avp/value list. +%% Message sent as a map or tagged avp/value list. is_answer_message([Name | _], _) -> Name == 'answer-message'; -%% Message sent as a map. -is_answer_message(Map, _) - when is_map(Map) -> - #{':name' := Name} = Map, - Name == 'answer-message'; - %% Message sent as a record. is_answer_message(Rec, Dict) -> try @@ -875,15 +869,13 @@ reset(Msg, [RC | Avps], Dict) -> %% set/3 -%% Reply as name and tuple list ... +%% Reply as name/values list ... +set([Name|As], Avps, _) + when is_map(As) -> + [Name | maps:merge(As, maps:from_list(Avps))]; set([_|_] = Ans, Avps, _) -> Ans ++ Avps; %% Values nearer tail take precedence. -%% ... a map ... -set(Ans, Avps, _) - when is_map(Ans) -> - maps:merge(Ans, maps:from_list(Avps)); - %% ... or record. set(Rec, Avps, Dict) -> Dict:'#set-'(Avps, Rec). @@ -902,9 +894,6 @@ rc([MsgName | _], RC, Dict) -> _ -> [] end; -rc(#{':name' := Name}, RC, Dict) -> - rc([Name], RC, Dict); - rc(Rec, RC, Dict) -> rc([Dict:rec2msg(element(1, Rec))], RC, Dict). @@ -937,10 +926,6 @@ failed(Msg, FailedAvp, Dict) -> msg2rec([MsgName | _], Dict) -> Dict:msg2rec(MsgName); -%% ... map ... -msg2rec(#{':name' := MsgName}, Dict) -> - Dict:msg2rec(MsgName); - %% ... or record. msg2rec(Rec, _) -> element(1, Rec). @@ -949,12 +934,11 @@ msg2rec(Rec, _) -> %% Message as name/values list ... values([_ | Avps], F, _) -> - proplists:get_value(F, Avps, []); - -%% ... map ... -values(Msg, F, _) - when is_map(Msg) -> - maps:get(F, Msg, []); + if is_map(Avps) -> + maps:get(F, Avps, []); + is_list(Avps) -> + proplists:get_value(F, Avps, []) + end; %% ... or record. values(Rec, F, Dict) -> @@ -1906,23 +1890,13 @@ get_avp(Dict, Name, [#diameter_header{} | Avps]) -> %% Message as name/values list ... get_avp(_, Name, [_MsgName | Avps]) -> - case lists:keyfind(Name, 1, Avps) of + case find(Name, Avps) of {_, V} -> #diameter_avp{name = Name, value = V}; _ -> undefined end; -%% ... map ... -get_avp(_, Name, Map) - when is_map(Map) -> - case maps:find(Name, Map) of - {ok, V} -> - #diameter_avp{name = Name, value = V}; - error -> - undefined - end; - %% ... or record (but not necessarily). get_avp(Dict, Name, Rec) -> try @@ -1932,6 +1906,16 @@ get_avp(Dict, Name, Rec) -> undefined end. +%% find/2 + +find(Key, Map) + when is_map(Map) -> + maps:find(Key, Map); + +find(Key, List) + when is_list(List) -> + lists:keyfind(Key, 1, List). + %% get_avp_value/3 get_avp_value(Dict, Name, Msg) -> diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 100a4eebc9..fae9f86c38 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -99,14 +99,14 @@ stop/1]). %% diameter callbacks --export([peer_up/3, - peer_down/3, - pick_peer/6, pick_peer/7, - prepare_request/5, prepare_request/6, - prepare_retransmit/5, - handle_answer/6, handle_answer/7, - handle_error/6, - handle_request/3]). +-export([peer_up/4, + peer_down/4, + pick_peer/7, pick_peer/8, + prepare_request/6, prepare_request/7, + prepare_retransmit/6, + handle_answer/7, handle_answer/8, + handle_error/7, + handle_request/4]). %% diameter_{tcp,sctp} callbacks -export([message/3]). @@ -182,19 +182,17 @@ %% A common match when receiving answers in a client. -define(answer_message(SessionId, ResultCode), - #{':name' := 'answer-message', - 'Session-Id' := SessionId, - 'Origin-Host' := _, - 'Origin-Realm' := _, - 'Result-Code' := ResultCode}). + ['answer-message' | #{'Session-Id' := SessionId, + 'Origin-Host' := _, + 'Origin-Realm' := _, + 'Result-Code' := ResultCode}]). -define(answer_message(ResultCode), - #{':name' := 'answer-message', - 'Origin-Host' := _, - 'Origin-Realm' := _, - 'Result-Code' := ResultCode}). + ['answer-message' | #{'Origin-Host' := _, + 'Origin-Realm' := _, + 'Result-Code' := ResultCode}]). %% Config for diameter:start_service/2. --define(SERVICE(Name, Decode), +-define(SERVICE(Name, Grp), [{'Origin-Host', Name ++ "." ++ ?REALM}, {'Origin-Realm', ?REALM}, {'Host-IP-Address', [?ADDR]}, @@ -203,10 +201,10 @@ {'Auth-Application-Id', [0]}, %% common messages {'Acct-Application-Id', [3]}, %% base accounting {restrict_connections, false}, - {string_decode, Decode}, + {string_decode, Grp#group.strings}, {incoming_maxlen, 1 bsl 21} | [{application, [{dictionary, D}, - {module, ?MODULE}, + {module, [?MODULE, Grp]}, {answer_errors, callback}]} || D <- [diameter_gen_base_rfc3588, diameter_gen_base_accounting, @@ -446,15 +444,15 @@ start(_Config) -> ok = diameter:start(). start_services(Config) -> - #group{strings = S, - client_service = CN, + #group{client_service = CN, server_service = SN, server_decoding = SD} + = Grp = group(Config), ok = diameter:start_service(SN, [{decode_format, SD} - | ?SERVICE(SN, S)]), + | ?SERVICE(SN, Grp)]), ok = diameter:start_service(CN, [{sequence, ?CLIENT_MASK} - | ?SERVICE(CN, S)]). + | ?SERVICE(CN, Grp)]). add_transports(Config) -> #group{transport = T, @@ -484,8 +482,6 @@ add_transports(Config) -> R <- ?RFCS, R /= rfc4005 orelse have_nas(), Id <- [{D,E}]], - %% The server uses the client's Origin-State-Id to decide how to - %% answer. ?util:write_priv(Config, "transport", [LRef | Cs]). server_apps() -> @@ -567,9 +563,8 @@ send_ok(Config) -> Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD}, {'Accounting-Record-Number', 1}], - #{':name' := 'ACA', - 'Result-Code' := ?SUCCESS, - 'Session-Id' := _} + ['ACA' | #{'Result-Code' := ?SUCCESS, + 'Session-Id' := _}] = call(Config, Req). %% Send an accounting ACR that the server answers badly to. @@ -585,9 +580,8 @@ send_eval(Config) -> Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD}, {'Accounting-Record-Number', 3}], - #{':name' := 'ACA', - 'Result-Code' := ?SUCCESS, - 'Session-Id' := _} + ['ACA' | #{'Result-Code' := ?SUCCESS, + 'Session-Id' := _}] = call(Config, Req). %% Send an accounting ACR that the server tries to answer with an @@ -613,8 +607,7 @@ send_protocol_error(Config) -> send_experimental_result(Config) -> Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD}, {'Accounting-Record-Number', 5}], - #{':name' := 'ACA', - 'Session-Id' := _} + ['ACA' | #{'Session-Id' := _}] = call(Config, Req). %% Send an ASR with an arbitrary non-mandatory AVP and expect success @@ -622,11 +615,10 @@ send_experimental_result(Config) -> send_arbitrary(Config) -> Req = ['ASR', {'AVP', [#diameter_avp{name = 'Product-Name', value = "XXX"}]}], - #{':name' := 'ASA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS, - 'AVP' := [#diameter_avp{name = 'Product-Name', - value = V}]} + ['ASA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS, + 'AVP' := [#diameter_avp{name = 'Product-Name', + value = V}]}] = call(Config, Req), "XXX" = string(V, Config). @@ -635,12 +627,11 @@ send_unknown(Config) -> Req = ['ASR', {'AVP', [#diameter_avp{code = 999, is_mandatory = false, data = <<17>>}]}], - #{':name' := 'ASA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS, - 'AVP' := [#diameter_avp{code = 999, - is_mandatory = false, - data = <<17>>}]} + ['ASA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS, + 'AVP' := [#diameter_avp{code = 999, + is_mandatory = false, + data = <<17>>}]}] = call(Config, Req). %% Ditto, and point the AVP length past the end of the message. Expect @@ -652,10 +643,9 @@ send_unknown_short(Config, M, RC) -> Req = ['ASR', {'AVP', [#diameter_avp{code = 999, is_mandatory = M, data = <<17>>}]}], - #{':name' := 'ASA', - 'Session-Id' := _, - 'Result-Code' := RC, - 'Failed-AVP' := Avps} + ['ASA' | #{'Session-Id' := _, + 'Result-Code' := RC, + 'Failed-AVP' := Avps}] = call(Config, Req), [[#diameter_avp{code = 999, is_mandatory = M, @@ -667,10 +657,9 @@ send_unknown_mandatory(Config) -> Req = ['ASR', {'AVP', [#diameter_avp{code = 999, is_mandatory = true, data = <<17>>}]}], - #{':name' := 'ASA', - 'Session-Id' := _, - 'Result-Code' := ?AVP_UNSUPPORTED, - 'Failed-AVP' := Avps} + ['ASA' | #{'Session-Id' := _, + 'Result-Code' := ?AVP_UNSUPPORTED, + 'Failed-AVP' := Avps}] = call(Config, Req), [[#diameter_avp{code = 999, is_mandatory = true, @@ -688,10 +677,9 @@ send_unexpected_mandatory_decode(Config) -> Req = ['ASR', {'AVP', [#diameter_avp{code = 27, %% Session-Timeout is_mandatory = true, data = <<12:32>>}]}], - #{':name' := 'ASA', - 'Session-Id' := _, - 'Result-Code' := ?AVP_UNSUPPORTED, - 'Failed-AVP' := Avps} + ['ASA' | #{'Session-Id' := _, + 'Result-Code' := ?AVP_UNSUPPORTED, + 'Failed-AVP' := Avps}] = call(Config, Req), [[#diameter_avp{code = 27, is_mandatory = true, @@ -707,10 +695,9 @@ send_unexpected_mandatory_decode(Config) -> send_grouped_error(Config) -> Req = ['ASR', {'Proxy-Info', [[{'Proxy-Host', "abcd"}, {'Proxy-State', ""}]]}], - #{':name' := 'ASA', - 'Session-Id' := _, - 'Result-Code' := ?INVALID_AVP_LENGTH, - 'Failed-AVP' := Avps} + ['ASA' | #{'Session-Id' := _, + 'Result-Code' := ?INVALID_AVP_LENGTH, + 'Failed-AVP' := Avps}] = call(Config, Req), [[#diameter_avp{name = 'Proxy-Info', value = V}]] = failed_avps(Avps, Config), @@ -743,9 +730,8 @@ send_error_bit(Config) -> %% Send a bad version and check that we get 5011. send_unsupported_version(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?UNSUPPORTED_VERSION} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?UNSUPPORTED_VERSION}] = call(Config, Req). %% Send a request containing an AVP length > data size. @@ -765,12 +751,11 @@ send_zero_avp_length(Config) -> send_invalid_avp_length(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?INVALID_AVP_LENGTH, - 'Origin-Host' := _, - 'Origin-Realm' := _, - 'Failed-AVP' := Avps} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?INVALID_AVP_LENGTH, + 'Origin-Host' := _, + 'Origin-Realm' := _, + 'Failed-AVP' := Avps}] = call(Config, Req), [[_]] = failed_avps(Avps, Config). @@ -787,18 +772,16 @@ send_invalid_reject(Config) -> send_unexpected_mandatory(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?AVP_UNSUPPORTED} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?AVP_UNSUPPORTED}] = call(Config, Req). %% Send something long that will be fragmented by TCP. send_long(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}, {'User-Name', [binary:copy(<<$X>>, 1 bsl 20)]}], - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = call(Config, Req). %% Send something longer than the configure incoming_maxlen. @@ -841,9 +824,8 @@ send_any_2(Config) -> send_all_1(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], Realm = lists:foldr(fun(C,A) -> [C,A] end, [], ?REALM), - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = call(Config, Req, [{filter, {all, [{host, any}, {realm, Realm}]}}]). send_all_2(Config) -> @@ -872,9 +854,8 @@ send_detach(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], Ref = make_ref(), ok = call(Config, Req, [{extra, [{self(), Ref}]}, detach]), - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = receive {Ref, T} -> T end. %% Send a request which can't be encoded and expect {error, encode}. @@ -887,15 +868,13 @@ send_destination_1(Config) -> = group(Config), Req = ['STR', {'Termination-Cause', ?LOGOUT}, {'Destination-Host', [?HOST(SN, ?REALM)]}], - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = call(Config, Req, [{filter, {all, [host, realm]}}]). send_destination_2(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = call(Config, Req, [{filter, {all, [host, realm]}}]). %% Send with filtering on and expect failure when specifying an @@ -959,9 +938,8 @@ send_bad_filter(Config, F) -> %% Specify multiple filter options and expect them be conjunctive. send_multiple_filters_1(Config) -> Fun = fun(#diameter_caps{}) -> true end, - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = send_multiple_filters(Config, [host, {eval, Fun}]). send_multiple_filters_2(Config) -> E = {erlang, is_tuple, []}, @@ -972,9 +950,8 @@ send_multiple_filters_3(Config) -> E2 = {erlang, is_tuple, []}, E3 = {erlang, is_record, [diameter_caps]}, E4 = [{erlang, is_record, []}, diameter_caps], - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = send_multiple_filters(Config, [{eval, E} || E <- [E1,E2,E3,E4]]). send_multiple_filters(Config, Fs) -> @@ -985,9 +962,8 @@ send_multiple_filters(Config, Fs) -> %% only the return value from the prepare_request callback being %% significant. send_anything(Config) -> - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = call(Config, anything). %% =========================================================================== @@ -1037,12 +1013,11 @@ call(Config, Req, Opts) -> #group{encoding = Enc, client_service = CN, client_dict = Dict0} - = Group = group(Config), diameter:call(CN, dict(Req, Dict0), msg(Req, Enc, Dict0), - [{extra, [{Name, Group}, diameter_lib:now()]} | Opts]). + [{extra, [Name, diameter_lib:now()]} | Opts]). origin({D,E}) -> 4*decode(D) + encode(E); @@ -1082,32 +1057,37 @@ msg([H|_] = Msg, record = E, diameter_gen_base_rfc6733) msg([H|T], record, Dict) -> Dict:'#new-'(Dict:msg2rec(H), T); -msg([H|T], map, _) -> - Map = maps:from_list(T), - Map#{':name' => H}; +msg([H|As], map, _) + when is_list(As) -> + [H | maps:from_list(As)]; msg(Msg, _, _) -> Msg. -to_map(map, #diameter_packet{msg = Msg}) - when is_map(Msg) -> +to_map(#diameter_packet{msg = [_MsgName | Avps] = Msg}, + #group{server_decoding = map}) + when is_map(Avps) -> Msg; -to_map(list, #diameter_packet{msg = [MsgName | Avps]}) -> - maps:put(':name', MsgName, maps:from_list(Avps)); +to_map(#diameter_packet{msg = [MsgName | Avps]}, + #group{server_decoding = list}) -> + [MsgName | maps:from_list(Avps)]; -to_map(record, #diameter_packet{header = H, msg = Rec}) -> +to_map(#diameter_packet{header = H, msg = Rec}, + #group{server_decoding = record}) -> rec_to_map(Rec, dict(H)); %% No record decode: do it ourselves. -to_map(false, #diameter_packet{header = H, - msg = false, - bin = Bin}) -> +to_map(#diameter_packet{header = H, + msg = false, + bin = Bin}, + #group{server_decoding = false, + strings = B}) -> Opts = #{decode_format => map, - string_decode => false, + string_decode => B, strict_mbit => true, rfc => 6733}, - #diameter_packet{msg = Msg} + #diameter_packet{msg = [_MsgName | _Map] = Msg} = diameter_codec:decode(dict(H), Opts, Bin), Msg. @@ -1123,11 +1103,9 @@ dict(#diameter_header{application_id = Id, rec_to_map(Rec, Dict) -> [R | Vs] = Dict:'#get-'(Rec), - maps:put(':name', - Dict:rec2msg(R), - maps:from_list([T || {_,V} = T <- Vs, - V /= undefined, - V /= []])). + [Dict:rec2msg(R) | maps:from_list([T || {_,V} = T <- Vs, + V /= undefined, + V /= []])]. appdict(rfc4005) -> nas4005; @@ -1166,12 +1144,12 @@ acct(diameter_gen_base_rfc6733) -> %% Set only values that aren't already. -set(_, [H|T], Vs) -> - [H | Vs ++ T]; - -set(_, Map, Vs) - when is_map(Map) -> - maps:merge(maps:from_list(Vs), Map); +set(_, [N | As], Vs) -> + [N | if is_map(As) -> + maps:merge(maps:from_list(Vs), As); + is_list(As) -> + Vs ++ As + end]; set(#group{client_dict = Dict0} = _Group, Rec, Vs) -> Dict = dict(Rec, Dict0), @@ -1192,26 +1170,26 @@ reset(_, _, _, Rec) -> %% =========================================================================== %% diameter callbacks -%% peer_up/3 +%% peer_up/4 -peer_up(_SvcName, _Peer, State) -> +peer_up(_SvcName, _Peer, State, _Group) -> State. %% peer_down/3 -peer_down(_SvcName, _Peer, State) -> +peer_down(_SvcName, _Peer, State, _Group) -> State. -%% pick_peer/6-7 +%% pick_peer/7-8 -pick_peer(Peers, _, [$C|_], _State, {Name, Group}, _) +pick_peer(Peers, _, [$C|_], _State, Group, Name, _) when Name /= send_detach -> find(Group, Peers). -pick_peer(_Peers, _, [$C|_], _State, {send_nopeer, _}, _, ?EXTRA) -> +pick_peer(_Peers, _, [$C|_], _State, _Group, send_nopeer, _, ?EXTRA) -> false; -pick_peer(Peers, _, [$C|_], _State, {send_detach, Group}, _, {_,_}) -> +pick_peer(Peers, _, [$C|_], _State, Group, send_detach, _, {_,_}) -> find(Group, Peers). find(#group{encoding = E, @@ -1227,15 +1205,15 @@ id(Id, {Pid, _Caps}, SvcName) -> = diameter:service_info(SvcName, Pid), lists:member({id, Id}, Opts). -%% prepare_request/5-6 +%% prepare_request/6-7 -prepare_request(_Pkt, [$C|_], {_Ref, _Caps}, {send_discard, _}, _) -> +prepare_request(_Pkt, [$C|_], {_Ref, _Caps}, _, send_discard, _) -> {discard, unprepared}; -prepare_request(Pkt, [$C|_], {_Ref, Caps}, {Name, Group}, _) -> +prepare_request(Pkt, [$C|_], {_Ref, Caps}, Group, Name, _) -> {send, prepare(Pkt, Caps, Name, Group)}. -prepare_request(Pkt, [$C|_], {_Ref, Caps}, {send_detach, Group}, _, _) -> +prepare_request(Pkt, [$C|_], {_Ref, Caps}, Group, send_detach, _, _) -> {eval_packet, {send, prepare(Pkt, Caps, Group)}, [fun log/2, detach]}. log(#diameter_packet{bin = Bin} = P, T) @@ -1446,12 +1424,12 @@ set(N, #diameter_packet{msg = Req}, Caps, Group) %% name/1 +name([H|#{}]) -> + {map, H}; + name([H|_]) -> {list, H}; -name(#{} = Map) -> - {map, maps:get(':name', Map)}; - name(Rec) -> try {record, element(1, Rec)} @@ -1460,17 +1438,17 @@ name(Rec) -> false end. -%% prepare_retransmit/5 +%% prepare_retransmit/6 -prepare_retransmit(_Pkt, false, _Peer, _Name, _Group) -> +prepare_retransmit(_Pkt, false, _Peer, _Group, _Name, _) -> discard. -%% handle_answer/6-7 +%% handle_answer/7-8 -handle_answer(Pkt, Req, [$C|_], Peer, {Name, Group}, _) -> +handle_answer(Pkt, Req, [$C|_], Peer, Group, Name, _) -> answer(Pkt, Req, Peer, Name, Group). -handle_answer(Pkt, Req, [$C|_], Peer, {send_detach = Name, Group}, _, X) -> +handle_answer(Pkt, Req, [$C|_], Peer, Group, send_detach = Name, _, X) -> {Pid, Ref} = X, Pid ! {Ref, answer(Pkt, Req, Peer, Name, Group)}. @@ -1503,18 +1481,18 @@ app(Req, _, Dict0) -> Dict = dict(Req, Dict0), Dict:id(). -%% handle_error/6 +%% handle_error/7 -handle_error(timeout = Reason, _Req, [$C|_], _Peer, _, Time) -> +handle_error(timeout = Reason, _Req, [$C|_], _Peer, _, _, Time) -> Now = diameter_lib:now(), {Reason, {diameter_lib:timestamp(Time), diameter_lib:timestamp(Now), diameter_lib:micro_diff(Now, Time)}}; -handle_error(Reason, _Req, [$C|_], _Peer, _, _Time) -> +handle_error(Reason, _Req, [$C|_], _Peer, _, _, _Time) -> {error, Reason}. -%% handle_request/3 +%% handle_request/4 %% Note that diameter will set Result-Code and Failed-AVPs if %% #diameter_packet.errors is non-null. @@ -1522,7 +1500,10 @@ handle_error(Reason, _Req, [$C|_], _Peer, _, _Time) -> handle_request(#diameter_packet{header = H, avps = As} = Pkt, _, - {_Ref, Caps}) -> + {_Ref, Caps}, + #group{encoding = E, + server_decoding = D} + = Grp) -> #diameter_header{end_to_end_id = EI, hop_by_hop_id = HI} = H, @@ -1530,8 +1511,8 @@ handle_request(#diameter_packet{header = H, avps = As} V = EI bsr B, %% assert V = HI bsr B, %% #diameter_caps{origin_state_id = {_,[Id]}} = Caps, - {D,_} = T = origin(Id), - wrap(T, H, request(to_map(D, Pkt), [H|As], Caps)). + {D,E} = T = origin(Id), %% assert + wrap(T, H, request(to_map(Pkt, Grp), [H|As], Caps)). wrap(Id, H, {Tag, Action, Post}) -> {Tag, wrap(Id, H, Action), Post}; @@ -1585,8 +1566,7 @@ base_to_nas(Msg, _) -> %% request/3 %% send_experimental_result -request(#{':name' := 'ACR', - 'Accounting-Record-Number' := 5}, +request(['ACR' | #{'Accounting-Record-Number' := 5}], [Hdr | Avps], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> @@ -1619,16 +1599,14 @@ request(Msg, _Avps, Caps) -> %% request/2 %% send_nok -request(#{':name' := 'ACR', - 'Accounting-Record-Number' := 0}, +request(['ACR' | #{'Accounting-Record-Number' := 0}], _) -> {eval_packet, {protocol_error, ?INVALID_AVP_BITS}, [fun log/2, invalid]}; %% send_bad_answer -request(#{':name' := 'ACR', - 'Session-Id' := SId, - 'Accounting-Record-Type' := RT, - 'Accounting-Record-Number' := 2 = RN}, +request(['ACR' | #{'Session-Id' := SId, + 'Accounting-Record-Type' := RT, + 'Accounting-Record-Number' := 2 = RN}], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> Ans = ['ACA', {'Result-Code', ?SUCCESS}, @@ -1642,10 +1620,9 @@ request(#{':name' := 'ACR', msg = Ans}}; %% send_eval -request(#{':name' := 'ACR', - 'Session-Id' := SId, - 'Accounting-Record-Type' := RT, - 'Accounting-Record-Number' := 3 = RN}, +request(['ACR' | #{'Session-Id' := SId, + 'Accounting-Record-Type' := RT, + 'Accounting-Record-Number' := 3 = RN}], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> Ans = ['ACA', {'Result-Code', ?SUCCESS}, @@ -1657,10 +1634,9 @@ request(#{':name' := 'ACR', {eval, {reply, Ans}, {erlang, now, []}}; %% send_ok -request(#{':name' := 'ACR', - 'Session-Id' := SId, - 'Accounting-Record-Type' := RT, - 'Accounting-Record-Number' := 1 = RN}, +request(['ACR' | #{'Session-Id' := SId, + 'Accounting-Record-Type' := RT, + 'Accounting-Record-Number' := 1 = RN}], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> {reply, ['ACA', {'Result-Code', ?SUCCESS}, @@ -1671,8 +1647,7 @@ request(#{':name' := 'ACR', {'Accounting-Record-Number', RN}]}; %% send_protocol_error -request(#{':name' := 'ACR', - 'Accounting-Record-Number' := 4}, +request(['ACR' | #{'Accounting-Record-Number' := 4}], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> Ans = ['answer-message', {'Result-Code', ?TOO_BUSY}, @@ -1680,46 +1655,39 @@ request(#{':name' := 'ACR', {'Origin-Realm', OR}], {reply, Ans}; -request(#{':name' := 'ASR', - 'Session-Id' := SId} - = Req, +request(['ASR' | #{'Session-Id' := SId} = Avps], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> {reply, ['ASA', {'Result-Code', ?SUCCESS}, {'Session-Id', SId}, {'Origin-Host', OH}, {'Origin-Realm', OR}, - {'AVP', maps:get('AVP', Req, [])}]}; + {'AVP', maps:get('AVP', Avps, [])}]}; %% send_invalid_reject -request(#{':name' := 'STR', - 'Termination-Cause' := ?USER_MOVED}, +request(['STR' | #{'Termination-Cause' := ?USER_MOVED}], _Caps) -> {protocol_error, ?TOO_BUSY}; %% send_noreply -request(#{':name' := 'STR', - 'Termination-Cause' := T}, +request(['STR' | #{'Termination-Cause' := T}], _Caps) when T /= ?LOGOUT -> discard; %% send_destination_5 -request(#{':name' := 'STR', - 'Destination-Realm' := R}, +request(['STR' | #{'Destination-Realm' := R}], #diameter_caps{origin_realm = {OR, _}}) when R /= undefined, R /= OR -> {protocol_error, ?REALM_NOT_SERVED}; %% send_destination_6 -request(#{':name' := 'STR', - 'Destination-Host' := [H]}, +request(['STR' | #{'Destination-Host' := [H]}], #diameter_caps{origin_host = {OH, _}}) when H /= OH -> {protocol_error, ?UNABLE_TO_DELIVER}; -request(#{':name' := 'STR', - 'Session-Id' := SId}, +request(['STR' | #{'Session-Id' := SId}], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> {reply, ['STA', {'Result-Code', ?SUCCESS}, @@ -1728,7 +1696,7 @@ request(#{':name' := 'STR', {'Origin-Realm', OR}]}; %% send_error/send_timeout -request(#{':name' := 'RAR'}, _Caps) -> +request(['RAR' | #{}], _Caps) -> receive after 2000 -> {protocol_error, ?TOO_BUSY} end. %% message/3 -- cgit v1.2.3 From e54f92c328c8fb117de88b1171f64358f1e7d3b1 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 9 Jul 2017 00:38:56 +0200 Subject: Don't take length of AVP lists unnecessarily at encode Count as AVPs are encoded instead. --- lib/diameter/src/base/diameter_gen.erl | 64 +++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index 597ec143a8..313be5f215 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -104,7 +104,8 @@ enc(_, AvpName, 1, undefined, _, _) -> ?THROW([mandatory_avp_missing, AvpName]); enc(Name, AvpName, 1, Value, Opts, Mod) -> - enc(Name, AvpName, [Value], Opts, Mod); + H = avp_header(AvpName, Mod), + enc1(Name, AvpName, H, Value, Opts, Mod); enc(_, _, {0,_}, [], _, _) -> []; @@ -113,31 +114,47 @@ enc(_, AvpName, _, T, _, _) when not is_list(T) -> ?THROW([repeated_avp_as_non_list, AvpName, T]); -enc(_, AvpName, {Min, _}, L, _, _) - when length(L) < Min -> - ?THROW([repeated_avp_insufficient_arity, AvpName, Min, L]); +enc(Name, AvpName, {Min, Max}, Values, Opts, Mod) -> + H = avp_header(AvpName, Mod), + enc(Name, AvpName, H, Min, 0, Max, Values, Opts, Mod). -enc(_, AvpName, {_, Max}, L, _, _) - when Max < length(L) -> - ?THROW([repeated_avp_excessive_arity, AvpName, Max, L]); +%% enc/9 -enc(Name, AvpName, _, Values, Opts, Mod) -> - enc(Name, AvpName, Values, Opts, Mod). +enc(_, AvpName, _, Min, N, _, [], _, _) + when N < Min -> + ?THROW([repeated_avp_insufficient_arity, AvpName, Min, N]); -%% enc/5 +enc(_, _, _, _, _, _, [], _, _) -> + []; -enc(Name, 'AVP', Values, Opts, Mod) -> - [enc_AVP(Name, A, Opts, Mod) || A <- Values]; +enc(_, AvpName, _, _, N, Max, _, _, _) + when Max =< N -> + ?THROW([repeated_avp_excessive_arity, AvpName, Max]); -enc(_, AvpName, Values, Opts, Mod) -> - enc(AvpName, Values, Opts, Mod). +enc(Name, AvpName, H, Min, N, Max, [V|Vs], Opts, Mod) -> + [enc1(Name, AvpName, H, V, Opts, Mod) + | enc(Name, AvpName, H, Min, N+1, Max, Vs, Opts, Mod)]. -%% enc/4 +%% avp_header/2 + +avp_header('AVP', _) -> + false; + +avp_header(AvpName, Mod) -> + {_,_,_} = Mod:avp_header(AvpName). + +%% enc1/6 -enc(AvpName, Values, Opts, Mod) -> - H = Mod:avp_header(AvpName), - [diameter_codec:pack_data(H, Mod:avp(encode, V, AvpName, Opts)) - || V <- Values]. +enc1(Name, 'AVP', false, Value, Opts, Mod) -> + enc_AVP(Name, Value, Opts, Mod); + +enc1(_, AvpName, Hdr, Value, Opts, Mod) -> + enc1(AvpName, Hdr, Value, Opts, Mod). + +%% enc1/5 + +enc1(AvpName, {_,_,_} = Hdr, Value, Opts, Mod) -> + diameter_codec:pack_data(Hdr, Mod:avp(encode, Value, AvpName, Opts)). %% enc_AVP/4 @@ -160,16 +177,21 @@ enc_AVP(_, #diameter_avp{name = N, value = V}, _, _) enc_AVP(Name, #diameter_avp{name = AvpName, value = Data}, Opts, Mod) -> 0 == Mod:avp_arity(Name, AvpName) orelse ?THROW([known_avp_as_AVP, Name, AvpName, Data]), - enc(AvpName, [Data], Opts, Mod); + enc(AvpName, Data, Opts, Mod); %% The backdoor ... enc_AVP(_, {AvpName, Value}, Opts, Mod) -> - enc(AvpName, [Value], Opts, Mod); + enc(AvpName, Value, Opts, Mod); %% ... and the side door. enc_AVP(_Name, {_Dict, _AvpName, _Data} = T, Opts, _) -> diameter_codec:pack_avp(#diameter_avp{data = T}, Opts). +%% enc/4 + +enc(AvpName, Value, Opts, Mod) -> + enc1(AvpName, Mod:avp_header(AvpName), Value, Opts, Mod). + %% --------------------------------------------------------------------------- %% # decode_avps/3 %% --------------------------------------------------------------------------- -- cgit v1.2.3 From 9a94d2df5b761685038a5e7f7f0920c6a8c3d451 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 9 Jul 2017 06:17:25 +0200 Subject: Tweak limiting of testcases in traffic suite Since the number is now under 50K again. Also make testing of individual groups or testcases easier. --- lib/diameter/test/diameter_traffic_SUITE.erl | 74 +++++++++++++++------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index fae9f86c38..fbd57ca4d4 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -120,6 +120,11 @@ %% =========================================================================== +%% Positive number of testcases from which to select (randomly) from +%% tc(), the list of testcases to run, or [] to run all. The random +%% selection is to limit the time it takes for the suite to run. +-define(LIMIT, length(tc())). + -define(util, diameter_util). -define(A, list_to_atom). @@ -258,44 +263,40 @@ suite() -> all() -> [rfc4005, start, result_codes, {group, traffic}, empty, stop]. +%% Redefine this to run one or more groups for debugging purposes. +-define(GROUPS, []). +%-define(GROUPS, [[tcp,rfc6733,record,map,false,false,false,false]]). + groups() -> - [{P, [P], Ts} || Ts <- [tc(tc())], P <- [shuffle, parallel]] + Names = names(), + [{P, [P], Ts} || Ts <- [tc()], P <- [shuffle, parallel]] ++ - [{?util:name([T,R,E,D,S,ST,SS,CS]), - [], - [{group, if S -> shuffle; not S -> parallel end}]} - || T <- ?TRANSPORTS, - R <- ?RFCS, - E <- ?ENCODINGS, - D <- ?DECODINGS, - S <- ?STRING_DECODES, - ST <- ?CALLBACKS, - SS <- ?SENDERS, - CS <- ?SENDERS] + [{?util:name(N), [], [{group, if S -> shuffle; not S -> parallel end}]} + || [_,_,_,_,S|_] = N <- Names] ++ - [{T, [], groups([[T,R,E,D,S,ST,SS,CS] - || R <- ?RFCS, - E <- ?ENCODINGS, - D <- ?DECODINGS, - S <- ?STRING_DECODES, - ST <- ?CALLBACKS, - SS <- ?SENDERS, - CS <- ?SENDERS, - SS orelse CS])} %% avoid deadlock + [{T, [], [{group, ?util:name(N)} || N <- names(Names, ?GROUPS), + T == hd(N)]} || T <- ?TRANSPORTS] ++ [{traffic, [], [{group, T} || T <- ?TRANSPORTS]}]. -%groups(_) -> %% debug -% Name = [tcp,rfc6733,record,map,false,false,false,false], -% [{group, ?util:name(Name)}]; -groups(Names) -> - [{group, ?util:name(L)} || L <- Names]. +names() -> + [[T,R,E,D,S,ST,SS,CS] || T <- ?TRANSPORTS, + R <- ?RFCS, + E <- ?ENCODINGS, + D <- ?DECODINGS, + S <- ?STRING_DECODES, + ST <- ?CALLBACKS, + SS <- ?SENDERS, + CS <- ?SENDERS]. + +names(Names, []) -> + [N || N <- Names, + [CS,SS|_] <- [lists:reverse(N)], + SS orelse CS]; %% avoid deadlock -%tc([N|_]) -> %% debug -% [N]; -tc(L) -> - L. +names(_, Names) -> + Names. %% -------------------- @@ -339,11 +340,7 @@ init_per_group(Name, Config) -> server_decoding = D, server_sender = SS, server_throttle = ST}, - %% Limit the number of testcase, since the number of - %% groups is large. - All = ?util:scramble(tc()), - TCs = lists:sublist(All, rand:uniform(32)), - [{group, G}, {runlist, TCs} | Config]; + [{group, G}, {runlist, select()} | Config]; _ -> Config end. @@ -357,6 +354,13 @@ end_per_group(Name, Config) end_per_group(_, _) -> ok. +select() -> + try rand:uniform(?LIMIT) of + N -> lists:sublist(?util:scramble(tc()), max(N,5)) + catch + error:_ -> ?LIMIT + end. + %% -------------------- %% Skip testcases that can reasonably fail under SCTP. -- cgit v1.2.3 From dec19d6f572a564502fb81c8f1ada5a470429d97 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 10 Jul 2017 14:54:28 +0200 Subject: Test decode_format record_from_map in traffic suite --- lib/diameter/test/diameter_traffic_SUITE.erl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index fbd57ca4d4..fb69cd831e 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -123,7 +123,7 @@ %% Positive number of testcases from which to select (randomly) from %% tc(), the list of testcases to run, or [] to run all. The random %% selection is to limit the time it takes for the suite to run. --define(LIMIT, length(tc())). +-define(LIMIT, 42). -define(util, diameter_util). @@ -148,7 +148,7 @@ -define(ENCODINGS, [list, record, map]). %% How to decode incoming messages. --define(DECODINGS, [record, false, map, list]). +-define(DECODINGS, [record, false, map, list, record_from_map]). %% Which dictionary to use in the clients. -define(RFCS, [rfc3588, rfc6733, rfc4005]). @@ -1036,10 +1036,12 @@ decode(record) -> 0; decode(list) -> 1; decode(map) -> 2; decode(false) -> 3; +decode(record_from_map) -> 4; decode(0) -> record; decode(1) -> list; decode(2) -> map; -decode(3) -> false. +decode(3) -> false; +decode(4) -> record_from_map. encode(record) -> 0; encode(list) -> 1; @@ -1078,7 +1080,9 @@ to_map(#diameter_packet{msg = [MsgName | Avps]}, [MsgName | maps:from_list(Avps)]; to_map(#diameter_packet{header = H, msg = Rec}, - #group{server_decoding = record}) -> + #group{server_decoding = D}) + when D == record; + D == record_from_map -> rec_to_map(Rec, dict(H)); %% No record decode: do it ourselves. -- cgit v1.2.3 From 246a5d8611e258119fc6bdc6c52772539c8b09ca Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 10 Jul 2017 20:32:02 +0200 Subject: Don't count AVPs unnecessarily at encode Stop counting when there can be no arity errors. --- lib/diameter/src/base/diameter_gen.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index 313be5f215..a7dc3aaec3 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -127,6 +127,10 @@ enc(_, AvpName, _, Min, N, _, [], _, _) enc(_, _, _, _, _, _, [], _, _) -> []; +enc(Name, AvpName, H, Min, N, '*', Vs, Opts, Mod) + when Min =< N -> + [enc1(Name, AvpName, H, V, Opts, Mod) || V <- Vs]; + enc(_, AvpName, _, _, N, Max, _, _, _) when Max =< N -> ?THROW([repeated_avp_excessive_arity, AvpName, Max]); -- cgit v1.2.3 From 19c246a124d1962535b535682a106dc862cdcddd Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 10 Jul 2017 18:17:49 +0200 Subject: Test Result-Code 5009 in traffic suite Aka DIAMETER_AVP_OCCURS_TOO_MANY_TIMES. This reveals a fault. The RFC says this: A message was received that included an AVP that appeared more often than permitted in the message definition. The Failed-AVP AVP MUST be included and contain a copy of the first instance of the offending AVP that exceeded the maximum number of occurrences. The list of AVPs is reversed when diameter checks arities, so Failed-AVP contains the wrong AVP, causing the new testcase to fail. --- lib/diameter/test/diameter_traffic_SUITE.erl | 40 +++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index fb69cd831e..eb3ee777ce 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -64,6 +64,7 @@ send_invalid_reject/1, send_unexpected_mandatory_decode/1, send_unexpected_mandatory/1, + send_too_many/1, send_long/1, send_maxlen/1, send_nopeer/1, @@ -234,6 +235,8 @@ ?'DIAMETER_BASE_RESULT-CODE_AVP_UNSUPPORTED'). -define(UNSUPPORTED_VERSION, ?'DIAMETER_BASE_RESULT-CODE_UNSUPPORTED_VERSION'). +-define(TOO_MANY, + ?'DIAMETER_BASE_RESULT-CODE_AVP_OCCURS_TOO_MANY_TIMES'). -define(REALM_NOT_SERVED, ?'DIAMETER_BASE_RESULT-CODE_REALM_NOT_SERVED'). -define(UNABLE_TO_DELIVER, @@ -411,6 +414,7 @@ tc() -> send_invalid_reject, send_unexpected_mandatory_decode, send_unexpected_mandatory, + send_too_many, send_long, send_maxlen, send_nopeer, @@ -549,7 +553,9 @@ rfc4005(Config) -> %% Ensure that result codes have the expected values. result_codes(_Config) -> - {2001, 3001, 3002, 3003, 3004, 3007, 3008, 3009, 5001, 5011, 5014} + {2001, + 3001, 3002, 3003, 3004, 3007, 3008, 3009, + 5001, 5009, 5011, 5014} = {?SUCCESS, ?COMMAND_UNSUPPORTED, ?UNABLE_TO_DELIVER, @@ -559,6 +565,7 @@ result_codes(_Config) -> ?INVALID_HDR_BITS, ?INVALID_AVP_BITS, ?AVP_UNSUPPORTED, + ?TOO_MANY, ?UNSUPPORTED_VERSION, ?INVALID_AVP_LENGTH}. @@ -691,6 +698,18 @@ send_unexpected_mandatory_decode(Config) -> data = <<12:32>>}]] = failed_avps(Avps, Config). +%% Try to two Auth-Application-Id in ASR expect 5009. +send_too_many(Config) -> + Req = ['ASR'], + + ['ASA' | #{'Session-Id' := _, + 'Result-Code' := ?TOO_MANY, + 'Failed-AVP' := Avps}] + = call(Config, Req), + [[#diameter_avp{name = 'Auth-Application-Id', + value = 42}]] + = failed_avps(Avps, Config). + %% Send an containing a faulty Grouped AVP (empty Proxy-Host in %% Proxy-Info) and expect that only the faulty AVP is sent in %% Failed-AVP. The encoded values of Proxy-Host and Proxy-State are @@ -1250,6 +1269,25 @@ prepare(Pkt, Caps, N, #group{client_dict = Dict0} = Group) <> = Bin, E#diameter_packet{bin = <>}; +prepare(Pkt, Caps, N, #group{client_dict = Dict0} = Group) + when N == send_too_many -> + Req = prepare(Pkt, Caps, Group), + + #diameter_packet{header = #diameter_header{length = L}, + bin = B} + = E + = diameter_codec:encode(Dict0, + Pkt#diameter_packet{msg = Req}), + M = L - 4 - 12, + <<1, L:24, + T:M/binary, + A:8/binary, D:4/binary>> + = B, + E#diameter_packet{bin = <<1, (L+12):24, + T/binary, + A/binary, D/binary, + A/binary, 42:32>>}; + prepare(Pkt, Caps, N, #group{client_dict = Dict0} = Group) when N == send_long_avp_length; N == send_short_avp_length; -- cgit v1.2.3 From df5814ace0461c37389d96e87ef8aae297802b2e Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Wed, 12 Jul 2017 17:42:03 +0200 Subject: Fix detection of 5009 errors As noted in the parent commit, the wrong AVPs were reported, being counted from the back of the message instead of the front. Both 5005 and 5009 errors are now detected after the message is decoded. --- lib/diameter/src/base/diameter_gen.erl | 201 ++++++++++++++++++--------------- 1 file changed, 110 insertions(+), 91 deletions(-) diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index a7dc3aaec3..eee0580080 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -209,14 +209,14 @@ decode_avps(Name, Recs, #{module := Mod, decode_format := Fmt} = Opts) -> = mapfoldl(fun(T,A) -> decode(Name, Opts, Mod, T, A) end, {newrec(Mod, Name, Fmt), #{}, []}, Recs), - %% AM counts the number of top-level AVPs, which missing/4 then - %% uses when adding 5005 errors. + %% AM counts the number of top-level AVPs, which arities/5 then + %% uses when adding 500[59] errors. Arities = Mod:avp_arity(Name), {reformat(Name, Rec, Arities, Mod, Fmt), Avps, - Failed ++ missing(Arities, Opts, Mod, AM)}. + Failed ++ arities(Arities, Opts, Mod, AM, Avps)}. -%% Append 5005 errors so that errors are reported in the order +%% Append arity errors so that errors are reported in the order %% encountered. Failed-AVP should typically contain the first %% error encountered. @@ -233,9 +233,7 @@ mapfoldl(F, Acc0, [T|Rest], List) -> mapfoldl(_, Acc, [], List) -> {List, Acc}. -%% missing/4 - -%% 3588: +%% 3588/6733: %% %% DIAMETER_MISSING_AVP 5005 %% The request did not contain an AVP that is required by the Command @@ -244,40 +242,73 @@ mapfoldl(_, Acc, [], List) -> %% AVP MUST contain an example of the missing AVP complete with the %% Vendor-Id if applicable. The value field of the missing AVP %% should be of correct minimum length and contain zeros. +%% +%% DIAMETER_AVP_OCCURS_TOO_MANY_TIMES 5009 +%% A message was received that included an AVP that appeared more +%% often than permitted in the message definition. The Failed-AVP +%% AVP MUST be included and contain a copy of the first instance of +%% the offending AVP that exceeded the maximum number of occurrences -missing(Arities, Opts, Mod, AM) -> - lists:foldl(fun(T,A) -> missing(T, AM, Opts, Mod, A) end, - [], - Arities). +arities(Arities, Opts, Mod, AM, Avps) -> + [Count, Map | More] + = lists:foldl(fun({N,T}, A) -> more(N, T, Opts, Mod, AM, A) end, + [0, #{}], + Arities), + less(Count, Map, Avps) ++ lists:reverse(More). -%% missing/5 +%% more/6 -missing({Name, Arity}, AM, Opts, Mod, Acc) -> - case missing(Name, Arity, AM) of - true -> [{5005, empty_avp(Name, Opts, Mod)} | Acc]; - false -> Acc - end. +more(_Name, {0, '*'}, _Opts, _Mod, _AM, Acc) -> + Acc; -%% missing/3 +more(Name, {Mn, Mx}, Opts, Mod, AM, Acc) -> + more(Name, Mn, Mx, Opts, Mod, AM, Acc); -missing(Name, Arity, AM) -> - 'AVP' /= Name andalso too_few(Name, Arity, AM). +more(Name, 1, Opts, Mod, AM, Acc) -> + more(Name, 1, 1, Opts, Mod, AM, Acc). -%% too_few/3 -%% -%% Maximum arities have already been checked during the decode. +%% more/7 -too_few(_, {0, _}, _) -> - false; +more(Name, Mn, Mx, Opts, Mod, AM, Acc) -> + macc(Name, Mn, maps:get(Name, AM, 0), Mx, Opts, Mod, Acc). + +%% macc/7 -too_few(FieldName, 1, AM) -> - not maps:is_key(FieldName, AM); +macc(Name, Mn, N, _, Opts, Mod, [M, Map | T]) + when N < Mn -> + [M, Map, {5005, empty_avp(Name, Opts, Mod)} | T]; -too_few(FieldName, {M, _}, AM) -> - maps:get(FieldName, AM, 0) < M. +macc(Name, _, N, Mx, _Opts, _Mod, [M, Map | T]) + when Mx < N -> + K = N - Mx, + [M + K, maps:put(Name, K, Map) | T]; + +macc(_Name, _, _, _, _Opts, _Mod, Acc) -> + Acc. + +%% less/3 + +less(0, _, _) -> + []; + +less(N, Map, [#diameter_avp{name = undefined} | Avps]) -> + less(N, Map, Avps); + +less(N, Map, [#diameter_avp{name = Name} = Avp | Avps]) -> + case Map of + #{Name := 0} -> + [{5009, Avp} | less(N-1, Map, Avps)]; + #{Name := M} -> + less(N, maps:put(Name, M-1, Map), Avps); + _ -> + less(N, Map, Avps) + end. %% empty_avp/3 +empty_avp('AVP', _, _) -> + #diameter_avp{data = <<0:64>>}; + empty_avp(Name, Opts, Mod) -> {Code, Flags, VId} = Mod:avp_header(Name), {Name, Type} = Mod:avp_name(Code, VId), @@ -307,15 +338,25 @@ decode(Name, = Avp, {Rec, AM, Failed}) -> T = Mod:avp_name(Code, Vid), - decode(Name, Opts, Mod, T, Avp, {Rec, incr(field(T), AM), Failed}). + F = field(T), + A = Mod:avp_arity(Name, F), + decode(Name, Opts, Mod, T, A, Avp, {Rec, incr(field(F, A), AM), Failed}). %% field/1 -field({AvpName, _Type}) -> +field({AvpName, _}) -> AvpName; -field('AVP' = A) -> - A. +field(_) -> + 'AVP'. + +%% field/2 + +field(_, 0) -> + 'AVP'; + +field(F, _) -> + F. %% incr/2 @@ -327,11 +368,11 @@ incr(Key, Map) -> incr(N) -> N + 1. -%% decode/6 +%% decode/7 %% AVP not in dictionary. -decode(Name, Opts, Mod, 'AVP', Avp, Acc) -> - decode_AVP(Name, Avp, Opts, Mod, Acc); +decode(Name, Opts, Mod, 'AVP', Arity, Avp, Acc) -> + decode_AVP(Name, Arity, Avp, Opts, Mod, Acc); %% 6733, 4.4: %% @@ -380,7 +421,7 @@ decode(Name, Opts, Mod, 'AVP', Avp, Acc) -> %% defined the RFC's "unrecognized", which is slightly stronger than %% "not defined".) -decode(Name, Opts0, Mod, {AvpName, Type}, Avp, Acc) -> +decode(Name, Opts0, Mod, {AvpName, Type}, Arity, Avp, Acc) -> #diameter_avp{data = Data, is_mandatory = M} = Avp, @@ -409,13 +450,13 @@ decode(Name, Opts0, Mod, {AvpName, Type}, Avp, Acc) -> A = Avp#diameter_avp{name = AvpName, value = Rec, type = Type}, - {[A|As], pack_avp(Name, A, Opts, Mod, Acc)}; + {[A|As], pack_avp(Name, Arity, A, Opts, Mod, Acc)}; V when Type /= 'Grouped' -> A = Avp#diameter_avp{name = AvpName, value = V, type = Type}, - {A, pack_avp(Name, A, Opts, Mod, Acc)} + {A, pack_avp(Name, Arity, A, Opts, Mod, Acc)} catch throw: {?MODULE, {grouped, Error, ComponentAvps}} -> decode_error(Name, @@ -525,7 +566,12 @@ set_failed(_, Opts) -> %% undecoded. Note that the type field is 'undefined' in this case. decode_AVP(Name, Avp, Opts, Mod, Acc) -> - {trim(Avp), pack_AVP(Name, Avp, Opts, Mod, Acc)}. + decode_AVP(Name, Mod:avp_arity(Name, 'AVP'), Avp, Opts, Mod, Acc). + +%% decode_AVP/6 + +decode_AVP(Name, Arity, Avp, Opts, Mod, Acc) -> + {trim(Avp), pack_AVP(Name, Arity, Avp, Opts, Mod, Acc)}. %% rc/2 @@ -545,11 +591,6 @@ rc({'DIAMETER', 5014 = RC, _}, #diameter_avp{name = AvpName} = A, Opts, Mod) -> rc(_, Avp, _, _) -> {5004, Avp}. -%% pack_avp/5 - -pack_avp(Name, #diameter_avp{name = AvpName} = Avp, Opts, Mod, Acc) -> - pack_avp(Name, Mod:avp_arity(Name, AvpName), Avp, Opts, Mod, Acc). - %% pack_avp/6 pack_avp(Name, 0, Avp, Opts, Mod, Acc) -> @@ -560,6 +601,11 @@ pack_avp(_, Arity, #diameter_avp{name = AvpName} = Avp, _Opts, Mod, Acc) -> %% pack_AVP/5 +pack_AVP(Name, Avp, Opts, Mod, Acc) -> + pack_AVP(Name, Mod:avp_arity(Name, 'AVP'), Avp, Opts, Mod, Acc). + +%% pack_AVP/6 + %% Length failure was induced because of a header/payload length %% mismatch. The AVP Length is reset to match the received data if %% this AVP is encoded in an answer message, since the length is @@ -573,17 +619,17 @@ pack_avp(_, Arity, #diameter_avp{name = AvpName} = Avp, _Opts, Mod, Acc) -> %% payload for the AVP's type, but in this case we don't know the %% type. -pack_AVP(_, #diameter_avp{data = {5014 = RC, Data}} = Avp, _, _, Acc) -> +pack_AVP(_, _, #diameter_avp{data = {5014 = RC, Data}} = Avp, _, _, Acc) -> {Rec, AM, Failed} = Acc, {Rec, AM, [{RC, Avp#diameter_avp{data = Data}} | Failed]}; -pack_AVP(Name, Avp, Opts, Mod, Acc) -> - Arity = pack_arity(Name, Opts, Mod, Avp), - if 0 == Arity -> +pack_AVP(Name, Arity, Avp, Opts, Mod, Acc) -> + case pack_AVP(Name, Opts, Arity, Avp) of + false -> M = Avp#diameter_avp.is_mandatory, {Rec, AM, Failed} = Acc, {Rec, AM, [{if M -> 5001; true -> 5008 end, Avp} | Failed]}; - true -> + true -> pack(Arity, 'AVP', Avp, Mod, Acc) end. @@ -600,18 +646,18 @@ pack_AVP(Name, Avp, Opts, Mod, Acc) -> %% Failed-AVP AVP MUST be included and contain a copy of the %% offending AVP. -%% pack_arity/4 +%% pack_AVP/4 %% Give Failed-AVP special treatment since (1) it'll contain any %% unrecognized mandatory AVP's and (2) the RFC 3588 grammar failed to %% allow for Failed-AVP in an answer-message. -pack_arity(Name, - #{strict_mbit := Strict, - failed_avp := Failed}, - Mod, - #diameter_avp{is_mandatory = M, - name = AvpName}) -> +pack_AVP(Name, + #{strict_mbit := Strict, + failed_avp := Failed}, + Arity, + #diameter_avp{is_mandatory = M, + name = AvpName}) -> %% Not testing just Name /= 'Failed-AVP' means we're changing the %% packing of AVPs nested within Failed-AVP, but the point of @@ -619,44 +665,17 @@ pack_arity(Name, %% possible, and failing because a mandatory AVP couldn't be %% packed into a dedicated field defeats that point. - if Failed == true; - Name == 'Failed-AVP'; - Name == 'answer-message', AvpName == 'Failed-AVP'; - not M; - not Strict -> - Mod:avp_arity(Name, 'AVP'); - true -> - 0 - end. + 0 < Arity andalso (Failed == true + orelse Name == 'Failed-AVP' + orelse (Name == 'answer-message' + andalso AvpName == 'Failed-AVP') + orelse not M + orelse not Strict). %% pack/5 pack(Arity, F, Avp, Mod, {Rec, AM, Failed}) -> - case too_many(F, Arity, AM) of - true -> {Rec, AM, [{5009, Avp} | Failed]}; - false -> {set(Arity, F, value(F, Avp), Mod, Rec), AM, Failed} - end. - -%% 3588: -%% -%% DIAMETER_AVP_OCCURS_TOO_MANY_TIMES 5009 -%% A message was received that included an AVP that appeared more -%% often than permitted in the message definition. The Failed-AVP -%% AVP MUST be included and contain a copy of the first instance of -%% the offending AVP that exceeded the maximum number of occurrences -%% - -%% too_many/3 - -too_many(_, {_, '*'}, _) -> - false; - -too_many(FieldName, {_, M}, Map) -> - too_many(FieldName, M, Map); - -too_many(FieldName, M, Map) -> - #{FieldName := N} = Map, - M < N. + {set(Arity, F, value(F, Avp), Mod, Rec), AM, Failed}. %% set/5 -- cgit v1.2.3 From 66bb5251e89487c5fb8c1f10b8ceb2c6c8f31eed Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 13 Jul 2017 01:09:57 +0200 Subject: Add service_opt() strict_arities To be able to disable the relatively expensive check that the number of AVPs received in a message or grouped AVP agrees with the dictionary in question. The may well be easier for the user in handle_request/answer callbacks, when digesting the received message, and in some cases may not be important. The check at encode can also be disabled, allowing messages that don't agree with the dictionary in question to be sent, which can be useful in test (at least). --- lib/diameter/doc/src/diameter.xml | 31 +++++++ lib/diameter/src/base/diameter.erl | 7 ++ lib/diameter/src/base/diameter_config.erl | 8 ++ lib/diameter/src/base/diameter_gen.erl | 125 ++++++++++++++++++++-------- lib/diameter/src/base/diameter_peer_fsm.erl | 1 + lib/diameter/src/base/diameter_service.erl | 3 +- lib/diameter/src/base/diameter_traffic.erl | 2 + lib/diameter/src/base/diameter_watchdog.erl | 9 +- 8 files changed, 145 insertions(+), 41 deletions(-) diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index 43cb3a538c..017f6bb01d 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -955,6 +955,37 @@ Options monitor and link are ignored.

Defaults to the empty list.

+ +{strict_arities, boolean() + | encode + | decode} + +

+Whether or not to require that the number of AVPs in a message or +grouped AVP agree with those specified in the dictionary in question. +If true then mismatches in an outgoing messages cause message +encoding to fail, while mismatches in an incoming message are reported +as 5005/5009 errors in the errors field of the diameter_packet record +passed to &app_handle_request; or &app_handle_answer; callbacks. +If false then neither error is enforced/detected. +If encode or decode then errors are only +enforced/detected on outgoing or incoming messages respectively.

+ +

+Defaults to true.

+ + +

+Disabling arity checks affects the form of messages at encode/decode. +In particular, decoded AVPs are represented as lists of values, +regardless of the AVP's arity (ie. expected number in the message/AVP +grammar in question), and values are expected to be supplied as lists +at encode. +This differs from the historic decode behaviour of representing AVPs +of arity 1 as bare values, not wrapped in a list.

+
+
+ {strict_mbit, boolean()} diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl index 85a54c8e61..c919ff4c93 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -48,6 +48,7 @@ -export_type([evaluable/0, decode_format/0, + strict_arities/0, restriction/0, message_length/0, remotes/0, @@ -338,6 +339,11 @@ call(SvcName, App, Message) -> | false | record_from_map. +-type strict_arities() + :: false + | encode + | decode. + %% Options passed to start_service/2 -type service_opt() @@ -348,6 +354,7 @@ call(SvcName, App, Message) -> | {share_peers, remotes()} | {decode_format, decode_format()} | {string_decode, boolean()} + | {strict_arities, true | strict_arities()} | {strict_mbit, boolean()} | {incoming_maxlen, message_length()} | {use_shared_peers, remotes()} diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index d591fa903e..2b069f40cc 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -712,6 +712,7 @@ make_config(SvcName, Opts) -> {?NOMASK, sequence}, {nodes, restrict_connections}, {16#FFFFFF, incoming_maxlen}, + {true, strict_arities}, {true, strict_mbit}, {record, decode_format}, {true, string_decode}, @@ -756,6 +757,7 @@ opt(K, false = B) K == use_shared_peers; K == monitor; K == restrict_connections; + K == strict_arities; K == strict_mbit; K == decode_format; K == string_decode -> @@ -764,6 +766,7 @@ opt(K, false = B) opt(K, true = B) when K == share_peers; K == use_shared_peers; + K == strict_arities; K == strict_mbit; K == string_decode -> B; @@ -775,6 +778,11 @@ opt(decode_format, T) T == record_from_map -> T; +opt(strict_arities, T) + when T == encode; + T == decode -> + T; + opt(restrict_connections, T) when T == node; T == nodes -> diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index eee0580080..243bc7965a 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -46,6 +46,9 @@ -type grouped_avp() :: nonempty_improper_list(#diameter_avp{}, [avp()]). -type avp() :: non_grouped_avp() | grouped_avp(). +%% The arbitrary arity returned from dictionary avp_arity functions. +-define(ANY, {0, '*'}). + %% --------------------------------------------------------------------------- %% # encode_avps/3 %% --------------------------------------------------------------------------- @@ -88,18 +91,26 @@ encode(Name, Vals, Opts, Mod) encode(Name, Map, Opts, Mod) when is_map(Map) -> [enc(Name, F, A, V, Opts, Mod) || {F,A} <- Mod:avp_arity(Name), - V <- [maps:get(F, Map, def(A))]]; + V <- [maps:get(F, Map, undefined)]]; encode(Name, Rec, Opts, Mod) -> [encode(Name, F, V, Opts, Mod) || {F,V} <- Mod:'#get-'(Rec)]. %% encode/5 +encode(Name, AvpName, Values, #{strict_arities := T} = Opts, Mod) + when T /= encode -> + enc(Name, AvpName, ?ANY, Values, Opts, Mod); + encode(Name, AvpName, Values, Opts, Mod) -> enc(Name, AvpName, Mod:avp_arity(Name, AvpName), Values, Opts, Mod). %% enc/6 +enc(Name, AvpName, Arity, Values, #{strict_arities := T} = Opts, Mod) + when T /= encode, Arity /= ?ANY -> + enc(Name, AvpName, ?ANY, Values, Opts, Mod); + enc(_, AvpName, 1, undefined, _, _) -> ?THROW([mandatory_avp_missing, AvpName]); @@ -110,6 +121,9 @@ enc(Name, AvpName, 1, Value, Opts, Mod) -> enc(_, _, {0,_}, [], _, _) -> []; +enc(_, _, _, undefined, _, _) -> + []; + enc(_, AvpName, _, T, _, _) when not is_list(T) -> ?THROW([repeated_avp_as_non_list, AvpName, T]); @@ -120,6 +134,14 @@ enc(Name, AvpName, {Min, Max}, Values, Opts, Mod) -> %% enc/9 +enc(Name, AvpName, H, Min, N, '*', Vs, Opts, Mod) + when Min =< N -> + [enc1(Name, AvpName, H, V, Opts, Mod) || V <- Vs]; + +enc(Name, AvpName, H, _, _, _, Vs, #{strict_arities := T} = Opts, Mod) + when T /= encode -> + [enc1(Name, AvpName, H, V, Opts, Mod) || V <- Vs]; + enc(_, AvpName, _, Min, N, _, [], _, _) when N < Min -> ?THROW([repeated_avp_insufficient_arity, AvpName, Min, N]); @@ -127,10 +149,6 @@ enc(_, AvpName, _, Min, N, _, [], _, _) enc(_, _, _, _, _, _, [], _, _) -> []; -enc(Name, AvpName, H, Min, N, '*', Vs, Opts, Mod) - when Min =< N -> - [enc1(Name, AvpName, H, V, Opts, Mod) || V <- Vs]; - enc(_, AvpName, _, _, N, Max, _, _, _) when Max =< N -> ?THROW([repeated_avp_excessive_arity, AvpName, Max]); @@ -207,12 +225,12 @@ enc(AvpName, Value, Opts, Mod) -> decode_avps(Name, Recs, #{module := Mod, decode_format := Fmt} = Opts) -> {Avps, {Rec, AM, Failed}} = mapfoldl(fun(T,A) -> decode(Name, Opts, Mod, T, A) end, - {newrec(Mod, Name, Fmt), #{}, []}, + {newrec(Fmt, Mod, Name, Opts), #{}, []}, Recs), %% AM counts the number of top-level AVPs, which arities/5 then %% uses when adding 500[59] errors. Arities = Mod:avp_arity(Name), - {reformat(Name, Rec, Arities, Mod, Fmt), + {reformat(Name, Rec, Arities, Mod, Opts, Fmt), Avps, Failed ++ arities(Arities, Opts, Mod, AM, Avps)}. @@ -249,6 +267,12 @@ mapfoldl(_, Acc, [], List) -> %% AVP MUST be included and contain a copy of the first instance of %% the offending AVP that exceeded the maximum number of occurrences +%% arities/5 + +arities(_, #{strict_arities := T}, _, _, _) + when T /= decode -> + []; + arities(Arities, Opts, Mod, AM, Avps) -> [Count, Map | More] = lists:foldl(fun({N,T}, A) -> more(N, T, Opts, Mod, AM, A) end, @@ -258,7 +282,7 @@ arities(Arities, Opts, Mod, AM, Avps) -> %% more/6 -more(_Name, {0, '*'}, _Opts, _Mod, _AM, Acc) -> +more(_Name, ?ANY, _Opts, _Mod, _AM, Acc) -> Acc; more(Name, {Mn, Mx}, Opts, Mod, AM, Acc) -> @@ -331,16 +355,21 @@ empty_avp(Name, Opts, Mod) -> %% decode/5 -decode(Name, - Opts, - Mod, - #diameter_avp{code = Code, vendor_id = Vid} - = Avp, - {Rec, AM, Failed}) -> - T = Mod:avp_name(Code, Vid), - F = field(T), - A = Mod:avp_arity(Name, F), - decode(Name, Opts, Mod, T, A, Avp, {Rec, incr(field(F, A), AM), Failed}). +decode(Name, Opts, Mod, Avp, Acc) -> + #diameter_avp{code = Code, vendor_id = Vid} + = Avp, + N = Mod:avp_name(Code, Vid), + case Opts of + #{strict_arities := T} when T /= decode -> + decode(Name, Opts, Mod, N, ?ANY, Avp, Acc); + _ -> + {Rec, AM, Failed} = Acc, + F = field(N), + A = Mod:avp_arity(Name, F), + decode(Name, Opts, Mod, N, A, Avp, {Rec, + incr(field(F, A), AM), + Failed}) + end. %% field/1 @@ -565,6 +594,10 @@ set_failed(_, Opts) -> %% Don't know this AVP: see if it can be packed in an 'AVP' field %% undecoded. Note that the type field is 'undefined' in this case. +decode_AVP(Name, Avp, #{strict_arities := T} = Opts, Mod, Acc) + when T /= decode -> + decode_AVP(Name, ?ANY, Avp, Opts, Mod, Acc); + decode_AVP(Name, Avp, Opts, Mod, Acc) -> decode_AVP(Name, Mod:avp_arity(Name, 'AVP'), Avp, Opts, Mod, Acc). @@ -601,6 +634,10 @@ pack_avp(_, Arity, #diameter_avp{name = AvpName} = Avp, _Opts, Mod, Acc) -> %% pack_AVP/5 +pack_AVP(Name, Avp, #{strict_arities := T} = Opts, Mod, Acc) + when T /= decode -> + pack_AVP(Name, ?ANY, Avp, Opts, Mod, Acc); + pack_AVP(Name, Avp, Opts, Mod, Acc) -> pack_AVP(Name, Mod:avp_arity(Name, 'AVP'), Avp, Opts, Mod, Acc). @@ -652,10 +689,17 @@ pack_AVP(Name, Arity, Avp, Opts, Mod, Acc) -> %% unrecognized mandatory AVP's and (2) the RFC 3588 grammar failed to %% allow for Failed-AVP in an answer-message. +pack_AVP(_, #{strict_arities := T}, _, _) + when T /= decode -> + true; + +pack_AVP(_, _, 0, _) -> + false; + pack_AVP(Name, #{strict_mbit := Strict, failed_avp := Failed}, - Arity, + _, #diameter_avp{is_mandatory = M, name = AvpName}) -> @@ -665,12 +709,11 @@ pack_AVP(Name, %% possible, and failing because a mandatory AVP couldn't be %% packed into a dedicated field defeats that point. - 0 < Arity andalso (Failed == true - orelse Name == 'Failed-AVP' - orelse (Name == 'answer-message' - andalso AvpName == 'Failed-AVP') - orelse not M - orelse not Strict). + Failed == true + orelse Name == 'Failed-AVP' + orelse (Name == 'answer-message' andalso AvpName == 'Failed-AVP') + orelse not M + orelse not Strict. %% pack/5 @@ -789,15 +832,21 @@ empty(Name, #{module := Mod} = Opts) -> %% ------------------------------------------------------------------------------ -%% newrec/3 +%% newrec/4 -newrec(_, _, false = No) -> +newrec(false = No, _, _, _) -> No; -newrec(Mod, Name, record) -> +newrec(record, Mod, Name, #{strict_arities := T}) + when T /= decode -> + RecName = Mod:name2rec(Name), + Sz = Mod:'#info-'(RecName, size), + erlang:make_tuple(Sz, [], [{1, RecName}]); + +newrec(record, Mod, Name, _) -> newrec(Mod, Name); -newrec(_, _, _) -> +newrec(_, _, _, _) -> #{}. %% newrec/2 @@ -805,22 +854,24 @@ newrec(_, _, _) -> newrec(Mod, Name) -> Mod:'#new-'(Mod:name2rec(Name)). -%% reformat/4 +%% reformat/5 -reformat(_, Map, Arities, _Mod, list) -> +reformat(_, Map, Arities, _Mod, _Opts, list) -> [{F,V} || {F,_} <- Arities, #{F := V} <- [Map]]; -reformat(Name, Map, Arities, Mod, record_from_map) -> +reformat(Name, Map, Arities, Mod, Opts, record_from_map) -> + SA = maps:get(strict_arities, Opts, decode), RecName = Mod:name2rec(Name), - list_to_tuple([RecName | [maps:get(F, Map, def(A)) || {F,A} <- Arities]]); + list_to_tuple([RecName | [maps:get(F, Map, def(A, SA)) + || {F,A} <- Arities]]); -reformat(_, Rec, _, _, _) -> +reformat(_, Rec, _, _, _, _) -> Rec. -%% def/1 +%% def/2 -def(1) -> +def(1, decode) -> undefined; -def(_) -> +def(_, _) -> []. diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index abf948593f..36014df94e 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -130,6 +130,7 @@ %% diameter:call/4. codec :: #{decode_format := record, string_decode := boolean(), + strict_arities => diameter:strict_arities(), strict_mbit := boolean(), rfc := 3588 | 6733, ordered_encode := false}, diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 43be4d889a..c3c3c4b0e7 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -112,6 +112,7 @@ use_shared_peers := diameter:remotes(),%% use from restrict_connections := diameter:restriction(), incoming_maxlen := diameter:message_length(), + strict_arities => diameter:strict_arities(), strict_mbit := boolean(), decode_format := diameter:decode_format(), string_decode := boolean(), @@ -701,7 +702,7 @@ init_peers() -> %% TPid} service_options(Opts) -> - maps:from_list(Opts). + maps:from_list(lists:delete({strict_arities, true}, Opts)). mref(false = No) -> No; diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index e9c422d6ab..3463a93568 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -78,6 +78,7 @@ sequence :: diameter:sequence(), codec :: #{decode_format := diameter:decode_format(), string_decode := boolean(), + strict_arities => diameter:strict_arities(), strict_mbit := boolean(), incoming_maxlen := diameter:message_length()}}). %% Note that incoming_maxlen is currently handled in diameter_peer_fsm, @@ -105,6 +106,7 @@ make_recvdata([SvcName, PeerT, Apps, SvcOpts | _]) -> sequence = Mask, codec = maps:with([decode_format, string_decode, + strict_arities, strict_mbit, ordered_encode, incoming_maxlen], diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl index 60baf1e8a4..294325751e 100644 --- a/lib/diameter/src/base/diameter_watchdog.erl +++ b/lib/diameter/src/base/diameter_watchdog.erl @@ -74,6 +74,7 @@ okay := non_neg_integer()}, %% REOPEN -> OKAY codec :: #{decode_format := false, string_decode := false, + strict_arities => diameter:strict_arities(), strict_mbit := boolean(), failed_avp := false, rfc := 3588 | 6733, @@ -138,6 +139,7 @@ i({Ack, T, Pid, {Opts, Nodes = restrict_nodes(Restrict), CodecKeys = [decode_format, string_decode, + strict_arities, strict_mbit, incoming_maxlen, spawn_opt, @@ -157,9 +159,10 @@ i({Ack, T, Pid, {Opts, suspect => 1, okay => 3}, Opts)), - codec = maps:with(CodecKeys, SvcOpts#{decode_format := false, - string_decode := false, - ordered_encode => false})}. + codec = maps:with(CodecKeys -- [strict_arities], + SvcOpts#{decode_format := false, + string_decode := false, + ordered_encode => false})}. wait(Ref, Pid) -> receive -- cgit v1.2.3 From 9adab9b89d49e7c8706e34b87be7d5123ce181cd Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 13 Jul 2017 13:18:19 +0200 Subject: Be forgiving of non-list values at encode --- lib/diameter/src/base/diameter_gen.erl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index 243bc7965a..3d0612c294 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -124,9 +124,12 @@ enc(_, _, {0,_}, [], _, _) -> enc(_, _, _, undefined, _, _) -> []; -enc(_, AvpName, _, T, _, _) - when not is_list(T) -> - ?THROW([repeated_avp_as_non_list, AvpName, T]); +%% Be forgiving when a list of values is expected. If the value itself +%% is a list then the user has to wrap it to avoid each member from +%% being interpreted as an individual AVP value. +enc(Name, AvpName, Arity, V, Opts, Mod) + when not is_list(V) -> + enc(Name, AvpName, Arity, [V], Opts, Mod); enc(Name, AvpName, {Min, Max}, Values, Opts, Mod) -> H = avp_header(AvpName, Mod), -- cgit v1.2.3 From a14ba6581063c4fca2edc36156e07c6582729e2e Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 13 Jul 2017 12:37:54 +0200 Subject: Use relaxed arity checks in traffic suite --- lib/diameter/test/diameter_traffic_SUITE.erl | 70 +++++++++++----------------- 1 file changed, 26 insertions(+), 44 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index eb3ee777ce..662d95e3ae 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -459,7 +459,8 @@ start_services(Config) -> = group(Config), ok = diameter:start_service(SN, [{decode_format, SD} | ?SERVICE(SN, Grp)]), - ok = diameter:start_service(CN, [{sequence, ?CLIENT_MASK} + ok = diameter:start_service(CN, [{sequence, ?CLIENT_MASK}, + {strict_arities, decode} | ?SERVICE(CN, Grp)]). add_transports(Config) -> @@ -700,14 +701,14 @@ send_unexpected_mandatory_decode(Config) -> %% Try to two Auth-Application-Id in ASR expect 5009. send_too_many(Config) -> - Req = ['ASR'], + Req = ['ASR', {'Auth-Application-Id', [?APP_ID, 44]}], ['ASA' | #{'Session-Id' := _, 'Result-Code' := ?TOO_MANY, 'Failed-AVP' := Avps}] = call(Config, Req), [[#diameter_avp{name = 'Auth-Application-Id', - value = 42}]] + value = 44}]] = failed_avps(Avps, Config). %% Send an containing a faulty Grouped AVP (empty Proxy-Host in @@ -883,7 +884,7 @@ send_detach(Config) -> %% Send a request which can't be encoded and expect {error, encode}. send_encode_error(Config) -> - {error, encode} = call(Config, ['STR']). %% No Termination-Cause + {error, encode} = call(Config, ['STR', {'Termination-Cause', huh}]). %% Send with filtering and expect success. send_destination_1(Config) -> @@ -904,14 +905,14 @@ send_destination_2(Config) -> %% unknown host or realm. send_destination_3(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}, - {'Destination-Realm', "unknown.org"}], + {'Destination-Realm', <<"unknown.org">>}], {error, no_connection} = call(Config, Req, [{filter, {all, [host, realm]}}]). send_destination_4(Config) -> #group{server_service = SN} = group(Config), Req = ['STR', {'Termination-Cause', ?LOGOUT}, - {'Destination-Host', [?HOST(SN, "unknown.org")]}], + {'Destination-Host', [?HOST(SN, ["unknown.org"])]}], {error, no_connection} = call(Config, Req, [{filter, {all, [host, realm]}}]). @@ -919,7 +920,7 @@ send_destination_4(Config) -> %% an unknown host or realm. send_destination_5(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}, - {'Destination-Realm', "unknown.org"}], + {'Destination-Realm', [<<"unknown.org">>]}], ?answer_message(?REALM_NOT_SERVED) = call(Config, Req). send_destination_6(Config) -> @@ -1269,25 +1270,6 @@ prepare(Pkt, Caps, N, #group{client_dict = Dict0} = Group) <> = Bin, E#diameter_packet{bin = <>}; -prepare(Pkt, Caps, N, #group{client_dict = Dict0} = Group) - when N == send_too_many -> - Req = prepare(Pkt, Caps, Group), - - #diameter_packet{header = #diameter_header{length = L}, - bin = B} - = E - = diameter_codec:encode(Dict0, - Pkt#diameter_packet{msg = Req}), - M = L - 4 - 12, - <<1, L:24, - T:M/binary, - A:8/binary, D:4/binary>> - = B, - E#diameter_packet{bin = <<1, (L+12):24, - T/binary, - A/binary, D/binary, - A/binary, 42:32>>}; - prepare(Pkt, Caps, N, #group{client_dict = Dict0} = Group) when N == send_long_avp_length; N == send_short_avp_length; @@ -1419,10 +1401,10 @@ set(N, #diameter_packet{msg = Req}, Caps, Group) origin_realm = {OR, DR}} = Caps, - set(Group, Req, [{'Session-Id', diameter:session_id(OH)}, - {'Origin-Host', OH}, - {'Origin-Realm', OR}, - {'Destination-Realm', DR}]); + set(Group, Req, [{'Session-Id', [diameter:session_id(OH)]}, + {'Origin-Host', [OH]}, + {'Origin-Realm', [OR]}, + {'Destination-Realm', [DR]}]); set(N, #diameter_packet{msg = Req}, Caps, Group) when N == {record, diameter_base_ASR}; @@ -1432,11 +1414,11 @@ set(N, #diameter_packet{msg = Req}, Caps, Group) #diameter_caps{origin_host = {OH, DH}, origin_realm = {OR, DR}} = Caps, - set(Group, Req, [{'Session-Id', diameter:session_id(OH)}, - {'Origin-Host', OH}, - {'Origin-Realm', OR}, - {'Destination-Host', DH}, - {'Destination-Realm', DR}, + set(Group, Req, [{'Session-Id', [diameter:session_id(OH)]}, + {'Origin-Host', [OH]}, + {'Origin-Realm', [OR]}, + {'Destination-Host', [DH]}, + {'Destination-Realm', [DR]}, {'Auth-Application-Id', ?APP_ID}]); set(N, #diameter_packet{msg = Req}, Caps, Group) @@ -1447,10 +1429,10 @@ set(N, #diameter_packet{msg = Req}, Caps, Group) #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, DR}} = Caps, - set(Group, Req, [{'Session-Id', diameter:session_id(OH)}, - {'Origin-Host', OH}, - {'Origin-Realm', OR}, - {'Destination-Realm', DR}, + set(Group, Req, [{'Session-Id', [diameter:session_id(OH)]}, + {'Origin-Host', [OH]}, + {'Origin-Realm', [OR]}, + {'Destination-Realm', [DR]}, {'Auth-Application-Id', ?APP_ID}]); set(N, #diameter_packet{msg = Req}, Caps, Group) @@ -1461,11 +1443,11 @@ set(N, #diameter_packet{msg = Req}, Caps, Group) #diameter_caps{origin_host = {OH, DH}, origin_realm = {OR, DR}} = Caps, - set(Group, Req, [{'Session-Id', diameter:session_id(OH)}, - {'Origin-Host', OH}, - {'Origin-Realm', OR}, - {'Destination-Host', DH}, - {'Destination-Realm', DR}, + set(Group, Req, [{'Session-Id', [diameter:session_id(OH)]}, + {'Origin-Host', [OH]}, + {'Origin-Realm', [OR]}, + {'Destination-Host', [DH]}, + {'Destination-Realm', [DR]}, {'Auth-Application-Id', ?APP_ID}]). %% name/1 -- cgit v1.2.3 From b3d9e0c09ff9d8f963b6084a84ab120cd423a9ae Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Tue, 25 Jul 2017 01:27:58 +0200 Subject: Redo message decode as a single pass Decode has previously been two passes: first chunk the message into a reversed list of toplevel diameter_avp records, then fold through the reversed list to build the full result. Various workarounds have made it a bit more convoluted than it should be however. Rework it completely, turning the previous 2-pass tail-recursive implementation into a 1-pass body recursive one. The relay decode still exists in diameter_codec, as a stripped down version of the full decode in diameter_gen. --- lib/diameter/include/diameter_gen.hrl | 8 +- lib/diameter/src/base/diameter_codec.erl | 185 +++----- lib/diameter/src/base/diameter_gen.erl | 648 +++++++++++++---------------- lib/diameter/src/base/diameter_traffic.erl | 12 +- 4 files changed, 362 insertions(+), 491 deletions(-) diff --git a/lib/diameter/include/diameter_gen.hrl b/lib/diameter/include/diameter_gen.hrl index fb6370fe54..548763ec7d 100644 --- a/lib/diameter/include/diameter_gen.hrl +++ b/lib/diameter/include/diameter_gen.hrl @@ -26,13 +26,13 @@ %% encode_avps/3 -encode_avps(Name, Vals, Opts) -> - diameter_gen:encode_avps(Name, Vals, Opts#{module => ?MODULE}). +encode_avps(Name, Avps, Opts) -> + diameter_gen:encode_avps(Name, Avps, Opts#{module => ?MODULE}). %% decode_avps/2 -decode_avps(Name, Recs, Opts) -> - diameter_gen:decode_avps(Name, Recs, Opts#{module => ?MODULE}). +decode_avps(Name, Avps, Opts) -> + diameter_gen:decode_avps(Name, Avps, Opts#{module => ?MODULE}). %% avp/5 diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index 9b21bf4141..c179c4b362 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -110,7 +110,7 @@ encode(Mod, Opts, Msg) -> enc(_, Opts, #diameter_packet{msg = [#diameter_header{} = Hdr | As]} = Pkt) -> - try encode_avps(reorder(As), Opts) of + try encode_avps(As, Opts) of Avps -> Bin = list_to_binary(Avps), Len = 20 + size(Bin), @@ -206,51 +206,12 @@ values(Avps) -> %% Message as a list of #diameter_avp{} ... encode_avps(_, _, [#diameter_avp{} | _] = Avps, Opts) -> - encode_avps(reorder(Avps), Opts); + encode_avps(Avps, Opts); %% ... or as a tuple list or record. encode_avps(Mod, MsgName, Values, Opts) -> Mod:encode_avps(MsgName, Values, Opts). -%% reorder/1 -%% -%% Reorder AVPs for the relay case using the index field of -%% diameter_avp records. Decode populates this field in collect_avps -%% and presents AVPs in reverse order. A relay then sends the reversed -%% list with a Route-Record AVP prepended. The goal here is just to do -%% lists:reverse/1 in Grouped AVPs and the outer list, but only in the -%% case there are indexed AVPs at all, so as not to reverse lists that -%% have been explicilty sent (unindexed, in the desired order) as a -%% diameter_avp list. The effect is the same as lists:keysort/2, but -%% only on the cases we expect, not a general sort. - -reorder(Avps) -> - case reorder(Avps, []) of - false -> - Avps; - Sorted -> - Sorted - end. - -%% reorder/3 - -%% In case someone has reversed the list already. (Not likely.) -reorder([#diameter_avp{index = 0} | _] = Avps, Acc) -> - Avps ++ Acc; - -%% Assume indexed AVPs are in reverse order. -reorder([#diameter_avp{index = N} = A | Avps], Acc) - when is_integer(N) -> - lists:reverse(Avps, [A | Acc]); - -%% An unindexed AVP. -reorder([H | T], Acc) -> - reorder(T, [H | Acc]); - -%% No indexed members. -reorder([], _) -> - false. - %% encode_avps/2 encode_avps(Avps, Opts) -> @@ -327,13 +288,7 @@ decode(Mod, AppMod, Opts, Pkt) -> %% Relay application: just extract the avp's without any decoding of %% their data since we don't know the application in question. decode(?APP_ID_RELAY, _, _, _, #diameter_packet{} = Pkt) -> - case collect_avps(Pkt) of - {E, As} -> - Pkt#diameter_packet{avps = As, - errors = [E]}; - As -> - Pkt#diameter_packet{avps = As} - end; + collect_avps(Pkt); %% Otherwise decode using the dictionary. decode(_, Mod, AppMod, Opts, #diameter_packet{header = Hdr} = Pkt) -> @@ -342,35 +297,31 @@ decode(_, Mod, AppMod, Opts, #diameter_packet{header = Hdr} = Pkt) -> is_error = IsError} = Hdr, - MsgName = if IsError andalso not IsRequest -> + MsgName = if IsError, not IsRequest -> 'answer-message'; true -> Mod:msg_name(CmdCode, IsRequest) end, - decode_avps(MsgName, Mod, AppMod, Opts, Pkt, collect_avps(Pkt)); + decode_avps(MsgName, Mod, AppMod, Opts, Pkt); decode(Id, Mod, AppMod, Opts, Bin) when is_binary(Bin) -> decode(Id, Mod, AppMod, Opts, #diameter_packet{header = decode_header(Bin), bin = Bin}). -%% decode_avps/6 +%% decode_avps/5 -decode_avps(MsgName, Mod, AppMod, Opts, Pkt, {E, Avps}) -> - ?LOG(invalid_avp_length, Pkt#diameter_packet.header), - #diameter_packet{errors = Failed} - = P - = decode_avps(MsgName, Mod, AppMod, Opts, Pkt, Avps), - P#diameter_packet{errors = [E | Failed]}; - -decode_avps('', _, _, _, Pkt, Avps) -> %% unknown message ... - ?LOG(unknown_message, Pkt#diameter_packet.header), - Pkt#diameter_packet{avps = lists:reverse(Avps), - errors = [3001]}; %% DIAMETER_COMMAND_UNSUPPORTED +decode_avps('', _, _, _, #diameter_packet{header = H, %% unknown message + bin = Bin} + = Pkt) -> + ?LOG(unknown_message, H), + Pkt#diameter_packet{avps = collect_avps(Bin), + errors = [3001]}; %% DIAMETER_COMMAND_UNSUPPORTED %% msg = undefined identifies this case. -decode_avps(MsgName, Mod, AppMod, Opts, Pkt, Avps) -> %% ... or not +decode_avps(MsgName, Mod, AppMod, Opts, #diameter_packet{bin = Bin} = Pkt) -> + <<_:20/binary, Avps/binary>> = Bin, {Rec, As, Errors} = Mod:decode_avps(MsgName, Avps, Opts#{dictionary => AppMod, @@ -380,6 +331,8 @@ decode_avps(MsgName, Mod, AppMod, Opts, Pkt, Avps) -> %% ... or not errors = Errors, avps = As}. +%% reformat/3 + reformat(MsgName, Avps, #{decode_format := T}) when T == map; T == list -> @@ -524,24 +477,21 @@ msg_id(<<_:32, Rbit:1, _:7, CmdCode:24, ApplId:32, _/binary>>) -> %%% # collect_avps/1 %%% --------------------------------------------------------------------------- -%% Note that the returned list of AVP's is reversed relative to their -%% order in the binary. Note also that grouped avp's aren't unraveled, -%% only those at the top level. +%% This is only used for the relay decode. Note that grouped avp's +%% aren't unraveled, only those at the top level. --spec collect_avps(#diameter_packet{} | binary()) - -> [Avp] - | {Error, [Avp]} - when Avp :: #diameter_avp{}, - Error :: {5014, #diameter_avp{}}. +-spec collect_avps(#diameter_packet{}) + -> #diameter_packet{}; + (binary()) + -> [#diameter_avp{}]. -collect_avps(#diameter_packet{bin = <<_:20/binary, Avps/binary>>}) -> - collect_avps(Avps, 0, []); +collect_avps(#diameter_packet{bin = Bin} = Pkt) -> + Pkt#diameter_packet{avps = collect_avps(Bin)}; -collect_avps(Bin) - when is_binary(Bin) -> - collect_avps(Bin, 0, []). +collect_avps(<<_:20/binary, Avps/binary>>) -> + collect(Avps, 0). -%% collect_avps/3 +%% collect/2 %% 0 1 2 3 %% 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 @@ -555,65 +505,44 @@ collect_avps(Bin) %% | Data ... %% +-+-+-+-+-+-+-+-+ -collect_avps(<>, - N, - Acc) -> - collect_avps(Code, - if 1 == V -> I; 0 == V -> undefined end, - 1 == M, - 1 == P, - Len - 8 - V*4, %% Might be negative, which ensures - ?PAD(Len), %% failure of the Data match below. - Rest, - N, - Acc); - -collect_avps(<<>>, _, Acc) -> - Acc; +collect(<>, N)-> + Vid = if 1 == V -> I; 0 == V -> undefined end, + DataLen = Len - 8 - V*4, %% Might be negative, which ensures + Pad = ?PAD(Len), %% failure of the match below. + MB = 1 == M, + PB = 1 == P, -%% Header is truncated. pack_avp/1 will pad this at encode if sent in -%% a Failed-AVP. -collect_avps(Bin, _, Acc) -> - {{5014, #diameter_avp{data = Bin}}, Acc}. - -%% collect_avps/9 - -%% Duplicate the diameter_avp creation in each branch below to avoid -%% modifying the record, which profiling has shown to be a relatively -%% costly part of building the list. - -collect_avps(Code, VendorId, M, P, Len, Pad, Rest, N, Acc) -> case Rest of - <> -> + <> -> Avp = #diameter_avp{code = Code, - vendor_id = VendorId, - is_mandatory = M, - need_encryption = P, + vendor_id = Vid, + is_mandatory = MB, + need_encryption = PB, data = Data, index = N}, - collect_avps(T, N+1, [Avp | Acc]); + [Avp | collect(T, N+1)]; _ -> %% Length in header points past the end of the message, or - %% doesn't span the header. As stated in the 6733 text - %% above, it's sufficient to return a zero-filled minimal - %% payload if this is a request. Do this (in cases that we - %% know the type) by inducing a decode failure and letting - %% the dictionary's decode (in diameter_gen) deal with it. - %% - %% Note that the extra bit can only occur in the trailing - %% AVP of a message or Grouped AVP, since a faulty AVP - %% Length is otherwise indistinguishable from a correct - %% one here, as we don't know the types of the AVPs being - %% extracted. - Avp = #diameter_avp{code = Code, - vendor_id = VendorId, - is_mandatory = M, - need_encryption = P, - data = {5014, Rest}, - index = N}, - [Avp | Acc] - end. + %% doesn't span the header. Note that an length error can + %% only occur in the trailing AVP of a message or Grouped + %% AVP, since a faulty AVP Length is otherwise + %% indistinguishable from a correct one here, as we don't + %% know the types of the AVPs being extracted. + [#diameter_avp{code = Code, + vendor_id = Vid, + is_mandatory = MB, + need_encryption = PB, + data = {5014, Rest}, + index = N}] + end; +collect(<<>>, _) -> + []; + +%% Header is truncated. pack_avp/1 will pad this at encode if sent in +%% a Failed-AVP. +collect(Bin, _) -> + [#diameter_avp{data = {5014, Bin}}]. %% 3588: %% diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index 3d0612c294..3688ff7b8f 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -91,7 +91,7 @@ encode(Name, Vals, Opts, Mod) encode(Name, Map, Opts, Mod) when is_map(Map) -> [enc(Name, F, A, V, Opts, Mod) || {F,A} <- Mod:avp_arity(Name), - V <- [maps:get(F, Map, undefined)]]; + V <- [mget(F, Map, undefined)]]; encode(Name, Rec, Opts, Mod) -> [encode(Name, F, V, Opts, Mod) || {F,V} <- Mod:'#get-'(Rec)]. @@ -221,115 +221,168 @@ enc(AvpName, Value, Opts, Mod) -> %% # decode_avps/3 %% --------------------------------------------------------------------------- --spec decode_avps(parent_name(), [#diameter_avp{}], map()) +-spec decode_avps(parent_name(), binary(), map()) -> {parent_record(), [avp()], Failed} when Failed :: [{5000..5999, #diameter_avp{}}]. -decode_avps(Name, Recs, #{module := Mod, decode_format := Fmt} = Opts) -> - {Avps, {Rec, AM, Failed}} - = mapfoldl(fun(T,A) -> decode(Name, Opts, Mod, T, A) end, - {newrec(Fmt, Mod, Name, Opts), #{}, []}, - Recs), - %% AM counts the number of top-level AVPs, which arities/5 then - %% uses when adding 500[59] errors. - Arities = Mod:avp_arity(Name), - {reformat(Name, Rec, Arities, Mod, Opts, Fmt), +decode_avps(Name, Bin, #{module := Mod, decode_format := Fmt} = Opts) -> + Strict = mget(strict_arities, Opts, decode), + [AM, Avps, Failed | Rec] + = decode(Bin, Name, Mod, Fmt, Strict, Opts, 0, #{}), + %% AM counts the number of top-level AVPs, which missing/5 then + %% uses when appending 5005 errors. + {reformat(Name, Rec, Strict, Mod, Fmt), Avps, - Failed ++ arities(Arities, Opts, Mod, AM, Avps)}. + Failed ++ missing(Name, Strict, Mod, Opts, AM)}. %% Append arity errors so that errors are reported in the order %% encountered. Failed-AVP should typically contain the first %% error encountered. -%% mapfoldl/3 -%% -%% Like lists:mapfoldl/3, but don't reverse the list. +%% decode/8 + +decode(<>, + Name, + Mod, + Fmt, + Strict, + Opts0, + Idx, + AM0) -> + Vid = if 1 == V -> I; true -> undefined end, + MB = 1 == M, + PB = 1 == P, + NameT = Mod:avp_name(Code, Vid), %% {AvpName, Type} | 'AVP' + DataLen = Len - 8 - 4*V, %% possibly negative, causing case match to fail + Pad = (4 - (Len rem 4)) rem 4, + + case Rest of + <> -> + Opts = setopts(NameT, Name, MB, Opts0), + %% Not AvpName or else a failed Failed-AVP + %% decode is packed into 'AVP'. + + Avp = #diameter_avp{code = Code, + vendor_id = Vid, + is_mandatory = MB, + need_encryption = PB, + data = Data, + name = name(NameT), + type = type(NameT), + index = Idx}, + + Dec = decode(Data, Name, NameT, Mod, Opts, Avp), %% decode + + AvpName = field(NameT), + Arity = avp_arity(Name, AvpName, Mod, Opts, Avp), + AM = incr(AvpName, Arity, Strict, AM0), %% count + + Acc = decode(T, Name, Mod, Fmt, Strict, Opts, Idx+1, AM),%% recurse + acc(Acc, Dec, Name, AvpName, Arity, Strict, Mod, Opts, AM0); + _ -> + Avp = #diameter_avp{code = Code, + vendor_id = Vid, + is_mandatory = MB, + need_encryption = PB, + data = Rest, + name = name(NameT), + type = type(NameT), + index = Idx}, + [AM0, [Avp], [{5014, Avp}] | newrec(Fmt, Mod, Name, Strict)] + end; + +decode(<<>>, Name, Mod, Fmt, Strict, _, _, AM) -> + [AM, [], [] | newrec(Fmt, Mod, Name, Strict)]; + +decode(Bin, Name, Mod, Fmt, Strict, _, Idx, AM) -> + Avp = #diameter_avp{data = Bin, + index = Idx}, + [AM, [Avp], [{5014, Avp}] | newrec(Fmt, Mod, Name, Strict)]. -mapfoldl(F, Acc, List) -> - mapfoldl(F, Acc, List, []). +%% Data is a truncated header if command_code = undefined, otherwise +%% payload bytes. The former is padded to the length of a header if +%% the AVP reaches an outgoing encode. +%% +%% RFC 6733 says that an AVP returned with 5014 can contain a minimal +%% payload for the AVP's type, but don't always know the type. -mapfoldl(F, Acc0, [T|Rest], List) -> - {B, Acc} = F(T, Acc0), - mapfoldl(F, Acc, Rest, [B|List]); -mapfoldl(_, Acc, [], List) -> - {List, Acc}. +setopts('AVP', _, _, Opts) -> + Opts; -%% 3588/6733: -%% -%% DIAMETER_MISSING_AVP 5005 -%% The request did not contain an AVP that is required by the Command -%% Code definition. If this value is sent in the Result-Code AVP, a -%% Failed-AVP AVP SHOULD be included in the message. The Failed-AVP -%% AVP MUST contain an example of the missing AVP complete with the -%% Vendor-Id if applicable. The value field of the missing AVP -%% should be of correct minimum length and contain zeros. -%% -%% DIAMETER_AVP_OCCURS_TOO_MANY_TIMES 5009 -%% A message was received that included an AVP that appeared more -%% often than permitted in the message definition. The Failed-AVP -%% AVP MUST be included and contain a copy of the first instance of -%% the offending AVP that exceeded the maximum number of occurrences +setopts({_, Type}, Name, M, Opts) -> + set_failed(Name, set_strict(Type, M, Opts)). -%% arities/5 +%% incr/4 -arities(_, #{strict_arities := T}, _, _, _) - when T /= decode -> - []; +incr(F, A, SA, AM) + when F == 'AVP'; + A == ?ANY; + A == 0; + SA /= decode -> + AM; -arities(Arities, Opts, Mod, AM, Avps) -> - [Count, Map | More] - = lists:foldl(fun({N,T}, A) -> more(N, T, Opts, Mod, AM, A) end, - [0, #{}], - Arities), - less(Count, Map, Avps) ++ lists:reverse(More). +incr(AvpName, _, _, AM) -> + maps:update_with(AvpName, fun incr/1, 1, AM). -%% more/6 +%% incr/1 -more(_Name, ?ANY, _Opts, _Mod, _AM, Acc) -> - Acc; +incr(N) -> + N + 1. -more(Name, {Mn, Mx}, Opts, Mod, AM, Acc) -> - more(Name, Mn, Mx, Opts, Mod, AM, Acc); +%% mget/3 +%% +%% Measurably faster than maps:get/3. -more(Name, 1, Opts, Mod, AM, Acc) -> - more(Name, 1, 1, Opts, Mod, AM, Acc). +mget(Key, Map, Def) -> + case Map of + #{Key := V} -> + V; + _ -> + Def + end. -%% more/7 +%% name/1 -more(Name, Mn, Mx, Opts, Mod, AM, Acc) -> - macc(Name, Mn, maps:get(Name, AM, 0), Mx, Opts, Mod, Acc). +name({Name, _}) -> + Name; +name(_) -> + undefined. -%% macc/7 +%% type/1 -macc(Name, Mn, N, _, Opts, Mod, [M, Map | T]) - when N < Mn -> - [M, Map, {5005, empty_avp(Name, Opts, Mod)} | T]; +type({_, Type}) -> + Type; +type(_) -> + undefined. -macc(Name, _, N, Mx, _Opts, _Mod, [M, Map | T]) - when Mx < N -> - K = N - Mx, - [M + K, maps:put(Name, K, Map) | T]; +%% missing/5 -macc(_Name, _, _, _, _Opts, _Mod, Acc) -> - Acc. +missing(Name, decode, Mod, Opts, AM) -> + [{5005, empty_avp(N, Opts, Mod)} || {N,A} <- Mod:avp_arity(Name), + N /= 'AVP', + Mn <- [min_arity(A)], + 0 < Mn, + mget(N, AM, 0) < Mn]; -%% less/3 +missing(_, _, _, _, _) -> + []. -less(0, _, _) -> - []; +%% 3588/6733: +%% +%% DIAMETER_MISSING_AVP 5005 +%% The request did not contain an AVP that is required by the Command +%% Code definition. If this value is sent in the Result-Code AVP, a +%% Failed-AVP AVP SHOULD be included in the message. The Failed-AVP +%% AVP MUST contain an example of the missing AVP complete with the +%% Vendor-Id if applicable. The value field of the missing AVP +%% should be of correct minimum length and contain zeros. -less(N, Map, [#diameter_avp{name = undefined} | Avps]) -> - less(N, Map, Avps); +%% min_arity/1 -less(N, Map, [#diameter_avp{name = Name} = Avp | Avps]) -> - case Map of - #{Name := 0} -> - [{5009, Avp} | less(N-1, Map, Avps)]; - #{Name := M} -> - less(N, maps:put(Name, M-1, Map), Avps); - _ -> - less(N, Map, Avps) - end. +min_arity(1) -> + 1; +min_arity({Mn,_}) -> + Mn. %% empty_avp/3 @@ -356,55 +409,18 @@ empty_avp(Name, Opts, Mod) -> %% specific errors that can be described by this AVP are described in %% the following section. -%% decode/5 - -decode(Name, Opts, Mod, Avp, Acc) -> - #diameter_avp{code = Code, vendor_id = Vid} - = Avp, - N = Mod:avp_name(Code, Vid), - case Opts of - #{strict_arities := T} when T /= decode -> - decode(Name, Opts, Mod, N, ?ANY, Avp, Acc); - _ -> - {Rec, AM, Failed} = Acc, - F = field(N), - A = Mod:avp_arity(Name, F), - decode(Name, Opts, Mod, N, A, Avp, {Rec, - incr(field(F, A), AM), - Failed}) - end. - %% field/1 field({AvpName, _}) -> AvpName; - field(_) -> 'AVP'. -%% field/2 - -field(_, 0) -> - 'AVP'; - -field(F, _) -> - F. - -%% incr/2 - -incr(Key, Map) -> - maps:update_with(Key, fun incr/1, 1, Map). - -%% incr/1 - -incr(N) -> - N + 1. - -%% decode/7 +%% decode/6 %% AVP not in dictionary. -decode(Name, Opts, Mod, 'AVP', Arity, Avp, Acc) -> - decode_AVP(Name, Arity, Avp, Opts, Mod, Acc); +decode(_Data, _Name, 'AVP', _Mod, _Opts, Avp) -> + Avp; %% 6733, 4.4: %% @@ -453,20 +469,9 @@ decode(Name, Opts, Mod, 'AVP', Arity, Avp, Acc) -> %% defined the RFC's "unrecognized", which is slightly stronger than %% "not defined".) -decode(Name, Opts0, Mod, {AvpName, Type}, Arity, Avp, Acc) -> - #diameter_avp{data = Data, is_mandatory = M} - = Avp, - - %% Whether or not to ignore an M-bit on an encapsulated AVP, or on - %% all AVPs with the service_opt() strict_mbit. - Opts1 = set_strict(Type, M, Opts0), - - %% Whether or not we're decoding within Failed-AVP and should - %% ignore decode errors. +decode(Data, Name, {AvpName, Type}, Mod, Opts, Avp) -> #{dictionary := AppMod, failed_avp := Failed} - = Opts - = set_failed(Name, Opts1), %% Not AvpName or else a failed Failed-AVP - %% decode is packed into 'AVP'. + = Opts, %% Reset the dictionary for best-effort decode of Failed-AVP. DecMod = if Failed -> AppMod; @@ -479,103 +484,55 @@ decode(Name, Opts0, Mod, {AvpName, Type}, Arity, Avp, Acc) -> try avp_decode(Data, AvpName, Opts, DecMod, Mod) of {Rec, As} when Type == 'Grouped' -> - A = Avp#diameter_avp{name = AvpName, - value = Rec, - type = Type}, - {[A|As], pack_avp(Name, Arity, A, Opts, Mod, Acc)}; - + A = Avp#diameter_avp{value = Rec}, + [A | As]; V when Type /= 'Grouped' -> - A = Avp#diameter_avp{name = AvpName, - value = V, - type = Type}, - {A, pack_avp(Name, Arity, A, Opts, Mod, Acc)} + Avp#diameter_avp{value = V} catch - throw: {?MODULE, {grouped, Error, ComponentAvps}} -> - decode_error(Name, - Error, - ComponentAvps, - Opts, - Mod, - Avp#diameter_avp{name = AvpName, - data = trim(Avp#diameter_avp.data), - type = Type}, - Acc); - + throw: {?MODULE, T} -> + decode_error(Failed, T, Avp); error: Reason -> - decode_error(Name, - Reason, - Opts, - Mod, - Avp#diameter_avp{name = AvpName, - data = trim(Avp#diameter_avp.data), - type = Type}, - Acc) + decode_error(Failed, Reason, Name, Mod, Opts, Avp) end. -%% avp_decode/5 - -avp_decode(Data, AvpName, Opts, Mod, Mod) -> - Mod:avp(decode, Data, AvpName, Opts); - -avp_decode(Data, AvpName, Opts, Mod, _) -> - Mod:avp(decode, Data, AvpName, Opts, Mod). - -%% trim/1 +%% decode_error/3 %% -%% Remove any extra bit that was added in diameter_codec to induce a -%% 5014 error. - -trim(#diameter_avp{data = Data} = Avp) -> - Avp#diameter_avp{data = trim(Data)}; - -trim({5014, Bin}) -> - Bin; - -trim(Avps) - when is_list(Avps) -> - lists:map(fun trim/1, Avps); - -trim(Avp) -> - Avp. - -%% decode_error/7 - -decode_error(Name, [_|Rec], _, #{failed_avp := true} = Opts, Mod, Avp, Acc) -> - decode_AVP(Name, Avp#diameter_avp{value = Rec}, Opts, Mod, Acc); - -decode_error(Name, _, _, #{failed_avp := true} = Opts, Mod, Avp, Acc) -> - decode_AVP(Name, Avp, Opts, Mod, Acc); +%% Error when decoding a grouped AVP. -decode_error(_, [Error | _], ComponentAvps, _, _, Avp, Acc) -> - decode_error(Error, Avp, Acc, ComponentAvps); +decode_error(true, {Rec, _, _}, Avp) -> + Avp#diameter_avp{value = Rec}; -decode_error(_, Error, ComponentAvps, _, _, Avp, Acc) -> - decode_error(Error, Avp, Acc, ComponentAvps). +decode_error(false, {_, ComponentAvps, [{RC,A} | _]}, Avp) -> + {RC, [Avp | ComponentAvps], Avp#diameter_avp{data = [A]}}. %% decode_error/6 +%% +%% Error when decoding a non-grouped AVP. -decode_error(Name, _Reason, #{failed_avp := true} = Opts, Mod, Avp, Acc) -> - decode_AVP(Name, Avp, Opts, Mod, Acc); +decode_error(true, _, _, _, _, Avp) -> + Avp; -decode_error(Name, Reason, Opts, Mod, Avp, {Rec, AM, Failed}) -> +decode_error(false, Reason, Name, Mod, Opts, Avp) -> Stack = diameter_lib:get_stacktrace(), - AvpName = Avp#diameter_avp.name, diameter_lib:log(decode_error, ?MODULE, ?LINE, - {Reason, Name, AvpName, Mod, Stack}), - {Avp, {Rec, AM, [rc(Reason, Avp, Opts, Mod) | Failed]}}. + {Reason, Name, Avp#diameter_avp.name, Mod, Stack}), + rc(Reason, Avp, Opts, Mod). -%% decode_error/4 +%% avp_decode/5 + +avp_decode(Data, AvpName, Opts, Mod, Mod) -> + Mod:avp(decode, Data, AvpName, Opts); -decode_error({RC, ErrorData}, Avp, {Rec, AM, Failed}, ComponentAvps) -> - E = Avp#diameter_avp{data = [ErrorData]}, - {[Avp | trim(ComponentAvps)], {Rec, AM, [{RC, E} | Failed]}}. +avp_decode(Data, AvpName, Opts, Mod, _) -> + Mod:avp(decode, Data, AvpName, Opts, Mod). %% set_strict/3 - +%% %% Set false as soon as we see a Grouped AVP that doesn't set the %% M-bit, to ignore the M-bit on an encapsulated AVP. + set_strict('Grouped', false = M, #{strict_mbit := true} = Opts) -> Opts#{strict_mbit := M}; set_strict(_, _, Opts) -> @@ -592,86 +549,82 @@ set_failed('Failed-AVP', #{failed_avp := false} = Opts) -> set_failed(_, Opts) -> Opts. -%% decode_AVP/5 -%% -%% Don't know this AVP: see if it can be packed in an 'AVP' field -%% undecoded. Note that the type field is 'undefined' in this case. - -decode_AVP(Name, Avp, #{strict_arities := T} = Opts, Mod, Acc) - when T /= decode -> - decode_AVP(Name, ?ANY, Avp, Opts, Mod, Acc); - -decode_AVP(Name, Avp, Opts, Mod, Acc) -> - decode_AVP(Name, Mod:avp_arity(Name, 'AVP'), Avp, Opts, Mod, Acc). - -%% decode_AVP/6 - -decode_AVP(Name, Arity, Avp, Opts, Mod, Acc) -> - {trim(Avp), pack_AVP(Name, Arity, Avp, Opts, Mod, Acc)}. - -%% rc/2 - -%% diameter_types will raise an error of this form to communicate -%% DIAMETER_INVALID_AVP_LENGTH (5014). A module specified to a -%% @custom_types tag in a dictionary file can also raise an error of -%% this form. -rc({'DIAMETER', 5014 = RC, _}, #diameter_avp{name = AvpName} = A, Opts, Mod) -> - {RC, A#diameter_avp{data = Mod:empty_value(AvpName, Opts)}}; +%% acc/9 + +acc([AM | Acc], As, Name, AvpName, Arity, Strict, Mod, Opts, AM0) -> + [AM | acc1(Acc, As, Name, AvpName, Arity, Strict, Mod, Opts, AM0)]. + +%% acc1/9 + +%% Faulty AVP, not grouped. +acc1(Acc, {_RC, Avp} = E, _, _, _, _, _, _, _) -> + [Avps, Failed | Rec] = Acc, + [[Avp | Avps], [E | Failed] | Rec]; + +%% Faulty component in grouped AVP. +acc1(Acc, {RC, As, Avp}, _, _, _, _, _, _, _) -> + [Avps, Failed | Rec] = Acc, + [[As | Avps], [{RC, Avp} | Failed] | Rec]; + +%% Grouped AVP ... +acc1([Avps | Acc], [Avp|_] = As, Name, AvpName, Arity, Strict, Mod, Opts, AM)-> + [[As|Avps] | acc2(Acc, Avp, Name, AvpName, Arity, Strict, Mod, Opts, AM)]; + +%% ... or not. +acc1([Avps | Acc], Avp, Name, AvpName, Arity, Strict, Mod, Opts, AM) -> + [[Avp|Avps] | acc2(Acc, Avp, Name, AvpName, Arity, Strict, Mod, Opts, AM)]. + +%% acc2/9 + +%% No errors, but nowhere to pack. +acc2(Acc, Avp, _, 'AVP', 0, _, _, _, _) -> + [Failed | Rec] = Acc, + [[{rc(Avp), Avp} | Failed] | Rec]; + +%% No AVP of this name: try to pack as 'AVP'. +acc2(Acc, Avp, Name, _, 0, Strict, Mod, Opts, AM) -> + Arity = pack_arity(Name, Opts, Mod, Avp), + acc2(Acc, Avp, Name, 'AVP', Arity, Strict, Mod, Opts, AM); + +%% Relaxed arities. +acc2(Acc, Avp, _, AvpName, Arity, Strict, Mod, _, _) + when Strict /= decode -> + pack(Arity, AvpName, Avp, Mod, Acc); + +%% No maximum arity. +acc2(Acc, Avp, _, AvpName, {_,'*'} = Arity, _, Mod, _, _) -> + pack(Arity, AvpName, Avp, Mod, Acc); + +%% Or check. +acc2(Acc, Avp, _, AvpName, Arity, _, Mod, _, AM) -> + Count = maps:get(AvpName, AM, 0), + Mx = max_arity(Arity), + if Mx =< Count -> + [Failed | Rec] = Acc, + [[{5009, Avp} | Failed] | Rec]; + true -> + pack(Arity, AvpName, Avp, Mod, Acc) + end. -%% 3588: +%% 3588/6733: %% -%% DIAMETER_INVALID_AVP_VALUE 5004 -%% The request contained an AVP with an invalid value in its data -%% portion. A Diameter message indicating this error MUST include -%% the offending AVPs within a Failed-AVP AVP. -rc(_, Avp, _, _) -> - {5004, Avp}. - -%% pack_avp/6 - -pack_avp(Name, 0, Avp, Opts, Mod, Acc) -> - pack_AVP(Name, Avp, Opts, Mod, Acc); - -pack_avp(_, Arity, #diameter_avp{name = AvpName} = Avp, _Opts, Mod, Acc) -> - pack(Arity, AvpName, Avp, Mod, Acc). - -%% pack_AVP/5 +%% DIAMETER_AVP_OCCURS_TOO_MANY_TIMES 5009 +%% A message was received that included an AVP that appeared more +%% often than permitted in the message definition. The Failed-AVP +%% AVP MUST be included and contain a copy of the first instance of +%% the offending AVP that exceeded the maximum number of occurrences -pack_AVP(Name, Avp, #{strict_arities := T} = Opts, Mod, Acc) - when T /= decode -> - pack_AVP(Name, ?ANY, Avp, Opts, Mod, Acc); +%% max_arity/1 -pack_AVP(Name, Avp, Opts, Mod, Acc) -> - pack_AVP(Name, Mod:avp_arity(Name, 'AVP'), Avp, Opts, Mod, Acc). +max_arity(1) -> + 1; +max_arity({_,Mx}) -> + Mx. -%% pack_AVP/6 +%% rc/1 -%% Length failure was induced because of a header/payload length -%% mismatch. The AVP Length is reset to match the received data if -%% this AVP is encoded in an answer message, since the length is -%% computed. -%% -%% Data is a truncated header if command_code = undefined, otherwise -%% payload bytes. The former is padded to the length of a header if -%% the AVP reaches an outgoing encode in diameter_codec. -%% -%% RFC 6733 says that an AVP returned with 5014 can contain a minimal -%% payload for the AVP's type, but in this case we don't know the -%% type. - -pack_AVP(_, _, #diameter_avp{data = {5014 = RC, Data}} = Avp, _, _, Acc) -> - {Rec, AM, Failed} = Acc, - {Rec, AM, [{RC, Avp#diameter_avp{data = Data}} | Failed]}; - -pack_AVP(Name, Arity, Avp, Opts, Mod, Acc) -> - case pack_AVP(Name, Opts, Arity, Avp) of - false -> - M = Avp#diameter_avp.is_mandatory, - {Rec, AM, Failed} = Acc, - {Rec, AM, [{if M -> 5001; true -> 5008 end, Avp} | Failed]}; - true -> - pack(Arity, 'AVP', Avp, Mod, Acc) - end. +rc(#diameter_avp{is_mandatory = M}) -> + if M -> 5001; true -> 5008 end. %% 3588: %% @@ -686,42 +639,59 @@ pack_AVP(Name, Arity, Avp, Opts, Mod, Acc) -> %% Failed-AVP AVP MUST be included and contain a copy of the %% offending AVP. -%% pack_AVP/4 +%% pack_arity/4 %% Give Failed-AVP special treatment since (1) it'll contain any %% unrecognized mandatory AVP's and (2) the RFC 3588 grammar failed to %% allow for Failed-AVP in an answer-message. -pack_AVP(_, #{strict_arities := T}, _, _) - when T /= decode -> - true; +pack_arity(Name, _, Mod, #diameter_avp{is_mandatory = M, name = AvpName}) + when Name == 'Failed-AVP'; + Name == 'answer-message', AvpName == 'Failed-AVP'; + not M -> + Mod:avp_arity(Name, 'AVP'); +%% Not testing just Name /= 'Failed-AVP' means we're changing the +%% packing of AVPs nested within Failed-AVP, but the point of +%% ignoring errors within Failed-AVP is to decode as much as +%% possible, and failing because a mandatory AVP couldn't be +%% packed into a dedicated field defeats that point. -pack_AVP(_, _, 0, _) -> - false; +pack_arity(Name, #{strict_mbit := Strict, failed_avp := Failed}, Mod, _) + when not Strict; + Failed -> + Mod:avp_arity(Name, 'AVP'); + +pack_arity(_, _, _, _) -> + 0. -pack_AVP(Name, - #{strict_mbit := Strict, - failed_avp := Failed}, - _, - #diameter_avp{is_mandatory = M, - name = AvpName}) -> - - %% Not testing just Name /= 'Failed-AVP' means we're changing the - %% packing of AVPs nested within Failed-AVP, but the point of - %% ignoring errors within Failed-AVP is to decode as much as - %% possible, and failing because a mandatory AVP couldn't be - %% packed into a dedicated field defeats that point. - - Failed == true - orelse Name == 'Failed-AVP' - orelse (Name == 'answer-message' andalso AvpName == 'Failed-AVP') - orelse not M - orelse not Strict. +%% avp_arity/5 + +avp_arity(Name, 'AVP', Mod, Opts, Avp) -> + pack_arity(Name, Opts, Mod, Avp); + +avp_arity(Name, AvpName, Mod, _, _) -> + Mod:avp_arity(Name, AvpName). + +%% rc/4 + +%% Length error communicated from diameter_types or a +%% @custom_types/@codecs module. +rc({'DIAMETER', 5014 = RC, _}, #diameter_avp{name = AvpName} = A, Opts, Mod) -> + {RC, A#diameter_avp{data = Mod:empty_value(AvpName, Opts)}}; + +%% 3588: +%% +%% DIAMETER_INVALID_AVP_VALUE 5004 +%% The request contained an AVP with an invalid value in its data +%% portion. A Diameter message indicating this error MUST include +%% the offending AVPs within a Failed-AVP AVP. +rc(_, Avp, _, _) -> + {5004, Avp}. %% pack/5 -pack(Arity, F, Avp, Mod, {Rec, AM, Failed}) -> - {set(Arity, F, value(F, Avp), Mod, Rec), AM, Failed}. +pack(Arity, F, Avp, Mod, [Failed | Rec]) -> + [Failed | set(Arity, F, value(F, Avp), Mod, Rec)]. %% set/5 @@ -730,7 +700,7 @@ set(_, _, _, _, false = No) -> set(1, F, Value, _, Map) when is_map(Map) -> - maps:put(F, Value, Map); + Map#{F => Value}; set(_, F, V, _, Map) when is_map(Map) -> @@ -755,36 +725,28 @@ value(_, #diameter_avp{value = V}) -> %% # grouped_avp/3 %% --------------------------------------------------------------------------- --spec grouped_avp(decode, avp_name(), binary() | {5014, binary()}, term()) +%% Note that Grouped is the only AVP type that doesn't just return a +%% decoded value, also returning the list of component diameter_avp +%% records. + +-spec grouped_avp(decode, avp_name(), binary(), term()) -> {avp_record(), [avp()]}; (encode, avp_name(), avp_record() | avp_values(), term()) -> iolist() | no_return(). -%% Length error induced by diameter_codec:collect_avps/1: the AVP -%% length in the header was too short (insufficient for the extracted -%% header) or too long (past the end of the message). An empty payload -%% is sufficient according to the RFC text for 5014. -grouped_avp(decode, _Name, {5014 = RC, _Bin}, _) -> - ?THROW({grouped, {RC, []}, []}); - -grouped_avp(decode, Name, Data, Opts) -> - grouped_decode(Name, diameter_codec:collect_avps(Data), Opts); +%% An error in decoding a component AVP throws the first faulty +%% component, which a catch wraps in the Grouped AVP in question. A +%% partially decoded record is only used when ignoring errors in +%% Failed-AVP. +grouped_avp(decode, Name, Bin, Opts) -> + {Rec, Avps, Es} = T = decode_avps(Name, Bin, Opts), + [] == Es orelse ?THROW(T), + {Rec, Avps}; grouped_avp(encode, Name, Data, Opts) -> encode_avps(Name, Data, Opts). -%% grouped_decode/2 -%% -%% Note that Grouped is the only AVP type that doesn't just return a -%% decoded value, also returning the list of component diameter_avp -%% records. - -%% Length error in trailing component AVP. -grouped_decode(_Name, {Error, Acc}, _) -> - {5014, Avp} = Error, - ?THROW({grouped, Error, [Avp | Acc]}); - %% 7.5. Failed-AVP AVP %% In the case where the offending AVP is embedded within a Grouped AVP, @@ -795,15 +757,6 @@ grouped_decode(_Name, {Error, Acc}, _) -> %% to the single offending AVP. This enables the recipient to detect %% the location of the offending AVP when embedded in a group. -%% An error in decoding a component AVP throws the first faulty -%% component, which the catch in d/3 wraps in the Grouped AVP in -%% question. A partially decoded record is only used when ignoring -%% errors in Failed-AVP. -grouped_decode(Name, ComponentAvps, Opts) -> - {Rec, Avps, Es} = decode_avps(Name, ComponentAvps, Opts), - [] == Es orelse ?THROW({grouped, [{_,_} = hd(Es) | Rec], Avps}), - {Rec, Avps}. - %% --------------------------------------------------------------------------- %% # empty_group/2 %% --------------------------------------------------------------------------- @@ -840,7 +793,7 @@ empty(Name, #{module := Mod} = Opts) -> newrec(false = No, _, _, _) -> No; -newrec(record, Mod, Name, #{strict_arities := T}) +newrec(record, Mod, Name, T) when T /= decode -> RecName = Mod:name2rec(Name), Sz = Mod:'#info-'(RecName, size), @@ -859,16 +812,15 @@ newrec(Mod, Name) -> %% reformat/5 -reformat(_, Map, Arities, _Mod, _Opts, list) -> - [{F,V} || {F,_} <- Arities, #{F := V} <- [Map]]; +reformat(Name, Map, _Strict, Mod, list) -> + [{F,V} || {F,_} <- Mod:avp_arity(Name), #{F := V} <- [Map]]; -reformat(Name, Map, Arities, Mod, Opts, record_from_map) -> - SA = maps:get(strict_arities, Opts, decode), +reformat(Name, Map, Strict, Mod, record_from_map) -> RecName = Mod:name2rec(Name), - list_to_tuple([RecName | [maps:get(F, Map, def(A, SA)) - || {F,A} <- Arities]]); + list_to_tuple([RecName | [mget(F, Map, def(A, Strict)) + || {F,A} <- Mod:avp_arity(Name)]]); -reformat(_, Rec, _, _, _, _) -> +reformat(_, Rec, _, _, _) -> Rec. %% def/2 diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index 3463a93568..3194cb4bf1 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -327,9 +327,7 @@ recv_request(Ack, %% A request was sent for an application that is not %% supported. RC = 3007, - Es = Pkt#diameter_packet.errors, - DecPkt = Pkt#diameter_packet{avps = collect_avps(Pkt), - errors = [RC | Es]}, + DecPkt = diameter_codec:collect_avps(Pkt), send_answer(answer_message(RC, Dict0, Caps, DecPkt), TPid, Dict0, @@ -345,14 +343,6 @@ recv_request(Ack, decode(Id, Dict, #recvdata{codec = Opts}, Pkt) -> errors(Id, diameter_codec:decode(Id, Dict, Opts, Pkt)). -collect_avps(Pkt) -> - case diameter_codec:collect_avps(Pkt) of - {_Error, Avps} -> - Avps; - Avps -> - Avps - end. - %% send_A/7 send_A([T | Fs], TPid, App, Dict0, RecvData, DecPkt, Caps) -> -- cgit v1.2.3 From 43b84071754a27894a44ffb0f34d4a03157ebeb5 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 3 Aug 2017 14:09:33 +0200 Subject: Don't extract options unnecessarily at encode Extract strict_arities once and pass it through as an argument. --- lib/diameter/src/base/diameter_gen.erl | 88 +++++++++++++++++----------------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index 3688ff7b8f..4bfad345d0 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -60,8 +60,9 @@ | no_return(). encode_avps(Name, Vals, #{module := Mod} = Opts) -> + Strict = mget(strict_arities, Opts, encode), try - encode(Name, Vals, Opts, Mod) + encode(Name, Vals, Opts, Strict, Mod) catch throw: {?MODULE, Reason} -> diameter_lib:log({encode, error}, @@ -78,87 +79,88 @@ encode_avps(Name, Vals, #{module := Mod} = Opts) -> erlang:error({encode_failure, Reason, Name, Stack}) end. -%% encode/4 - -encode(Name, Vals, #{ordered_encode := false} = Opts, Mod) - when is_list(Vals) -> - lists:map(fun({F,V}) -> encode(Name, F, V, Opts, Mod) end, Vals); +%% encode/5 -encode(Name, Vals, Opts, Mod) +encode(Name, Vals, Opts, Strict, Mod) when is_list(Vals) -> - encode(Name, Mod:'#set-'(Vals, newrec(Mod, Name)), Opts, Mod); + case Opts of + #{ordered_encode := false} -> + lists:map(fun({F,V}) -> encode(Name, F, V, Opts, Strict, Mod) end, + Vals); + _ -> + Rec = Mod:'#set-'(Vals, newrec(Mod, Name)), + encode(Name, Rec, Opts, Strict, Mod) + end; -encode(Name, Map, Opts, Mod) +encode(Name, Map, Opts, Strict, Mod) when is_map(Map) -> - [enc(Name, F, A, V, Opts, Mod) || {F,A} <- Mod:avp_arity(Name), - V <- [mget(F, Map, undefined)]]; + [enc(Name, F, A, V, Opts, Strict, Mod) || {F,A} <- Mod:avp_arity(Name), + V <- [mget(F, Map, undefined)]]; -encode(Name, Rec, Opts, Mod) -> - [encode(Name, F, V, Opts, Mod) || {F,V} <- Mod:'#get-'(Rec)]. +encode(Name, Rec, Opts, Strict, Mod) -> + [encode(Name, F, V, Opts, Strict, Mod) || {F,V} <- Mod:'#get-'(Rec)]. -%% encode/5 +%% encode/6 -encode(Name, AvpName, Values, #{strict_arities := T} = Opts, Mod) - when T /= encode -> - enc(Name, AvpName, ?ANY, Values, Opts, Mod); +encode(Name, AvpName, Values, Opts, Strict, Mod) + when Strict /= encode -> + enc(Name, AvpName, ?ANY, Values, Opts, Strict, Mod); -encode(Name, AvpName, Values, Opts, Mod) -> - enc(Name, AvpName, Mod:avp_arity(Name, AvpName), Values, Opts, Mod). +encode(Name, AvpName, Values, Opts, Strict, Mod) -> + Arity = Mod:avp_arity(Name, AvpName), + enc(Name, AvpName, Arity, Values, Opts, Strict, Mod). -%% enc/6 +%% enc/7 -enc(Name, AvpName, Arity, Values, #{strict_arities := T} = Opts, Mod) - when T /= encode, Arity /= ?ANY -> - enc(Name, AvpName, ?ANY, Values, Opts, Mod); +enc(Name, AvpName, Arity, Values, Opts, Strict, Mod) + when Strict /= encode, Arity /= ?ANY -> + enc(Name, AvpName, ?ANY, Values, Opts, Strict, Mod); -enc(_, AvpName, 1, undefined, _, _) -> +enc(_, AvpName, 1, undefined, _, _, _) -> ?THROW([mandatory_avp_missing, AvpName]); -enc(Name, AvpName, 1, Value, Opts, Mod) -> +enc(Name, AvpName, 1, Value, Opts, _, Mod) -> H = avp_header(AvpName, Mod), enc1(Name, AvpName, H, Value, Opts, Mod); -enc(_, _, {0,_}, [], _, _) -> +enc(_, _, {0,_}, [], _, _, _) -> []; -enc(_, _, _, undefined, _, _) -> +enc(_, _, _, undefined, _, _, _) -> []; %% Be forgiving when a list of values is expected. If the value itself %% is a list then the user has to wrap it to avoid each member from %% being interpreted as an individual AVP value. -enc(Name, AvpName, Arity, V, Opts, Mod) +enc(Name, AvpName, Arity, V, Opts, Strict, Mod) when not is_list(V) -> - enc(Name, AvpName, Arity, [V], Opts, Mod); + enc(Name, AvpName, Arity, [V], Opts, Strict, Mod); -enc(Name, AvpName, {Min, Max}, Values, Opts, Mod) -> +enc(Name, AvpName, {Min, Max}, Values, Opts, Strict, Mod) -> H = avp_header(AvpName, Mod), - enc(Name, AvpName, H, Min, 0, Max, Values, Opts, Mod). + enc(Name, AvpName, H, Min, 0, Max, Values, Opts, Strict, Mod). -%% enc/9 - -enc(Name, AvpName, H, Min, N, '*', Vs, Opts, Mod) - when Min =< N -> - [enc1(Name, AvpName, H, V, Opts, Mod) || V <- Vs]; +%% enc/10 -enc(Name, AvpName, H, _, _, _, Vs, #{strict_arities := T} = Opts, Mod) - when T /= encode -> +enc(Name, AvpName, H, Min, N, Max, Vs, Opts, Strict, Mod) + when Strict /= encode; + Max == '*', Min =< N -> [enc1(Name, AvpName, H, V, Opts, Mod) || V <- Vs]; -enc(_, AvpName, _, Min, N, _, [], _, _) +enc(_, AvpName, _, Min, N, _, [], _, _, _) when N < Min -> ?THROW([repeated_avp_insufficient_arity, AvpName, Min, N]); -enc(_, _, _, _, _, _, [], _, _) -> +enc(_, _, _, _, _, _, [], _, _, _) -> []; -enc(_, AvpName, _, _, N, Max, _, _, _) +enc(_, AvpName, _, _, N, Max, _, _, _, _) when Max =< N -> ?THROW([repeated_avp_excessive_arity, AvpName, Max]); -enc(Name, AvpName, H, Min, N, Max, [V|Vs], Opts, Mod) -> +enc(Name, AvpName, H, Min, N, Max, [V|Vs], Opts, Strict, Mod) -> [enc1(Name, AvpName, H, V, Opts, Mod) - | enc(Name, AvpName, H, Min, N+1, Max, Vs, Opts, Mod)]. + | enc(Name, AvpName, H, Min, N+1, Max, Vs, Opts, Strict, Mod)]. %% avp_header/2 -- cgit v1.2.3 From 96cd627acfde682875149effda4611dc778622fd Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 30 Jul 2017 01:27:42 +0200 Subject: Count AVPs in #diameter_avp.index The index field in record diameter_avp was previously used to enumerate AVPs so that the list could be returned in some cases, since diameter_codec:collect_avps/2 (now /1) reversed the order. That's no longer the case as of the grandparent commit, so use the field to enumerate instances of the same AVP instead, and only when arities are being checked, to save having to look them up in the map when checking for 5009 errors, or counting AVPs at all in diameter_codec:collect_avps/1. --- lib/diameter/src/base/diameter_codec.erl | 18 +++--- lib/diameter/src/base/diameter_gen.erl | 104 +++++++++++++++---------------- 2 files changed, 58 insertions(+), 64 deletions(-) diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index c179c4b362..81c21fb8f2 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -489,9 +489,9 @@ collect_avps(#diameter_packet{bin = Bin} = Pkt) -> Pkt#diameter_packet{avps = collect_avps(Bin)}; collect_avps(<<_:20/binary, Avps/binary>>) -> - collect(Avps, 0). + collect(Avps). -%% collect/2 +%% collect/1 %% 0 1 2 3 %% 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 @@ -505,7 +505,7 @@ collect_avps(<<_:20/binary, Avps/binary>>) -> %% | Data ... %% +-+-+-+-+-+-+-+-+ -collect(<>, N)-> +collect(<>) -> Vid = if 1 == V -> I; 0 == V -> undefined end, DataLen = Len - 8 - V*4, %% Might be negative, which ensures Pad = ?PAD(Len), %% failure of the match below. @@ -518,9 +518,8 @@ collect(<>, N)-> vendor_id = Vid, is_mandatory = MB, need_encryption = PB, - data = Data, - index = N}, - [Avp | collect(T, N+1)]; + data = Data}, + [Avp | collect(T)]; _ -> %% Length in header points past the end of the message, or %% doesn't span the header. Note that an length error can @@ -532,16 +531,15 @@ collect(<>, N)-> vendor_id = Vid, is_mandatory = MB, need_encryption = PB, - data = {5014, Rest}, - index = N}] + data = {5014, Rest}}] end; -collect(<<>>, _) -> +collect(<<>>) -> []; %% Header is truncated. pack_avp/1 will pad this at encode if sent in %% a Failed-AVP. -collect(Bin, _) -> +collect(Bin) -> [#diameter_avp{data = {5014, Bin}}]. %% 3588: diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index 4bfad345d0..f0196ccaa9 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -230,7 +230,7 @@ enc(AvpName, Value, Opts, Mod) -> decode_avps(Name, Bin, #{module := Mod, decode_format := Fmt} = Opts) -> Strict = mget(strict_arities, Opts, decode), [AM, Avps, Failed | Rec] - = decode(Bin, Name, Mod, Fmt, Strict, Opts, 0, #{}), + = decode(Bin, Name, Mod, Fmt, Strict, Opts, #{}), %% AM counts the number of top-level AVPs, which missing/5 then %% uses when appending 5005 errors. {reformat(Name, Rec, Strict, Mod, Fmt), @@ -241,7 +241,7 @@ decode_avps(Name, Bin, #{module := Mod, decode_format := Fmt} = Opts) -> %% encountered. Failed-AVP should typically contain the first %% error encountered. -%% decode/8 +%% decode/7 decode(<>, Name, @@ -249,7 +249,6 @@ decode(<>, Fmt, Strict, Opts0, - Idx, AM0) -> Vid = if 1 == V -> I; true -> undefined end, MB = 1 == M, @@ -258,12 +257,16 @@ decode(<>, DataLen = Len - 8 - 4*V, %% possibly negative, causing case match to fail Pad = (4 - (Len rem 4)) rem 4, + Opts = setopts(NameT, Name, MB, Opts0), + %% Not AvpName or else a failed Failed-AVP + %% decode is packed into 'AVP'. + + AvpName = field(NameT), + Arity = avp_arity(Name, AvpName, Mod, Opts, MB), + {Idx, AM} = incr(AvpName, Arity, Strict, AM0), %% count + case Rest of <> -> - Opts = setopts(NameT, Name, MB, Opts0), - %% Not AvpName or else a failed Failed-AVP - %% decode is packed into 'AVP'. - Avp = #diameter_avp{code = Code, vendor_id = Vid, is_mandatory = MB, @@ -272,15 +275,9 @@ decode(<>, name = name(NameT), type = type(NameT), index = Idx}, - - Dec = decode(Data, Name, NameT, Mod, Opts, Avp), %% decode - - AvpName = field(NameT), - Arity = avp_arity(Name, AvpName, Mod, Opts, Avp), - AM = incr(AvpName, Arity, Strict, AM0), %% count - - Acc = decode(T, Name, Mod, Fmt, Strict, Opts, Idx+1, AM),%% recurse - acc(Acc, Dec, Name, AvpName, Arity, Strict, Mod, Opts, AM0); + Dec = decode(Data, Name, NameT, Mod, Opts, Avp), %% decode + Acc = decode(T, Name, Mod, Fmt, Strict, Opts, AM), %% recurse + acc(Acc, Dec, Name, AvpName, Arity, Strict, Mod, Opts); _ -> Avp = #diameter_avp{code = Code, vendor_id = Vid, @@ -290,15 +287,14 @@ decode(<>, name = name(NameT), type = type(NameT), index = Idx}, - [AM0, [Avp], [{5014, Avp}] | newrec(Fmt, Mod, Name, Strict)] + [AM, [Avp], [{5014, Avp}] | newrec(Fmt, Mod, Name, Strict)] end; -decode(<<>>, Name, Mod, Fmt, Strict, _, _, AM) -> +decode(<<>>, Name, Mod, Fmt, Strict, _, AM) -> [AM, [], [] | newrec(Fmt, Mod, Name, Strict)]; -decode(Bin, Name, Mod, Fmt, Strict, _, Idx, AM) -> - Avp = #diameter_avp{data = Bin, - index = Idx}, +decode(Bin, Name, Mod, Fmt, Strict, _, AM) -> + Avp = #diameter_avp{data = Bin}, [AM, [Avp], [{5014, Avp}] | newrec(Fmt, Mod, Name, Strict)]. %% Data is a truncated header if command_code = undefined, otherwise @@ -321,15 +317,15 @@ incr(F, A, SA, AM) A == ?ANY; A == 0; SA /= decode -> - AM; + {undefined, AM}; incr(AvpName, _, _, AM) -> - maps:update_with(AvpName, fun incr/1, 1, AM). - -%% incr/1 - -incr(N) -> - N + 1. + case AM of + #{AvpName := N} -> + {N, AM#{AvpName => N+1}}; + _ -> + {0, AM#{AvpName => 1}} + end. %% mget/3 %% @@ -551,57 +547,57 @@ set_failed('Failed-AVP', #{failed_avp := false} = Opts) -> set_failed(_, Opts) -> Opts. -%% acc/9 +%% acc/8 -acc([AM | Acc], As, Name, AvpName, Arity, Strict, Mod, Opts, AM0) -> - [AM | acc1(Acc, As, Name, AvpName, Arity, Strict, Mod, Opts, AM0)]. +acc([AM | Acc], As, Name, AvpName, Arity, Strict, Mod, Opts) -> + [AM | acc1(Acc, As, Name, AvpName, Arity, Strict, Mod, Opts)]. -%% acc1/9 +%% acc1/8 %% Faulty AVP, not grouped. -acc1(Acc, {_RC, Avp} = E, _, _, _, _, _, _, _) -> +acc1(Acc, {_RC, Avp} = E, _, _, _, _, _, _) -> [Avps, Failed | Rec] = Acc, [[Avp | Avps], [E | Failed] | Rec]; %% Faulty component in grouped AVP. -acc1(Acc, {RC, As, Avp}, _, _, _, _, _, _, _) -> +acc1(Acc, {RC, As, Avp}, _, _, _, _, _, _) -> [Avps, Failed | Rec] = Acc, [[As | Avps], [{RC, Avp} | Failed] | Rec]; %% Grouped AVP ... -acc1([Avps | Acc], [Avp|_] = As, Name, AvpName, Arity, Strict, Mod, Opts, AM)-> - [[As|Avps] | acc2(Acc, Avp, Name, AvpName, Arity, Strict, Mod, Opts, AM)]; +acc1([Avps | Acc], [Avp|_] = As, Name, AvpName, Arity, Strict, Mod, Opts) -> + [[As|Avps] | acc2(Acc, Avp, Name, AvpName, Arity, Strict, Mod, Opts)]; %% ... or not. -acc1([Avps | Acc], Avp, Name, AvpName, Arity, Strict, Mod, Opts, AM) -> - [[Avp|Avps] | acc2(Acc, Avp, Name, AvpName, Arity, Strict, Mod, Opts, AM)]. +acc1([Avps | Acc], Avp, Name, AvpName, Arity, Strict, Mod, Opts) -> + [[Avp|Avps] | acc2(Acc, Avp, Name, AvpName, Arity, Strict, Mod, Opts)]. -%% acc2/9 +%% acc2/8 %% No errors, but nowhere to pack. -acc2(Acc, Avp, _, 'AVP', 0, _, _, _, _) -> +acc2(Acc, Avp, _, 'AVP', 0, _, _, _) -> [Failed | Rec] = Acc, [[{rc(Avp), Avp} | Failed] | Rec]; %% No AVP of this name: try to pack as 'AVP'. -acc2(Acc, Avp, Name, _, 0, Strict, Mod, Opts, AM) -> - Arity = pack_arity(Name, Opts, Mod, Avp), - acc2(Acc, Avp, Name, 'AVP', Arity, Strict, Mod, Opts, AM); +acc2(Acc, Avp, Name, AvpName, 0, Strict, Mod, Opts) -> + M = Avp#diameter_avp.is_mandatory, + Arity = pack_arity(Name, AvpName, Opts, Mod, M), + acc2(Acc, Avp, Name, 'AVP', Arity, Strict, Mod, Opts); %% Relaxed arities. -acc2(Acc, Avp, _, AvpName, Arity, Strict, Mod, _, _) +acc2(Acc, Avp, _, AvpName, Arity, Strict, Mod, _) when Strict /= decode -> pack(Arity, AvpName, Avp, Mod, Acc); %% No maximum arity. -acc2(Acc, Avp, _, AvpName, {_,'*'} = Arity, _, Mod, _, _) -> +acc2(Acc, Avp, _, AvpName, {_,'*'} = Arity, _, Mod, _) -> pack(Arity, AvpName, Avp, Mod, Acc); %% Or check. -acc2(Acc, Avp, _, AvpName, Arity, _, Mod, _, AM) -> - Count = maps:get(AvpName, AM, 0), +acc2(Acc, Avp, _, AvpName, Arity, _, Mod, _) -> Mx = max_arity(Arity), - if Mx =< Count -> + if Mx =< Avp#diameter_avp.index -> [Failed | Rec] = Acc, [[{5009, Avp} | Failed] | Rec]; true -> @@ -641,13 +637,13 @@ rc(#diameter_avp{is_mandatory = M}) -> %% Failed-AVP AVP MUST be included and contain a copy of the %% offending AVP. -%% pack_arity/4 +%% pack_arity/5 %% Give Failed-AVP special treatment since (1) it'll contain any %% unrecognized mandatory AVP's and (2) the RFC 3588 grammar failed to %% allow for Failed-AVP in an answer-message. -pack_arity(Name, _, Mod, #diameter_avp{is_mandatory = M, name = AvpName}) +pack_arity(Name, AvpName, _, Mod, M) when Name == 'Failed-AVP'; Name == 'answer-message', AvpName == 'Failed-AVP'; not M -> @@ -658,18 +654,18 @@ pack_arity(Name, _, Mod, #diameter_avp{is_mandatory = M, name = AvpName}) %% possible, and failing because a mandatory AVP couldn't be %% packed into a dedicated field defeats that point. -pack_arity(Name, #{strict_mbit := Strict, failed_avp := Failed}, Mod, _) +pack_arity(Name, _, #{strict_mbit := Strict, failed_avp := Failed}, Mod, _) when not Strict; Failed -> Mod:avp_arity(Name, 'AVP'); -pack_arity(_, _, _, _) -> +pack_arity(_, _, _, _, _) -> 0. %% avp_arity/5 -avp_arity(Name, 'AVP', Mod, Opts, Avp) -> - pack_arity(Name, Opts, Mod, Avp); +avp_arity(Name, 'AVP' = AvpName, Mod, Opts, M) -> + pack_arity(Name, AvpName, Opts, Mod, M); avp_arity(Name, AvpName, Mod, _, _) -> Mod:avp_arity(Name, AvpName). -- cgit v1.2.3 From 1a2c87f0035849fc5a24bff5dd36796500d1f451 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sat, 29 Jul 2017 16:53:18 +0200 Subject: Optimize sub-binaries With ERL_COMPILER_OPTIONS=bin_opt_info, before: base/diameter_codec.erl:508: Warning: NOT OPTIMIZED: the binary matching instruction that follows in the same function have problems that prevent delayed sub binary optimization (probably indicated by INFO warnings) And after: base/diameter_codec.erl:508: Warning: OPTIMIZED: creation of sub binary delayed This has a surprisingly large impact on the performance of diameter_codec:collect_avps/2: about 15% faster in one testcase on a RAR with 7 AVPs. --- lib/diameter/src/base/diameter_codec.erl | 51 ++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index 81c21fb8f2..c171ea9dca 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -506,20 +506,33 @@ collect_avps(<<_:20/binary, Avps/binary>>) -> %% +-+-+-+-+-+-+-+-+ collect(<>) -> - Vid = if 1 == V -> I; 0 == V -> undefined end, - DataLen = Len - 8 - V*4, %% Might be negative, which ensures - Pad = ?PAD(Len), %% failure of the match below. - MB = 1 == M, - PB = 1 == P, - - case Rest of - <> -> + collect(Rest, + Code, + if 1 == V -> I; 0 == V -> undefined end, + Len - 8 - V*4, %% Might be negative, which ensures + ?PAD(Len), %% failure of the match below. + 1 == M, + 1 == P); + +collect(<<>>) -> + []; + +%% Header is truncated. pack_avp/1 will pad this at encode if sent in +%% a Failed-AVP. +collect(Bin) -> + [#diameter_avp{data = {5014, Bin}}]. + +%% collect/7 + +collect(Bin, Code, Vid, DataLen, Pad, M, P) -> + case Bin of + <> -> Avp = #diameter_avp{code = Code, vendor_id = Vid, - is_mandatory = MB, - need_encryption = PB, + is_mandatory = M, + need_encryption = P, data = Data}, - [Avp | collect(T)]; + [Avp | collect(Rest)]; _ -> %% Length in header points past the end of the message, or %% doesn't span the header. Note that an length error can @@ -529,18 +542,10 @@ collect(<>) -> %% know the types of the AVPs being extracted. [#diameter_avp{code = Code, vendor_id = Vid, - is_mandatory = MB, - need_encryption = PB, - data = {5014, Rest}}] - end; - -collect(<<>>) -> - []; - -%% Header is truncated. pack_avp/1 will pad this at encode if sent in -%% a Failed-AVP. -collect(Bin) -> - [#diameter_avp{data = {5014, Bin}}]. + is_mandatory = M, + need_encryption = P, + data = {5014, Bin}}] + end. %% 3588: %% -- cgit v1.2.3 From bacca97210d31865c46b759115039b22f9ba9ed7 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sat, 29 Jul 2017 17:24:30 +0200 Subject: Optimize sub-binaries Also inline incr/8 and associated functions that were needed for the compiler to accept the optimization, since this does make for a measuable improvement. --- lib/diameter/src/base/diameter_gen.erl | 82 +++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 26 deletions(-) diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index f0196ccaa9..7a1a46ec52 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -26,6 +26,14 @@ -module(diameter_gen). +-compile({inline, [incr/8, + incr/4, + field/1, + setopts/4, + avp_arity/5, + set_failed/2, + set_strict/3]}). + -export([encode_avps/3, decode_avps/3, grouped_avp/4, @@ -248,54 +256,76 @@ decode(<>, Mod, Fmt, Strict, - Opts0, - AM0) -> - Vid = if 1 == V -> I; true -> undefined end, - MB = 1 == M, - PB = 1 == P, - NameT = Mod:avp_name(Code, Vid), %% {AvpName, Type} | 'AVP' - DataLen = Len - 8 - 4*V, %% possibly negative, causing case match to fail - Pad = (4 - (Len rem 4)) rem 4, + Opts, + AM) -> + decode(Rest, + Code, + if 1 == V -> I; true -> undefined end, + Len - 8 - 4*V, %% possibly negative, causing case match to fail + (4 - (Len rem 4)) rem 4, + 1 == M, + 1 == P, + Name, + Mod, + Fmt, + Strict, + Opts, + AM); - Opts = setopts(NameT, Name, MB, Opts0), - %% Not AvpName or else a failed Failed-AVP - %% decode is packed into 'AVP'. +decode(<<>>, Name, Mod, Fmt, Strict, _, AM) -> + [AM, [], [] | newrec(Fmt, Mod, Name, Strict)]; - AvpName = field(NameT), - Arity = avp_arity(Name, AvpName, Mod, Opts, MB), - {Idx, AM} = incr(AvpName, Arity, Strict, AM0), %% count +decode(Bin, Name, Mod, Fmt, Strict, _, AM) -> + Avp = #diameter_avp{data = Bin}, + [AM, [Avp], [{5014, Avp}] | newrec(Fmt, Mod, Name, Strict)]. + +%% decode/13 - case Rest of +decode(Bin, Code, Vid, DataLen, Pad, M, P, Name, Mod, Fmt, Strict, Opts0, AM0) -> + case Bin of <> -> + {NameT, AvpName, Arity, {Idx, AM}} + = incr(Name, Code, Vid, M, Mod, Strict, Opts0, AM0), + + Opts = setopts(NameT, Name, M, Opts0), + %% Not AvpName or else a failed Failed-AVP + %% decode is packed into 'AVP'. + Avp = #diameter_avp{code = Code, vendor_id = Vid, - is_mandatory = MB, - need_encryption = PB, + is_mandatory = M, + need_encryption = P, data = Data, name = name(NameT), type = type(NameT), index = Idx}, + Dec = decode(Data, Name, NameT, Mod, Opts, Avp), %% decode Acc = decode(T, Name, Mod, Fmt, Strict, Opts, AM), %% recurse acc(Acc, Dec, Name, AvpName, Arity, Strict, Mod, Opts); _ -> + {NameT, _AvpName, _Arity, {Idx, AM}} + = incr(Name, Code, Vid, M, Mod, Strict, Opts0, AM0), + Avp = #diameter_avp{code = Code, vendor_id = Vid, - is_mandatory = MB, - need_encryption = PB, - data = Rest, + is_mandatory = M, + need_encryption = P, + data = Bin, name = name(NameT), type = type(NameT), index = Idx}, + [AM, [Avp], [{5014, Avp}] | newrec(Fmt, Mod, Name, Strict)] - end; + end. -decode(<<>>, Name, Mod, Fmt, Strict, _, AM) -> - [AM, [], [] | newrec(Fmt, Mod, Name, Strict)]; +%% incr/8 -decode(Bin, Name, Mod, Fmt, Strict, _, AM) -> - Avp = #diameter_avp{data = Bin}, - [AM, [Avp], [{5014, Avp}] | newrec(Fmt, Mod, Name, Strict)]. +incr(Name, Code, Vid, M, Mod, Strict, Opts, AM0) -> + NameT = Mod:avp_name(Code, Vid), %% {AvpName, Type} | 'AVP' + AvpName = field(NameT), + Arity = avp_arity(Name, AvpName, Mod, Opts, M), + {NameT, AvpName, Arity, incr(AvpName, Arity, Strict, AM0)}. %% Data is a truncated header if command_code = undefined, otherwise %% payload bytes. The former is padded to the length of a header if -- cgit v1.2.3 From 5beaaf0cdc856c1a7623a1beee4da74aa9c1825e Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 20 Jul 2017 22:58:53 +0200 Subject: Fix type spec That dialyzer hasn't noticed is broken. --- lib/diameter/src/base/diameter_traffic.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index 3194cb4bf1..cfa857e09a 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -220,13 +220,13 @@ incr_rc(Dir, Pkt, TPid, Dict0) -> -> pid() %% request handler | boolean() %% answer, known request or not | discard %% request discarded by MFA - when Route :: {Handler, RequestRef, Seqs} + when Route :: {Handler, RequestRef, TPid} | Ack, RecvData :: {[SpawnOpt], #recvdata{}}, SpawnOpt :: term(), Handler :: pid(), RequestRef :: reference(), - Seqs :: {0..16#FFFFFFFF, 0..16#FFFFFFFF}, + TPid :: pid(), Ack :: boolean(). receive_message(TPid, Route, Pkt, Dict0, RecvData) -> -- cgit v1.2.3 From c319fb83523f6a70b2e6c38ad17056bae0ff62f4 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 3 Aug 2017 19:21:47 +0200 Subject: Fix ct return value in traffic suite Which has had no negative effect. --- lib/diameter/test/diameter_traffic_SUITE.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 662d95e3ae..0dbd363dc6 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -317,7 +317,8 @@ init_per_group(Name, Config) when Name == shuffle; Name == parallel -> start_services(Config), - add_transports(Config); + add_transports(Config), + Config; init_per_group(sctp = Name, Config) -> {_, Sctp} = lists:keyfind(Name, 1, Config), -- cgit v1.2.3 From 6e20cd446428685e975a2e51a009c07b1ea2066a Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 3 Aug 2017 19:26:02 +0200 Subject: Sleep randomly at the start of (parallel) traffic testcases --- lib/diameter/test/diameter_traffic_SUITE.erl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 0dbd363dc6..1760d7c5dc 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -318,7 +318,7 @@ init_per_group(Name, Config) Name == parallel -> start_services(Config), add_transports(Config), - Config; + [{sleep, Name == parallel} | Config]; init_per_group(sctp = Name, Config) -> {_, Sctp} = lists:keyfind(Name, 1, Config), @@ -380,6 +380,8 @@ init_per_testcase(Name, Config) -> _ when not Run -> {skip, random}; _ -> + proplists:get_value(sleep, Config, false) + andalso timer:sleep(rand:uniform(200)), [{testcase, Name} | Config] end. -- cgit v1.2.3 From b435807b3aec896493cf3f2fc029c6c12b80ed55 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 23 Jul 2017 13:10:06 +0200 Subject: Restructure/simplify message reception in diameter_peer_fsm Create less garbage. --- lib/diameter/src/base/diameter_peer_fsm.erl | 153 ++++++++++++++-------------- 1 file changed, 74 insertions(+), 79 deletions(-) diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index 36014df94e..4870b86be5 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -653,10 +653,6 @@ encode(Rec, Opts, Dict) -> %% incoming/2 -incoming({recv = T, Name, Pkt}, #state{parent = Pid, ack = Ack} = S) -> - Pid ! {T, self(), get_route(Ack, Pkt), Name, Pkt}, - rcv(Name, Pkt, S); - incoming(#diameter_header{is_request = R}, #state{transport = TPid, ack = Ack}) -> R andalso Ack andalso send(TPid, false), @@ -674,98 +670,97 @@ incoming(T, _) -> %% recv/2 -recv(#diameter_packet{header = #diameter_header{} = Hdr} - = Pkt, - #state{dictionary = Dict0} - = S) -> - recv1(diameter_codec:msg_name(Dict0, Hdr), Pkt, S); - -recv(#diameter_packet{header = undefined, - bin = Bin} - = Pkt, - S) -> - recv(diameter_codec:decode_header(Bin), Pkt, S); +recv(#diameter_packet{bin = Bin} = Pkt, S) -> + recv(Bin, Pkt, S); recv(Bin, S) -> - recv(#diameter_packet{bin = Bin}, S). + recv(Bin, Bin, S). + +%% recv/3 + +recv(Bin, Msg, S) -> + recv(diameter_codec:decode_header(Bin), Bin, Msg, S). + +%% recv/4 -%% recv1/3 +recv(false, Bin, _, #state{length_errors = E}) -> + invalid(E, truncated_header, Bin), + Bin; + +recv(#diameter_header{length = Len} = H, Bin, Msg, #state{length_errors = E, + incoming_maxlen = M, + dictionary = Dict0} + = S) + when E == handle; + 0 == Len rem 4, bit_size(Bin) == 8*Len, size(Bin) =< M -> + recv1(diameter_codec:msg_name(Dict0, H), H, Msg, S); -recv1(_, - #diameter_packet{header = H, bin = Bin}, - #state{incoming_maxlen = M}) +recv(H, Bin, _, #state{incoming_maxlen = M}) when M < size(Bin) -> invalid(false, incoming_maxlen_exceeded, {size(Bin), H}), H; +recv(H, Bin, _, #state{length_errors = E}) -> + T = {size(Bin), bit_size(Bin) rem 8, H}, + invalid(E, message_length_mismatch, T), + H. + +%% recv1/4 + %% Ignore anything but an expected CER/CEA if so configured. This is %% non-standard behaviour. -recv1(Name, #diameter_packet{header = H}, #state{state = {'Wait-CEA', _, _}, - strict = false}) +recv1(Name, H, _, #state{state = {'Wait-CEA', _, _}, + strict = false}) when Name /= 'CEA' -> H; -recv1(Name, #diameter_packet{header = H}, #state{state = recv_CER, - strict = false}) +recv1(Name, H, _, #state{state = recv_CER, + strict = false}) when Name /= 'CER' -> H; %% Incoming request after outgoing DPR: discard. Don't discard DPR, so %% both ends don't do so when sending simultaneously. -recv1(Name, - #diameter_packet{header = #diameter_header{is_request = true} = H}, - #state{dpr = {_,_,_}}) +recv1(Name, #diameter_header{is_request = true} = H, _, #state{dpr = {_,_,_}}) when Name /= 'DPR' -> invalid(false, recv_after_outgoing_dpr, H), H; %% Incoming request after incoming DPR: discard. -recv1(_, - #diameter_packet{header = #diameter_header{is_request = true} = H}, - #state{dpr = true}) -> +recv1(_, #diameter_header{is_request = true} = H, _, #state{dpr = true}) -> invalid(false, recv_after_incoming_dpr, H), H; %% DPA with identifier mismatch, or in response to a DPR initiated by %% the service. -recv1('DPA' = N, - #diameter_packet{header = #diameter_header{hop_by_hop_id = Hid, - end_to_end_id = Eid}} - = Pkt, - #state{dpr = {X,H,E}} +recv1('DPA' = Name, + #diameter_header{hop_by_hop_id = Hid, end_to_end_id = Eid} + = H, + Msg, + #state{dpr = {X,HI,EI}} = S) - when H /= Hid; - E /= Eid; + when HI /= Hid; + EI /= Eid; not X -> - rcv(N, Pkt, S); + Pkt = pkt(H, Msg), + handle(Name, Pkt, S); -%% Any other message with a header and no length errors: send to the -%% parent. -recv1(Name, Pkt, #state{}) -> - {recv, Name, Pkt}. +%% Any other message with a header and no length errors. +recv1(Name, H, Msg, #state{parent = Pid, ack = Ack} = S) -> + Pkt = pkt(H, Msg), + Pid ! {recv, self(), get_route(Ack, Pkt), Name, Pkt}, + handle(Name, Pkt, S). -%% recv/3 +%% pkt/2 -recv(#diameter_header{length = Len} - = H, - #diameter_packet{bin = Bin} - = Pkt, - #state{length_errors = E} - = S) - when E == handle; - 0 == Len rem 4, bit_size(Bin) == 8*Len -> - recv(Pkt#diameter_packet{header = H}, S); +pkt(H, Bin) + when is_binary(Bin) -> + #diameter_packet{header = H, + bin = Bin}; -recv(#diameter_header{} - = H, - #diameter_packet{bin = Bin}, - #state{length_errors = E}) -> - T = {size(Bin), bit_size(Bin) rem 8, H}, - invalid(E, message_length_mismatch, T), - Bin; +pkt(H, Pkt) -> + Pkt#diameter_packet{header = H}. -recv(false, #diameter_packet{bin = Bin}, #state{length_errors = E}) -> - invalid(E, truncated_header, Bin), - Bin. +%% invalid/3 %% Note that counters here only count discarded messages. invalid(E, Reason, T) -> @@ -774,39 +769,39 @@ invalid(E, Reason, T) -> ?LOG(Reason, T), ok. -%% rcv/3 +%% handle/3 %% Incoming CEA. -rcv('CEA' = N, - #diameter_packet{header = #diameter_header{end_to_end_id = Eid, - hop_by_hop_id = Hid}} - = Pkt, - #state{state = {'Wait-CEA', Hid, Eid}} - = S) -> +handle('CEA' = N, + #diameter_packet{header = #diameter_header{end_to_end_id = Eid, + hop_by_hop_id = Hid}} + = Pkt, + #state{state = {'Wait-CEA', Hid, Eid}} + = S) -> ?LOG(recv, N), handle_CEA(Pkt, S); %% Incoming CER -rcv('CER' = N, Pkt, #state{state = recv_CER} = S) -> +handle('CER' = N, Pkt, #state{state = recv_CER} = S) -> handle_request(N, Pkt, S); %% Anything but CER/CEA in a non-Open state is an error, as is %% CER/CEA in anything but recv_CER/Wait-CEA. -rcv(Name, _, #state{state = PS}) +handle(Name, _, #state{state = PS}) when PS /= 'Open'; Name == 'CER'; Name == 'CEA' -> {stop, {Name, PS}}; -rcv('DPR' = N, Pkt, S) -> +handle('DPR' = N, Pkt, S) -> handle_request(N, Pkt, S); %% DPA in response to DPR, with the expected identifiers. -rcv('DPA' = N, - #diameter_packet{header = #diameter_header{end_to_end_id = Eid, - hop_by_hop_id = Hid} - = H} - = Pkt, +handle('DPA' = N, + #diameter_packet{header = #diameter_header{end_to_end_id = Eid, + hop_by_hop_id = Hid} + = H} + = Pkt, #state{dictionary = Dict0, transport = TPid, dpr = {X, Hid, Eid}, @@ -825,13 +820,13 @@ rcv('DPA' = N, %% Ignore an unsolicited DPA in particular. Note that dpa_timeout %% deals with the case in which the peer sends the wrong identifiers %% in DPA. -rcv('DPA' = N, #diameter_packet{header = H}, _) -> +handle('DPA' = N, #diameter_packet{header = H}, _) -> ?LOG(ignored, N), %% Note that these aren't counted in the normal recv counter. diameter_stats:incr({diameter_codec:msg_id(H), recv, ignored}), ok; -rcv(_, _, _) -> +handle(_, _, _) -> ok. %% incr/3 -- cgit v1.2.3 From 64e0dbd8f002d076f5d6c079f9166840c5fb17e3 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 7 Aug 2017 02:59:49 +0200 Subject: Simplify extraction of incoming Diameter messages in diameter_tcp Appending to a binary is efficient, so just append message fragments Only match out the length once per message since doing so for every packet from TCP causes the binary to be copied. --- lib/diameter/src/transport/diameter_tcp.erl | 78 ++++++++++------------------- 1 file changed, 26 insertions(+), 52 deletions(-) diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl index a2f393d5d4..e6168652de 100644 --- a/lib/diameter/src/transport/diameter_tcp.erl +++ b/lib/diameter/src/transport/diameter_tcp.erl @@ -87,8 +87,7 @@ module :: module() | undefined}). -type length() :: 0..16#FFFFFF. %% message length from Diameter header --type size() :: non_neg_integer(). %% accumulated binary size --type frag() :: {length(), size(), binary(), list(binary())} +-type frag() :: maybe_improper_list(length(), binary()) | binary(). -type connect_option() :: {raddr, inet:ip_address()} @@ -721,7 +720,7 @@ tls(accept, Sock, Opts) -> %% Receive packets until a full message is received, recv(Bin, #transport{frag = Head} = S) -> - case rcv(Head, Bin) of + case acc(Head, Bin) of {Msg, B} -> %% have a complete message ... message(recv, Msg, S#transport{frag = B}); Frag -> %% read more on the socket @@ -729,77 +728,52 @@ recv(Bin, #transport{frag = Head} = S) -> flush = false})) end. -%% rcv/2 +%% acc/2 -%% No previous fragment. -rcv(<<>>, Bin) -> - rcv(Bin); +%% Know how many bytes to extract. +acc([Len | Acc], Bin) -> + acc1(Len, <>); -%% Not even the first four bytes of the header. -rcv(Head, Bin) - when is_binary(Head) -> - rcv(<>); +%% Or not. +acc(Head, Bin) -> + acc(<>). -%% Or enough to know how many bytes to extract. -rcv({Len, N, Head, Acc}, Bin) -> - rcv(Len, N + size(Bin), Head, [Bin | Acc]). - -%% rcv/4 +%% acc1/3 %% Extract a message for which we have all bytes. -rcv(Len, N, Head, Acc) - when Len =< N -> - recv1(Len, bin(Head, Acc)); +acc1(Len, Bin) + when Len =< byte_size(Bin) -> + <> = Bin, + {Msg, Rest}; %% Wait for more packets. -rcv(Len, N, Head, Acc) -> - {Len, N, Head, Acc}. - -%% rcv/1 +acc1(Len, Bin) -> + [Len | Bin]. -%% Nothing left. -rcv(<<>> = Bin) -> - Bin; +%% acc/1 %% The Message Length isn't even sufficient for a header. Chances are %% things will go south from here but if we're lucky then the bytes we %% have extend to an intended message boundary and we can recover by %% simply receiving them. Make it so. -rcv(<<_:1/binary, Len:24, _/binary>> = Bin) +acc(<<_:1/binary, Len:24, _/binary>> = Bin) when Len < 20 -> {Bin, <<>>}; -%% Enough bytes to extract a message. -rcv(<<_:1/binary, Len:24, _/binary>> = Bin) - when Len =< size(Bin) -> - recv1(Len, Bin); - -%% Or not: wait for more packets. -rcv(<<_:1/binary, Len:24, _/binary>> = Head) -> - {Len, size(Head), Head, []}; +%% Know the message length. +acc(<<_:1/binary, Len:24, _/binary>> = Bin) -> + acc1(Len, Bin); %% Not even 4 bytes yet. -rcv(Head) -> - Head. - -%% recv1/2 - -recv1(Len, Bin) -> - <> = Bin, - {Msg, Rest}. - -%% bin/2 - -bin(Head, Acc) -> - list_to_binary([Head | lists:reverse(Acc)]). +acc(Bin) -> + Bin. %% bin/1 -bin({_, _, Head, Acc}) -> - bin(Head, Acc); +bin([_ | Bin]) -> + Bin; -bin(Bin) - when is_binary(Bin) -> +bin(Bin) -> Bin. %% flush/1 -- cgit v1.2.3 From 05e1764c1f7d079b5432bff11a792cdb31b00dcd Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 7 Aug 2017 15:35:11 +0200 Subject: Don't update diameter_tcp state unnecessarily Only reset the fragment after all messages have been extracted. --- lib/diameter/src/transport/diameter_tcp.erl | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl index e6168652de..ead4e7e72a 100644 --- a/lib/diameter/src/transport/diameter_tcp.erl +++ b/lib/diameter/src/transport/diameter_tcp.erl @@ -598,11 +598,12 @@ t(T,S) -> %% Incoming packets. transition({P, Sock, Bin}, #transport{socket = Sock, - ssl = B} + ssl = B, + frag = Frag} = S) when P == ssl, true == B; P == tcp -> - recv(Bin, S#transport{active = false}); + recv(acc(Frag, Bin), S#transport{active = false}); %% Capabilties exchange has decided on whether or not to run over TLS. transition({diameter, {tls, Ref, Type, B}}, #transport{parent = Pid} @@ -719,14 +720,13 @@ tls(accept, Sock, Opts) -> %% using Nagle. %% Receive packets until a full message is received, -recv(Bin, #transport{frag = Head} = S) -> - case acc(Head, Bin) of - {Msg, B} -> %% have a complete message ... - message(recv, Msg, S#transport{frag = B}); - Frag -> %% read more on the socket - start_fragment_timer(setopts(S#transport{frag = Frag, - flush = false})) - end. + +recv({Msg, Rest}, S) -> %% have a complete message ... + recv(acc(Rest), message(recv, Msg, S)); + +recv(Frag, S) -> %% or not + start_fragment_timer(setopts(S#transport{frag = Frag, + flush = false})). %% acc/2 @@ -962,7 +962,7 @@ message(ack, _, #transport{message_cb = false} = S) -> S; message(Dir, Msg, #transport{message_cb = CB} = S) -> - recv(<<>>, actions(cb(CB, Dir, Msg), Dir, S)). + setopts(actions(cb(CB, Dir, Msg), Dir, S)). %% actions/3 -- cgit v1.2.3 From ca68fe8c449a170f64fd9b41b710ac67966be2ee Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 7 Aug 2017 15:41:50 +0200 Subject: Don't update diameter_tcp state unnecessarily Not with each setopts to re-activate the socket. --- lib/diameter/src/transport/diameter_tcp.erl | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl index ead4e7e72a..bda92d02f6 100644 --- a/lib/diameter/src/transport/diameter_tcp.erl +++ b/lib/diameter/src/transport/diameter_tcp.erl @@ -603,7 +603,7 @@ transition({P, Sock, Bin}, #transport{socket = Sock, = S) when P == ssl, true == B; P == tcp -> - recv(acc(Frag, Bin), S#transport{active = false}); + recv(acc(Frag, Bin), S); %% Capabilties exchange has decided on whether or not to run over TLS. transition({diameter, {tls, Ref, Type, B}}, #transport{parent = Pid} @@ -724,9 +724,14 @@ tls(accept, Sock, Opts) -> recv({Msg, Rest}, S) -> %% have a complete message ... recv(acc(Rest), message(recv, Msg, S)); -recv(Frag, S) -> %% or not - start_fragment_timer(setopts(S#transport{frag = Frag, - flush = false})). +recv(Frag, #transport{recv = B, + socket = Sock, + module = M} + = S) -> %% or not + B andalso setopts(M, Sock), + start_fragment_timer(S#transport{frag = Frag, + flush = false, + active = B}). %% acc/2 @@ -885,14 +890,20 @@ setopts(#transport{socket = Sock, module = M} = S) when B, not A -> - case setopts(M, Sock, [{active, once}]) of - ok -> S#transport{active = true}; - X -> x({setopts, Sock, M, X}) %% possibly on peer disconnect - end; + setopts(M, Sock), + S#transport{active = true}; setopts(S) -> S. +%% setopts/2 + +setopts(M, Sock) -> + case setopts(M, Sock, [{active, once}]) of + ok -> ok; + X -> x({setopts, Sock, M, X}) %% possibly on peer disconnect + end. + %% portnr/2 portnr(gen_tcp, Sock) -> -- cgit v1.2.3 From d1f4369afb9fb9453ebe8868d88ae299d908a2e4 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Tue, 8 Aug 2017 16:20:00 +0200 Subject: Avoid unnecessary copying of binaries in diameter_tcp Since messages are accumulated by appending binaries as of three commits back, the accumulated binary is prone to being copied, as discussed in the Efficiency Guide. Matching the Message Length header as bytes are being accumulated is one cause of this, so work around it by splitting the binary and extracting the length without a match. This doesn't feel like something that should be necessary: that matching a binary would cause an append to copy isn't obvious. The first attempt at simplifying the accumulation was simply to append an incoming binary to the current fragment, match against <<_, Len:24, _/binary>> to extract the length, and then test if there are enough bytes for a message. This lead to horrible performance (response times for 2 MB messages approximately 100 times worse than previously), and it wasn't at all obvious that the reason was the accumulated binary being copied with each append as a result of the match. Using split_binary avoids the match context that forces the copying. --- lib/diameter/src/transport/diameter_tcp.erl | 38 +++++++++++++++++++---------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl index bda92d02f6..6252dbddfb 100644 --- a/lib/diameter/src/transport/diameter_tcp.erl +++ b/lib/diameter/src/transport/diameter_tcp.erl @@ -748,8 +748,7 @@ acc(Head, Bin) -> %% Extract a message for which we have all bytes. acc1(Len, Bin) when Len =< byte_size(Bin) -> - <> = Bin, - {Msg, Rest}; + split_binary(Bin, Len); %% Wait for more packets. acc1(Len, Bin) -> @@ -757,17 +756,30 @@ acc1(Len, Bin) -> %% acc/1 -%% The Message Length isn't even sufficient for a header. Chances are -%% things will go south from here but if we're lucky then the bytes we -%% have extend to an intended message boundary and we can recover by -%% simply receiving them. Make it so. -acc(<<_:1/binary, Len:24, _/binary>> = Bin) - when Len < 20 -> - {Bin, <<>>}; - -%% Know the message length. -acc(<<_:1/binary, Len:24, _/binary>> = Bin) -> - acc1(Len, Bin); +%% Don't match on Bin since this results in it being copied at the +%% next append according to the Efficiency Guide. This is also the +%% reason that the Len is extracted and maintained when accumulating +%% messages. The simplest implementation is just to accumulate a +%% binary and match <<_, Len:24, _/binary>> each time the length is +%% required, but the performance of this decays quadratically with the +%% message length, since the binary is then copied with each append of +%% additional bytes from gen_tcp. + +acc(Bin) + when 3 < byte_size(Bin) -> + {Head, _} = split_binary(Bin, 4), + [_,A,B,C] = binary_to_list(Head), + Len = (A bsl 16) bor (B bsl 8) bor C, + if Len < 20 -> + %% Message length isn't sufficient for a Diameter Header. + %% Chances are things will go south from here but if we're + %% lucky then the bytes we have extend to an intended + %% message boundary and we can recover by simply receiving + %% them. Make it so. + {Bin, <<>>}; + true -> + acc1(Len, Bin) + end; %% Not even 4 bytes yet. acc(Bin) -> -- cgit v1.2.3 From 90b18515763cc332a1f2e612a24c50f245b5dc72 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Tue, 8 Aug 2017 21:42:14 +0200 Subject: Split AVPs at decode Despite what the Efficiency Guide says about match being more efficient, split_binary appears to be slightly faster. (Although this one extraction is a drop in the bucket.) Binary optimizations aren't an issue during decode. --- lib/diameter/src/base/diameter_codec.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index c171ea9dca..63e39b12d1 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -321,7 +321,7 @@ decode_avps('', _, _, _, #diameter_packet{header = H, %% unknown message %% msg = undefined identifies this case. decode_avps(MsgName, Mod, AppMod, Opts, #diameter_packet{bin = Bin} = Pkt) -> - <<_:20/binary, Avps/binary>> = Bin, + {_, Avps} = split_binary(Bin, 20), {Rec, As, Errors} = Mod:decode_avps(MsgName, Avps, Opts#{dictionary => AppMod, -- cgit v1.2.3 From 2be3e00698dea8236e60f3262c50b37eebfb4a04 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 10 Aug 2017 11:13:51 +0200 Subject: Fix type spec That dialyzer hasn't noticed is broken. --- lib/diameter/src/base/diameter_traffic.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index cfa857e09a..84ae33ae9c 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -186,7 +186,7 @@ incr_error(Dir, Id, TPid) -> %% --------------------------------------------------------------------------- -spec incr_rc(send|recv, Pkt, TPid, DictT) - -> {Counter, non_neg_integer()} + -> Counter | Reason when Pkt :: #diameter_packet{}, TPid :: pid(), -- cgit v1.2.3 From eb5fa7e2a0f67924828ea3d81891b86d08a53b0c Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 10 Aug 2017 11:14:14 +0200 Subject: Add service_opt() traffic_counters To be able to disable the counting of messages for which application callbacks take place. Messages sent/handled by diameter itself are always counted. --- lib/diameter/doc/src/diameter.xml | 20 ++ lib/diameter/src/base/diameter.erl | 1 + lib/diameter/src/base/diameter_config.erl | 3 + lib/diameter/src/base/diameter_service.erl | 1 + lib/diameter/src/base/diameter_traffic.erl | 295 ++++++++++++++++------------ lib/diameter/src/base/diameter_watchdog.erl | 2 +- 6 files changed, 191 insertions(+), 131 deletions(-) diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index 017f6bb01d..3ad24257a5 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -1053,6 +1053,26 @@ The default value is for backwards compatibility.

+ +{traffic_counters, boolean()} + +

+Whether or not to count application-specific messages; those for which +&man_app; callbacks take place. +If false then only messages handled by diameter itself are counted: +CER/CEA, DWR/DWA, DPR/DPA.

+ +

+Defaults to true.

+ + +

+Disabling counters is a performance improvement, but means that the +omitted counters are not returned by &service_info;.

+
+ +
+ {use_shared_peers, boolean() | [node()] | evaluable()}

diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl index c919ff4c93..2e18a1d903 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -353,6 +353,7 @@ call(SvcName, App, Message) -> | {sequence, sequence() | evaluable()} | {share_peers, remotes()} | {decode_format, decode_format()} + | {traffic_counters, boolean()} | {string_decode, boolean()} | {strict_arities, true | strict_arities()} | {strict_mbit, boolean()} diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index 2b069f40cc..f1b6e56782 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -715,6 +715,7 @@ make_config(SvcName, Opts) -> {true, strict_arities}, {true, strict_mbit}, {record, decode_format}, + {true, traffic_counters}, {true, string_decode}, {[], spawn_opt}]), @@ -760,6 +761,7 @@ opt(K, false = B) K == strict_arities; K == strict_mbit; K == decode_format; + K == traffic_counters; K == string_decode -> B; @@ -768,6 +770,7 @@ opt(K, true = B) K == use_shared_peers; K == strict_arities; K == strict_mbit; + K == traffic_counters; K == string_decode -> B; diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index c3c3c4b0e7..07f1fd3a4a 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -115,6 +115,7 @@ strict_arities => diameter:strict_arities(), strict_mbit := boolean(), decode_format := diameter:decode_format(), + traffic_counters := boolean(), string_decode := boolean(), spawn_opt := list() | {module(), atom(), list()}}}). diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index 84ae33ae9c..27a41d6eb0 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -70,12 +70,13 @@ timeout = 5000 :: 0..16#FFFFFFFF, %% for outgoing requests detach = false :: boolean()}). -%% Term passed back to receive_message/6 with every incoming message. +%% Term passed back to receive_message/5 with every incoming message. -record(recvdata, {peerT :: ets:tid(), service_name :: diameter:service_name(), apps :: [#diameter_app{}], sequence :: diameter:sequence(), + counters :: boolean(), codec :: #{decode_format := diameter:decode_format(), string_decode := boolean(), strict_arities => diameter:strict_arities(), @@ -98,12 +99,13 @@ %% --------------------------------------------------------------------------- make_recvdata([SvcName, PeerT, Apps, SvcOpts | _]) -> - #{sequence := {_,_} = Mask, spawn_opt := Opts} + #{sequence := {_,_} = Mask, spawn_opt := Opts, traffic_counters := B} = SvcOpts, {Opts, #recvdata{service_name = SvcName, peerT = PeerT, apps = Apps, sequence = Mask, + counters = B, codec = maps:with([decode_format, string_decode, strict_arities, @@ -197,18 +199,26 @@ incr_error(Dir, Id, TPid) -> | {'Experimental-Result', integer(), integer()}, Reason :: atom(). -incr_rc(Dir, Pkt, TPid, {_, AppDict, _} = DictT) -> - try - incr_result(Dir, Pkt, TPid, DictT) +incr_rc(Dir, Pkt, TPid, {MsgDict, AppDict, Dict0}) -> + incr_rc(Dir, Pkt, TPid, MsgDict, AppDict, Dict0); + +incr_rc(Dir, Pkt, TPid, Dict0) -> + incr_rc(Dir, Pkt, TPid, Dict0, Dict0, Dict0). + +%% incr_rc/6 + +incr_rc(Dir, Pkt, TPid, MsgDict, AppDict, Dict0) -> + try get_result(Dir, MsgDict, Dict0, Pkt) of + false -> + unknown; + Avp -> + incr_result(Dir, Avp, Pkt, TPid, AppDict) catch exit: {E,_} when E == no_result_code; E == invalid_error_bit -> incr(TPid, {msg_id(Pkt#diameter_packet.header, AppDict), Dir, E}), E - end; - -incr_rc(Dir, Pkt, TPid, Dict0) -> - incr_rc(Dir, Pkt, TPid, {Dict0, Dict0, Dict0}). + end. %% --------------------------------------------------------------------------- %% receive_message/5 @@ -307,14 +317,15 @@ recv_request(Ack, = Pkt, Dict0, #recvdata{peerT = PeerT, - apps = Apps} + apps = Apps, + counters = Count} = RecvData) -> Ack andalso (TPid ! {handler, self()}), case diameter_service:find_incoming_app(PeerT, TPid, Id, Apps) of {#diameter_app{id = Aid, dictionary = AppDict} = App, Caps} -> - incr(recv, Pkt, TPid, AppDict), + Count andalso incr(recv, Pkt, TPid, AppDict), DecPkt = decode(Aid, AppDict, RecvData, Pkt), - incr_error(recv, DecPkt, TPid, AppDict), + Count andalso incr_error(recv, DecPkt, TPid, AppDict), send_A(recv_R(App, TPid, Dict0, Caps, RecvData, DecPkt), TPid, App, @@ -535,6 +546,7 @@ send_A({call, Opts}, TPid, App, Dict0, RecvData, Pkt, Caps, Fs) -> MsgDict, AppDict, Dict0, + RecvData#recvdata.counters, Fs); RC -> send_answer(answer_message(RC, Dict0, Caps, Pkt), @@ -578,14 +590,22 @@ send_answer(Ans, TPid, MsgDict, AppDict, Dict0, RecvData, DecPkt, Fs) -> TPid, RecvData#recvdata.codec, make_answer_packet(Ans, DecPkt, MsgDict, Dict0)), - send_answer(Pkt, TPid, MsgDict, AppDict, Dict0, Fs). + send_answer(Pkt, + TPid, + MsgDict, + AppDict, + Dict0, + RecvData#recvdata.counters, + Fs). -%% send_answer/6 +%% send_answer/7 -send_answer(Pkt, TPid, MsgDict, AppDict, Dict0, [EvalPktFs | EvalFs]) -> +send_answer(Pkt, TPid, MsgDict, AppDict, Dict0, Count, [EvalPktFs | EvalFs]) -> eval_packet(Pkt, EvalPktFs), - incr(send, Pkt, TPid, AppDict), - incr_rc(send, Pkt, TPid, {MsgDict, AppDict, Dict0}), %% count outgoing + Count andalso begin + incr(send, Pkt, TPid, AppDict), + incr_rc(send, Pkt, TPid, MsgDict, AppDict, Dict0) + end, send(TPid, z(Pkt), _Route = self()), lists:foreach(fun diameter_lib:eval/1, EvalFs). @@ -1110,48 +1130,31 @@ find_avp(Code, VId, [_ | Avps]) -> %% Message sent as a header/avps list. incr_result(send = Dir, - #diameter_packet{msg = [#diameter_header{} = H | _]} - = Pkt, + Avp, + #diameter_packet{msg = [#diameter_header{} = H | _]}, TPid, - DictT) -> - incr_res(Dir, Pkt#diameter_packet{header = H}, TPid, DictT); - -%% Outgoing message as binary: don't count. (Sending binaries is only -%% partially supported.) -incr_result(send, #diameter_packet{header = undefined = No}, _, _) -> - No; + AppDict) -> + incr_result(Dir, Avp, H, [], TPid, AppDict); %% Incoming or outgoing. Outgoing with encode errors never gets here %% since encode fails. -incr_result(Dir, Pkt, TPid, DictT) -> - incr_res(Dir, Pkt, TPid, DictT). - -incr_res(Dir, - #diameter_packet{header = #diameter_header{is_error = E} - = Hdr, - errors = Es} - = Pkt, - TPid, - DictT) -> - {MsgDict, AppDict, Dict0} = DictT, +incr_result(Dir, Avp, Pkt, TPid, AppDict) -> + #diameter_packet{header = H, errors = Es} + = Pkt, + incr_result(Dir, Avp, H, Es, TPid, AppDict). +%% incr_result/6 + +incr_result(Dir, Avp, Hdr, Es, TPid, AppDict) -> Id = msg_id(Hdr, AppDict), %% Could be {relay, 0}, in which case the R-bit is redundant since %% only answers are being counted. Let it be however, so that the %% same tuple is in both send/recv and result code counters. %% Count incoming decode errors. - recv /= Dir orelse [] == Es orelse incr_error(Dir, Id, TPid, AppDict), - - %% Exit on a missing result code. - T = rc_counter(MsgDict, Dir, Pkt), - T == false andalso ?LOGX(no_result_code, {MsgDict, Dir, Hdr}), - {Ctr, RC, Avp} = T, - - %% Or on an inappropriate value. - is_result(RC, E, Dict0) - orelse ?LOGX(invalid_error_bit, {MsgDict, Dir, Hdr, Avp}), + send == Dir orelse [] == Es orelse incr_error(Dir, Id, TPid, AppDict), + Ctr = rcc(Avp), incr(TPid, {Id, Dir, Ctr}), Ctr. @@ -1196,7 +1199,50 @@ is_result(RC, true, _) -> incr(TPid, Counter) -> diameter_stats:incr(Counter, TPid, 1). -%% rc_counter/3 +%% rcc/1 + +rcc(#diameter_avp{name = 'Result-Code' = Name, value = V}) -> + {Name, head(V)}; + +rcc(#diameter_avp{name = 'Experimental-Result', value = V}) -> + head(V). + +%% head/1 + +head([V|_]) -> + V; +head(V) -> + V. + +%% rcv/1 + +rcv(#diameter_avp{name = N, value = V}) -> + rcv(N, head(V)). + +%% rcv/2 + +rcv('Experimental-Result', {_,_,N}) -> + N; + +rcv('Result-Code', N) -> + N. + +%% get_result/4 + +%% Message sent as binary: no checks or counting. +get_result(_, _, _, #diameter_packet{header = undefined}) -> + false; + +get_result(Dir, MsgDict, Dict0, Pkt) -> + Avp = get_result(MsgDict, msg(Dir, Pkt)), + Hdr = Pkt#diameter_packet.header, + %% Exit on a missing result code or inappropriate value. + Avp == false + andalso ?LOGX(no_result_code, {MsgDict, Dir, Hdr}), + E = Hdr#diameter_header.is_error, + is_result(rcv(Avp), E, Dict0) + orelse ?LOGX(invalid_error_bit, {MsgDict, Dir, Hdr, Avp}), + Avp. %% RFC 3588, 7.6: %% @@ -1204,46 +1250,29 @@ incr(TPid, Counter) -> %% applications MUST include either one Result-Code AVP or one %% Experimental-Result AVP. -rc_counter(Dict, Dir, #diameter_packet{header = H, - avps = As, - msg = Msg}) +%% msg/2 + +msg(Dir, #diameter_packet{header = H, + avps = As, + msg = Msg}) when Dir == recv; %% decoded incoming Msg == undefined -> %% relayed outgoing - rc_counter(Dict, [H|As]); - -rc_counter(Dict, _, #diameter_packet{msg = Msg}) -> - rc_counter(Dict, Msg). - -rc_counter(Dict, Msg) -> - rcc(get_result(Dict, Msg)). - -rcc(#diameter_avp{name = 'Result-Code' = Name, value = N} = A) - when is_integer(N) -> - {{Name, N}, N, A}; - -rcc(#diameter_avp{name = 'Result-Code' = Name, value = [N|_]} = A) - when is_integer(N) -> - {{Name, N}, N, A}; + [H|As]; -rcc(#diameter_avp{name = 'Experimental-Result', value = {_,_,N} = T} = A) - when is_integer(N) -> - {T, N, A}; - -rcc(#diameter_avp{name = 'Experimental-Result', value = [{_,_,N} = T|_]} = A) - when is_integer(N) -> - {T, N, A}; - -rcc(_) -> - false. +msg(_, #diameter_packet{msg = Msg}) -> + Msg. %% get_result/2 get_result(Dict, Msg) -> try [throw(A) || N <- ['Result-Code', 'Experimental-Result'], - #diameter_avp{} = A <- [get_avp(Dict, N, Msg)]] + #diameter_avp{} = A <- [get_avp(Dict, N, Msg)], + is_integer(catch rcv(A))], + false catch - #diameter_avp{} = A -> A + #diameter_avp{} = A -> + A end. x(T) -> @@ -1367,7 +1396,7 @@ make_opts([T | _], _, _, _, _, _) -> send_request({{TPid, _Caps} = TC, App} = Transport, - #{sequence := Mask} + #{sequence := Mask, traffic_counters := Count} = SvcOpts, Msg0, CallOpts, @@ -1383,9 +1412,15 @@ send_request({{TPid, _Caps} = TC, App} SvcOpts, ReqPkt), eval_packet(EncPkt, Fs), - T = send_R(ReqPkt, EncPkt, Transport, CallOpts, Caller, SvcName), + T = send_R(ReqPkt, + EncPkt, + Transport, + CallOpts, + Caller, + Count, + SvcName), Ans = recv_answer(SvcName, App, CallOpts, T), - handle_answer(SvcName, SvcOpts, App, Ans); + handle_answer(SvcName, Count, SvcOpts, App, Ans); {discard, Reason} -> {error, Reason}; discard -> @@ -1528,6 +1563,7 @@ send_R(ReqPkt, {{TPid, _Caps} = TC, #diameter_app{dictionary = AppDict}}, #options{timeout = Timeout}, {Pid, Ref}, + Count, SvcName) -> Req = #request{ref = Ref, caller = Pid, @@ -1535,7 +1571,7 @@ send_R(ReqPkt, peer = TC, packet = ReqPkt}, - incr(send, EncPkt, TPid, AppDict), + Count andalso incr(send, EncPkt, TPid, AppDict), {TRef, MRef} = zend_requezt(TPid, EncPkt, Req, SvcName, Timeout), Pid ! Ref, %% tell caller a send has been attempted {TRef, MRef, Req}. @@ -1567,15 +1603,16 @@ failover(SvcName, App, Req, CallOpts) -> CallOpts, SvcName). -%% handle_answer/4 +%% handle_answer/5 -handle_answer(SvcName, _, App, {error, Req, Reason}) -> +handle_answer(SvcName, _, _, App, {error, Req, Reason}) -> #request{packet = Pkt, peer = {_TPid, _Caps} = TC} = Req, cb(App, handle_error, [Reason, msg(Pkt), SvcName, TC]); handle_answer(SvcName, + Count, SvcOpts, #diameter_app{id = Id, dictionary = AppDict, @@ -1589,43 +1626,50 @@ handle_answer(SvcName, #request{peer = {TPid, _}} = Req, - incr(recv, DecPkt, TPid, AppDict), - - AnsPkt = try - incr_result(recv, DecPkt, TPid, {MsgDict, AppDict, Dict0}) - of - _ -> DecPkt - catch - exit: {no_result_code, _} -> - %% RFC 6733 requires one of Result-Code or - %% Experimental-Result, but the decode will have - %% detected a missing AVP. If both are optional in - %% the dictionary then this isn't a decode error: - %% just continue on. - DecPkt; - exit: {invalid_error_bit, {_, _, _, Avp}} -> - #diameter_packet{errors = Es} - = DecPkt, - E = {5004, Avp}, - DecPkt#diameter_packet{errors = [E|Es]} - end, - - handle_answer(AnsPkt, SvcName, App, AE, Req). + answer(answer(DecPkt, TPid, MsgDict, AppDict, Dict0, Count), + SvcName, + App, + AE, + Req). + +%% answer/6 + +answer(DecPkt, TPid, MsgDict, AppDict, Dict0, Count) -> + Count andalso incr(recv, DecPkt, TPid, AppDict), + try get_result(recv, MsgDict, Dict0, DecPkt) of + Avp -> + Count andalso false /= Avp + andalso incr_result(recv, Avp, DecPkt, TPid, AppDict), + DecPkt + catch + exit: {no_result_code, _} -> + %% RFC 6733 requires one of Result-Code or + %% Experimental-Result, but the decode will have + %% detected a missing AVP. If both are optional in + %% the dictionary then this isn't a decode error: + %% just continue on. + DecPkt; + exit: {invalid_error_bit, {_, _, _, Avp}} -> + #diameter_packet{errors = Es} + = DecPkt, + E = {5004, Avp}, + DecPkt#diameter_packet{errors = [E|Es]} + end. -%% handle_answer/5 +%% answer/5 -handle_answer(#diameter_packet{errors = Es} - = Pkt, - SvcName, - App, - AE, - #request{peer = {_TPid, _Caps} = TC, - packet = P}) +answer(#diameter_packet{errors = Es} + = Pkt, + SvcName, + App, + AE, + #request{peer = {_TPid, _Caps} = TC, + packet = P}) when callback == AE; [] == Es -> cb(App, handle_answer, [Pkt, msg(P), SvcName, TC]); -handle_answer(#diameter_packet{header = H}, SvcName, _, AE, _) -> +answer(#diameter_packet{header = H}, SvcName, _, AE, _) -> handle_error(H, SvcName, AE). %% handle_error/3 @@ -1838,10 +1882,8 @@ get_destination(Dict, Msg) -> [str(get_avp_value(Dict, D, Msg)) || D <- ['Destination-Realm', 'Destination-Host']]. -%% This is not entirely correct. The avp could have an arity 1, in -%% which case an empty list is a DiameterIdentity of length 0 rather -%% than the list of no values we treat it as by mapping to undefined. -%% This behaviour is documented. +%% A DiameterIdentity has length at least one, so an empty list is not +%% a Realm/Host. str([]) -> undefined; str(T) -> @@ -1871,10 +1913,8 @@ get_avp(?RELAY, Name, Msg) -> get_avp(Dict, Name, [#diameter_header{} | Avps]) -> try {Code, _, VId} = Dict:avp_header(Name), - find_avp(Code, VId, Avps) - of - A -> - (avp_decode(Dict, Name, ungroup(A)))#diameter_avp{name = Name} + A = find_avp(Code, VId, Avps), + (avp_decode(Dict, Name, ungroup(A)))#diameter_avp{name = Name} catch error: _ -> undefined @@ -1931,13 +1971,8 @@ avp_decode(Dict, Name, #diameter_avp{value = undefined, data = Bin} = Avp) when is_binary(Bin) -> - try Dict:avp(decode, Bin, Name, decode_opts(Dict)) of - V -> - Avp#diameter_avp{value = V} - catch - error:_ -> - Avp - end; + V = Dict:avp(decode, Bin, Name, decode_opts(Dict)), + Avp#diameter_avp{value = V}; avp_decode(_, _, #diameter_avp{} = Avp) -> Avp. diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl index 294325751e..b2172356ee 100644 --- a/lib/diameter/src/base/diameter_watchdog.erl +++ b/lib/diameter/src/base/diameter_watchdog.erl @@ -154,7 +154,7 @@ i({Ack, T, Pid, {Opts, receive_data = RecvData, dictionary = Dict0, config = - maps:without(CodecKeys, + maps:without([traffic_counters | CodecKeys], config(SvcOpts#{restrict => restrict(Nodes), suspect => 1, okay => 3}, -- cgit v1.2.3 From 97256c5e098f36ee3459db768775c951e87ca717 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 17 Jul 2017 08:58:00 +0200 Subject: Increase init_per_suite timetrap in traffic suite Compiling dictionaries is relatively slow, resulting in timeouts on some hosts. --- lib/diameter/test/diameter_traffic_SUITE.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index fb69cd831e..d676614084 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -27,6 +27,7 @@ -export([suite/0, all/0, groups/0, + init_per_suite/0, init_per_suite/1, end_per_suite/1, init_per_group/2, @@ -300,6 +301,9 @@ names(_, Names) -> %% -------------------- +init_per_suite() -> + [{timetrap, {seconds, 60}}]. + init_per_suite(Config) -> [{rfc4005, compile_and_load()}, {sctp, ?util:have_sctp()} | Config]. -- cgit v1.2.3 From baa65db0f00c84c7248d2401272569f1daf2e2b9 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 17 Jul 2017 11:14:32 +0200 Subject: Don't search forms unnecessarily in diameter_exprecs parse transform The forms being extracted are in the head of the split. --- lib/diameter/src/compiler/diameter_exprecs.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/diameter/src/compiler/diameter_exprecs.erl b/lib/diameter/src/compiler/diameter_exprecs.erl index 9a0cb6baf2..143dede037 100644 --- a/lib/diameter/src/compiler/diameter_exprecs.erl +++ b/lib/diameter/src/compiler/diameter_exprecs.erl @@ -110,9 +110,9 @@ %% parse_transform/2 parse_transform(Forms, _Options) -> - Rs = [R || {attribute, _, record, R} <- Forms], - Es = lists:append([E || {attribute, _, export_records, E} <- Forms]), {H,T} = lists:splitwith(fun is_head/1, Forms), + Rs = [R || {attribute, _, record, R} <- H], + Es = lists:append([E || {attribute, _, export_records, E} <- H]), H ++ [a_export(Es) | f_accessors(Es, Rs)] ++ T. is_head(T) -> -- cgit v1.2.3 From fc718fb2a7f0871930fb2553ae02875a106c414d Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 17 Jul 2017 11:40:34 +0200 Subject: Add diameter_util:eprof/1 for test Wrapping it around diameter_traffic_SUITE:compile_and_load/0 shows that it's not diameter that's expensive. See below. ****** Process <0.103.0> -- 1.11 % of profiled time *** FUNCTION CALLS % TIME [uS / CALLS] -------- ----- ------- ---- [----------] lists:append/1 3 0.00 0 [ 0.00] lists:usort/1 1 0.00 0 [ 0.00] lists:rmerge3_21_3/6 5 0.00 0 [ 0.00] lists:merge2_1/4 4 0.00 0 [ 0.00] lists:ukeysplit_1_1/10 2 0.00 0 [ 0.00] lists:ukeymergel/3 1 0.00 0 [ 0.00] lists:rukeymergel/3 2 0.00 0 [ 0.00] lists:rukeymerge3_1/11 3 0.00 0 [ 0.00] lists:rukeymerge2_1/6 1 0.00 0 [ 0.00] beam_lib:chunks/2 1 0.00 0 [ 0.00] beam_lib:read_chunk_data/2 1 0.00 0 [ 0.00] beam_lib:read_chunk_data/3 1 0.00 0 [ 0.00] beam_lib:check_chunks/5 2 0.00 0 [ 0.00] beam_lib:scan_beam/4 1 0.00 0 [ 0.00] beam_lib:scan_beam1/2 1 0.00 0 [ 0.00] beam_lib:scan_beam2/2 1 0.00 0 [ 0.00] beam_lib:get_atom_data/8 1 0.00 0 [ 0.00] beam_lib:get_chunk/4 2 0.00 0 [ 0.00] beam_lib:chunks_to_data/7 2 0.00 0 [ 0.00] beam_lib:extract_atom/2 1 0.00 0 [ 0.00] beam_lib:open_file/1 1 0.00 0 [ 0.00] beam_lib:beam_filename/1 1 0.00 0 [ 0.00] beam_lib:'-read_chunk_data/3-after$^0/0-0-'/1 1 0.00 0 [ 0.00] diameter_dict_scanner:scan/1 2 0.00 0 [ 0.00] diameter_codegen:from_dict/4 1 0.00 0 [ 0.00] diameter_codegen:mod/2 1 0.00 0 [ 0.00] diameter_codegen:maybe_write/5 1 0.00 0 [ 0.00] diameter_codegen:report/2 3 0.00 0 [ 0.00] diameter_codegen:report/3 3 0.00 0 [ 0.00] diameter_codegen:putr/2 1 0.00 0 [ 0.00] diameter_codegen:eraser/1 1 0.00 0 [ 0.00] diameter_codegen:gen/3 1 0.00 0 [ 0.00] diameter_codegen:erl_forms/2 1 0.00 0 [ 0.00] diameter_codegen:make_hrl_forms/1 1 0.00 0 [ 0.00] diameter_codegen:make_record_forms/1 1 0.00 0 [ 0.00] diameter_codegen:grp_proj/1 7 0.00 0 [ 0.00] diameter_codegen:f_name/1 1 0.00 0 [ 0.00] diameter_codegen:f_id/1 1 0.00 0 [ 0.00] diameter_codegen:c_id/1 1 0.00 0 [ 0.00] diameter_codegen:f_vendor_id/1 1 0.00 0 [ 0.00] diameter_codegen:b_vendor_id/1 1 0.00 0 [ 0.00] diameter_codegen:f_vendor_name/1 1 0.00 0 [ 0.00] diameter_codegen:b_vendor_name/1 1 0.00 0 [ 0.00] diameter_codegen:f_msg_name/1 1 0.00 0 [ 0.00] diameter_codegen:msg_name/1 1 0.00 0 [ 0.00] diameter_codegen:c_msg_name/1 5 0.00 0 [ 0.00] diameter_codegen:f_msg2rec/1 1 0.00 0 [ 0.00] diameter_codegen:msg2rec/1 1 0.00 0 [ 0.00] diameter_codegen:f_rec2msg/1 1 0.00 0 [ 0.00] diameter_codegen:rec2msg/1 1 0.00 0 [ 0.00] diameter_codegen:f_name2rec/1 1 0.00 0 [ 0.00] diameter_codegen:name2rec/1 1 0.00 0 [ 0.00] diameter_codegen:avps/1 6 0.00 0 [ 0.00] diameter_codegen:c_imported_avp_name/2 1 0.00 0 [ 0.00] diameter_codegen:f_avp_arity_2/1 1 0.00 0 [ 0.00] diameter_codegen:avp_arity/1 1 0.00 0 [ 0.00] diameter_codegen:f_avp/1 1 0.00 0 [ 0.00] diameter_codegen:avp/1 1 0.00 0 [ 0.00] diameter_codegen:types/1 1 0.00 0 [ 0.00] diameter_codegen:avp/4 1 0.00 0 [ 0.00] diameter_codegen:cs_imported_avp/3 1 0.00 0 [ 0.00] diameter_codegen:f_enumerated_avp/1 1 0.00 0 [ 0.00] diameter_codegen:enumerated_avp/1 1 0.00 0 [ 0.00] diameter_codegen:enumerated_avp/3 1 0.00 0 [ 0.00] diameter_codegen:f_msg_header/1 1 0.00 0 [ 0.00] diameter_codegen:msg_header/1 1 0.00 0 [ 0.00] diameter_codegen:msg_header/2 1 0.00 0 [ 0.00] diameter_codegen:f_avp_header/1 1 0.00 0 [ 0.00] diameter_codegen:avp_header/1 1 0.00 0 [ 0.00] diameter_codegen:f_empty_value/1 1 0.00 0 [ 0.00] diameter_codegen:empty_value/1 1 0.00 0 [ 0.00] diameter_codegen:f_dict/1 1 0.00 0 [ 0.00] diameter_codegen:to_upper/1 1 0.00 0 [ 0.00] diameter_codegen:tu/1 4 0.00 0 [ 0.00] diameter_codegen:preprocess/2 1 0.00 0 [ 0.00] diameter_codegen:pp/2 1 0.00 0 [ 0.00] diameter_codegen:abstract_code/1 1 0.00 0 [ 0.00] diameter_codegen:'-empty_value/1-fun-1-'/1 1 0.00 0 [ 0.00] diameter_codegen:'-empty_value/1-fun-0-'/1 1 0.00 0 [ 0.00] diameter_codegen:'-f_avp_header/1-lc$^0/1-0-'/1 2 0.00 0 [ 0.00] diameter_codegen:'-f_msg_header/1-lc$^0/1-0-'/1 2 0.00 0 [ 0.00] diameter_codegen:'-enumerated_avp/1-fun-0-'/2 1 0.00 0 [ 0.00] diameter_codegen:'-avp/4-fun-4-'/3 1 0.00 0 [ 0.00] diameter_codegen:'-avp/4-fun-0-'/1 1 0.00 0 [ 0.00] diameter_codegen:'-avp_arity/1-fun-0-'/1 1 0.00 0 [ 0.00] diameter_codegen:'-avp_arities/1-fun-0-'/1 1 0.00 0 [ 0.00] diameter_codegen:'-f_avp_arity_1/1-lc$^0/1-0-'/1 2 0.00 0 [ 0.00] diameter_codegen:'-avp_name/1-fun-0-'/2 1 0.00 0 [ 0.00] diameter_codegen:'-name2rec/1-fun-0-'/1 1 0.00 0 [ 0.00] diameter_codegen:'-rec2msg/1-lc$^0/1-0-'/1 2 0.00 0 [ 0.00] diameter_codegen:'-msg2rec/1-lc$^0/1-0-'/1 2 0.00 0 [ 0.00] diameter_codegen:'-make_record_forms/1-lc$^2/1-0-'/2 2 0.00 0 [ 0.00] diameter_codegen:'-make_record_forms/1-fun-1-'/1 2 0.00 0 [ 0.00] diameter_codegen:'-make_hrl_forms/1-fun-0-'/1 1 0.00 0 [ 0.00] diameter_codegen:'-from_dict/4-after$^0/0-0-'/0 1 0.00 0 [ 0.00] filename:dirname/1 1 0.00 0 [ 0.00] code:is_loaded/1 1 0.00 0 [ 0.00] code:get_object_code/1 1 0.00 0 [ 0.00] code:lib_dir/1 1 0.00 0 [ 0.00] code:add_pathsa/1 1 0.00 0 [ 0.00] code:which/1 1 0.00 0 [ 0.00] file:read_file_info/1 2 0.00 0 [ 0.00] file:read_file/1 1 0.00 0 [ 0.00] file:call/2 3 0.00 0 [ 0.00] file:check_and_call/2 3 0.00 0 [ 0.00] dict:new/0 1 0.00 0 [ 0.00] dict:fetch/2 2 0.00 0 [ 0.00] dict:fold/3 3 0.00 0 [ 0.00] dict:fold_dict/3 3 0.00 0 [ 0.00] dict:mk_seg/1 1 0.00 0 [ 0.00] diameter_make:default/1 1 0.00 0 [ 0.00] diameter_make:def/2 1 0.00 0 [ 0.00] diameter_make:modes/1 2 0.00 0 [ 0.00] diameter_make:identify/1 1 0.00 0 [ 0.00] diameter_make:make/3 1 0.00 0 [ 0.00] diameter_make:ok/1 1 0.00 0 [ 0.00] diameter_make:make/4 1 0.00 0 [ 0.00] diameter_make:'-make/3-fun-0-'/5 1 0.00 0 [ 0.00] diameter_traffic_SUITE:here/0 1 0.00 0 [ 0.00] diameter_traffic_SUITE:'-compile_and_load/0-lc$^1/1-2-'/2 4 0.00 0 [ 0.00] diameter_dict_util:parse/2 1 0.00 0 [ 0.00] diameter_dict_util:do_parse/2 1 0.00 0 [ 0.00] diameter_dict_util:read/1 1 0.00 0 [ 0.00] diameter_dict_util:make_dict/2 1 0.00 0 [ 0.00] diameter_dict_util:make_orddict/1 1 0.00 0 [ 0.00] diameter_dict_util:make_dict/1 1 0.00 0 [ 0.00] diameter_dict_util:reset/2 1 0.00 0 [ 0.00] diameter_dict_util:reset/3 3 0.00 0 [ 0.00] diameter_dict_util:opt/2 3 0.00 0 [ 0.00] diameter_dict_util:reinherit/3 1 0.00 0 [ 0.00] diameter_dict_util:pass1/1 1 0.00 0 [ 0.00] diameter_dict_util:no_messages_without_id/1 1 0.00 0 [ 0.00] diameter_dict_util:vendor_id_mismatch/6 2 0.00 0 [ 0.00] diameter_dict_util:grouped_flags/4 2 0.00 0 [ 0.00] diameter_dict_util:pass2/1 1 0.00 0 [ 0.00] diameter_dict_util:p2/3 1 0.00 0 [ 0.00] diameter_dict_util:pass3/2 1 0.00 0 [ 0.00] diameter_dict_util:insert_codes/1 1 0.00 0 [ 0.00] diameter_dict_util:import_avps/2 1 0.00 0 [ 0.00] diameter_dict_util:explode_imports/2 1 0.00 0 [ 0.00] diameter_dict_util:import_groups/1 1 0.00 0 [ 0.00] diameter_dict_util:import_enums/1 1 0.00 0 [ 0.00] diameter_dict_util:import/2 2 0.00 0 [ 0.00] diameter_dict_util:import_key/2 2 0.00 0 [ 0.00] diameter_dict_util:inherit/2 1 0.00 0 [ 0.00] diameter_dict_util:inherit_avps/2 1 0.00 0 [ 0.00] diameter_dict_util:find_avps/2 1 0.00 0 [ 0.00] diameter_dict_util:avps_from_module/1 1 0.00 0 [ 0.00] diameter_dict_util:examine/1 1 0.00 0 [ 0.00] diameter_dict_util:putr/2 1 0.00 0 [ 0.00] diameter_dict_util:eraser/1 1 0.00 0 [ 0.00] diameter_dict_util:flatmap/2 2 0.00 0 [ 0.00] diameter_dict_util:'-flatmap/2-fun-0-'/2 2 0.00 0 [ 0.00] diameter_dict_util:'-inherit/2-fun-1-'/2 1 0.00 0 [ 0.00] diameter_dict_util:'-import/2-fun-0-'/2 2 0.00 0 [ 0.00] diameter_dict_util:'-import_avps/2-fun-0-'/2 1 0.00 0 [ 0.00] diameter_dict_util:'-import_avps/2-fun-2-'/1 1 0.00 0 [ 0.00] diameter_dict_util:'-pass2/1-fun-1-'/2 1 0.00 0 [ 0.00] diameter_dict_util:'-pass2/1-fun-0-'/3 1 0.00 0 [ 0.00] diameter_dict_util:'-opt/2-lc$^1/1-0-'/3 2 0.00 0 [ 0.00] diameter_dict_util:'-reset/3-fun-1-'/2 3 0.00 0 [ 0.00] diameter_dict_util:'-reset/2-fun-0-'/3 3 0.00 0 [ 0.00] diameter_dict_util:'-do_parse/2-fun-0-'/1 1 0.00 0 [ 0.00] diameter_dict_util:'-parse/2-after$^0/0-0-'/0 1 0.00 0 [ 0.00] erl_abstract_code:debug_info/4 1 0.00 0 [ 0.00] compile:env_default_opts/0 1 0.00 0 [ 0.00] compile:do_compile/2 1 0.00 0 [ 0.00] compile:expand_opts/1 1 0.00 0 [ 0.00] compile:expand_opt/2 3 0.00 0 [ 0.00] compile:'-expand_opts/1-anonymous-0-'/2 3 0.00 0 [ 0.00] diameter_dict_parser:parse/1 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars0/5 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars1/5 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_0/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_1/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_3/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_4/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_10/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_11/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_13/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_22/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_76/7 5 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_82/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_83/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_84/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_86/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_88/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_89/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_90/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_91/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_92/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_93/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_94/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_95/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_96/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_97/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_99/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_100/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_101/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_102/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_103/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_105/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_106/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_122/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccpars2_130/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccgoto_avp_code/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccgoto_avp_header/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccgoto_avp_header_tok/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccgoto_avp_names/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccgoto_dictionary/7 1 0.00 0 [ 0.00] diameter_dict_parser:yeccgoto_group_def/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccgoto_group_defs/7 2 0.00 0 [ 0.00] diameter_dict_parser:yeccgoto_module/7 1 0.00 0 [ 0.00] file:native_name_encoding/0 3 0.00 0 [ 0.00] erlang:binary_to_atom/2 1 0.00 0 [ 0.00] erlang:put/2 2 0.00 0 [ 0.00] gen:'-call/4-fun-0-'/4 5 0.00 1 [ 0.20] diameter_util:eprof/1 1 0.00 1 [ 1.00] lists:sort/1 2 0.00 1 [ 0.50] lists:ukeysort/2 2 0.00 1 [ 0.50] lists:foldr/3 4 0.00 1 [ 0.25] lists:mergel/2 5 0.00 1 [ 0.20] lists:rmergel/2 8 0.00 1 [ 0.13] lists:merge3_12_3/6 7 0.00 1 [ 0.14] lists:merge3_21_3/6 12 0.00 1 [ 0.08] lists:rmerge3_12_3/6 7 0.00 1 [ 0.14] lists:merge2_2/5 6 0.00 1 [ 0.17] lists:ukeysplit_1/8 6 0.00 1 [ 0.17] lists:rukeymerge3_2/11 5 0.00 1 [ 0.20] beam_lib:scan_beam/5 12 0.00 1 [ 0.08] beam_lib:chunk_to_data/6 1 0.00 1 [ 1.00] code_server:call/1 5 0.00 1 [ 0.20] diameter_codegen:getr/1 3 0.00 1 [ 0.33] diameter_codegen:msg_proj/1 10 0.00 1 [ 0.10] diameter_codegen:a_record/3 3 0.00 1 [ 0.33] diameter_codegen:c_msg2rec/2 10 0.00 1 [ 0.10] diameter_codegen:c_rec2msg/2 10 0.00 1 [ 0.10] diameter_codegen:f_avp_name/1 1 0.00 1 [ 1.00] diameter_codegen:avp_name/1 1 0.00 1 [ 1.00] diameter_codegen:vendor_id_map/1 2 0.00 1 [ 0.50] diameter_codegen:f_avp_arity_1/1 1 0.00 1 [ 1.00] diameter_codegen:avp_arities/1 1 0.00 1 [ 1.00] diameter_codegen:cs_enumerated_avp/3 9 0.00 1 [ 0.11] diameter_codegen:c_msg_header/4 10 0.00 1 [ 0.10] diameter_codegen:emf/2 15 0.00 1 [ 0.07] diameter_codegen:prefix/1 4 0.00 1 [ 0.25] diameter_codegen:files/2 6 0.00 1 [ 0.17] diameter_codegen:'-to_upper/1-fun-0-'/1 4 0.00 1 [ 0.25] diameter_codegen:'-f_enumerated_avp/1-lc$^0/1-0-'/1 4 0.00 1 [ 0.25] diameter_codegen:'-vendor_id_map/1-fun-0-'/1 4 0.00 1 [ 0.25] diameter_codegen:'-name2rec/1-fun-1-'/2 7 0.00 1 [ 0.14] diameter_codegen:'-msg_name/1-fun-0-'/1 5 0.00 1 [ 0.20] diameter_codegen:'-make_record_forms/1-fun-3-'/1 5 0.00 1 [ 0.20] filename:skip_prefix/2 7 0.00 1 [ 0.14] filename:maybe_remove_dirsep/2 9 0.00 1 [ 0.11] filename:unix_pathtype/1 7 0.00 1 [ 0.14] code:call/1 5 0.00 1 [ 0.20] file:file_name/1 3 0.00 1 [ 0.33] file:check_args/1 6 0.00 1 [ 0.17] dict:fetch_val/2 6 0.00 1 [ 0.17] dict:expand_segs/2 4 0.00 1 [ 0.25] diameter_gen_base_rfc3588:dict/0 3 0.00 1 [ 0.33] diameter_make:codec/2 1 0.00 1 [ 1.00] diameter_make:parse/2 1 0.00 1 [ 1.00] diameter_make:is_mode/1 10 0.00 1 [ 0.10] diameter_traffic_SUITE:'-compile_and_load/0-lc$^0/1-1-'/1 3 0.00 1 [ 0.33] diameter_traffic_SUITE:'-compile_and_load/0-lc$^2/1-3-'/4 4 0.00 1 [ 0.25] orddict:from_list/1 2 0.00 1 [ 0.50] diameter_dict_util:post/2 5 0.00 1 [ 0.20] diameter_dict_util:mk_code/2 5 0.00 1 [ 0.20] diameter_dict_util:'-inherit/2-lc$^0/1-0-'/1 6 0.00 1 [ 0.17] diameter_dict_util:'-pass1/1-fun-1-'/2 11 0.00 1 [ 0.09] diameter_dict_util:'-opt/2-fun-0-'/1 7 0.00 1 [ 0.14] erlang:spawn_monitor/1 1 0.00 1 [ 1.00] compile:forms/2 1 0.00 1 [ 1.00] diameter_dict_parser:yeccpars2_24/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_25/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_28/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_30/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_53/7 11 0.00 1 [ 0.09] diameter_dict_parser:yeccpars2_59/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_60/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_64/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_66/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_68/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_69/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_71/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_72/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_73/7 15 0.00 1 [ 0.07] diameter_dict_parser:yeccpars2_75/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_78/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_80/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_81/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccpars2_107/7 14 0.00 1 [ 0.07] diameter_dict_parser:yeccgoto_bit/7 15 0.00 1 [ 0.07] diameter_dict_parser:yeccgoto_bits/7 15 0.00 1 [ 0.07] diameter_dict_parser:yeccgoto_command_def/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccgoto_command_id/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccgoto_header/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccgoto_header_tok/7 10 0.00 1 [ 0.10] diameter_dict_parser:yeccgoto_message_defs/7 10 0.00 1 [ 0.10] erlang:erase/1 2 0.00 1 [ 0.50] gen:do_call/4 5 0.00 2 [ 0.40] lists:seq_loop/3 9 0.00 2 [ 0.22] beam_lib:get_data/8 11 0.00 2 [ 0.18] diameter_codegen:a_record/2 17 0.00 2 [ 0.12] diameter_codegen:c_name2rec/2 17 0.00 2 [ 0.12] diameter_codegen:c_avp_arities/1 17 0.00 2 [ 0.12] diameter_codegen:c_avp_arity/1 18 0.00 2 [ 0.11] diameter_codegen:c_avp_arity/2 17 0.00 2 [ 0.12] diameter_codegen:cs_enumerated_avp/1 14 0.00 2 [ 0.14] diameter_codegen:encode_msg_flags/1 10 0.00 2 [ 0.20] diameter_codegen:'-include/2-lc$^0/1-0-'/2 12 0.00 2 [ 0.17] diameter_codegen:'-msg_header/2-fun-0-'/2 10 0.00 2 [ 0.20] diameter_codegen:'-enumerated_avp/3-fun-0-'/3 9 0.00 2 [ 0.22] diameter_codegen:'-rec2msg/1-fun-1-'/2 10 0.00 2 [ 0.20] diameter_codegen:'-msg2rec/1-fun-1-'/2 10 0.00 2 [ 0.20] diameter_codegen:'-make_record_forms/1-fun-0-'/1 10 0.00 2 [ 0.20] filename:basename/1 7 0.00 2 [ 0.29] filename:join/1 9 0.00 2 [ 0.22] filename:join/2 7 0.00 2 [ 0.29] filename:pathtype/1 7 0.00 2 [ 0.29] filename:major_os_type/0 9 0.00 2 [ 0.22] code:load_binary/3 1 0.00 2 [ 2.00] diameter_make:'-modes/1-fun-0-'/1 10 0.00 2 [ 0.20] diameter_dict_util:do/2 3 0.00 2 [ 0.67] diameter_dict_util:make/3 18 0.00 2 [ 0.11] diameter_dict_util:opt/1 12 0.00 2 [ 0.17] diameter_dict_util:report/3 21 0.00 2 [ 0.10] diameter_dict_util:application_id_mismatch/3 10 0.00 2 [ 0.20] diameter_dict_util:dict/1 3 0.00 2 [ 0.67] diameter_dict_util:'-explode/3-fun-0-'/4 15 0.00 2 [ 0.13] diameter_dict_util:'-pass1/1-fun-0-'/3 19 0.00 2 [ 0.11] diameter_dict_util:'-make_dict/1-fun-0-'/2 19 0.00 2 [ 0.11] diameter_dict_util:'-make/2-fun-0-'/3 18 0.00 2 [ 0.11] diameter_dict_util:'-make_orddict/1-lc$^0/1-0-'/1 10 0.00 2 [ 0.20] diameter_dict_parser:yeccpars2_2/7 19 0.00 2 [ 0.11] diameter_dict_parser:yeccpars2_32/7 10 0.00 2 [ 0.20] diameter_dict_parser:yeccpars2_65/7 10 0.00 2 [ 0.20] diameter_dict_parser:yeccpars2_70/7 10 0.00 2 [ 0.20] diameter_dict_parser:yeccpars2_74/7 15 0.00 2 [ 0.13] diameter_dict_parser:yeccpars2_77/7 15 0.00 2 [ 0.13] diameter_dict_parser:yeccpars2_109/7 14 0.00 2 [ 0.14] diameter_dict_parser:yeccpars2_131/7 19 0.00 2 [ 0.11] diameter_dict_parser:yeccgoto_section/7 19 0.00 2 [ 0.11] diameter_dict_parser:yeccgoto_sections/7 19 0.00 2 [ 0.11] os:getenv/1 1 0.00 2 [ 2.00] ets:delete/1 1 0.00 2 [ 2.00] ets:new/2 1 0.00 2 [ 2.00] gen:do_for_proc/2 5 0.01 3 [ 0.60] beam_lib:del_chunk/2 11 0.01 3 [ 0.27] diameter_codegen:filter/1 23 0.01 3 [ 0.13] diameter_codegen:'-empty_value/1-lc$^2/1-0-'/2 15 0.01 3 [ 0.20] diameter_codegen:'-encode_msg_flags/1-fun-0-'/2 15 0.01 3 [ 0.20] diameter_codegen:'-enumerated_avp/1-fun-1-'/1 14 0.01 3 [ 0.21] diameter_codegen:'-f_avp/1-lc$^0/1-0-'/1 5 0.01 3 [ 0.60] diameter_codegen:'-make_hrl_forms/1-fun-1-'/1 17 0.01 3 [ 0.18] filename:separators/0 8 0.01 3 [ 0.38] filename:flatten/1 8 0.01 3 [ 0.38] dict:append/3 28 0.01 3 [ 0.11] diameter_traffic_SUITE:'-compile_and_load/0-after$^3/0-0-'/0 1 0.01 3 [ 3.00] diameter_dict_util:make_body/1 12 0.01 3 [ 0.25] diameter_dict_util:section/2 19 0.01 3 [ 0.16] diameter_dict_util:explode_avps/4 12 0.01 3 [ 0.25] diameter_dict_util:'-reset/3-lc$^0/1-0-'/2 18 0.01 3 [ 0.17] diameter_dict_util:'-mk/2-fun-0-'/1 15 0.01 3 [ 0.20] gen_server:call/3 5 0.01 3 [ 0.60] lists:seq/2 7 0.01 4 [ 0.57] lists:merge3_2/6 23 0.01 4 [ 0.17] lists:rmerge3_2/6 20 0.01 4 [ 0.20] diameter_dict_scanner:'-split/1-fun-4-'/1 20 0.01 4 [ 0.20] diameter_codegen:c_avp_arities/2 17 0.01 4 [ 0.24] diameter_codegen:c_empty_value/1 29 0.01 4 [ 0.14] diameter_codegen:'-c_avp_arity/1-fun-0-'/1 17 0.01 4 [ 0.24] diameter_codegen:'-a_record/3-fun-0-'/3 17 0.01 4 [ 0.24] orddict:fetch/2 25 0.01 4 [ 0.16] diameter_dict_util:make/2 12 0.01 4 [ 0.33] diameter_dict_util:p1/3 19 0.01 4 [ 0.21] diameter_dict_util:is_enumerated_avp/3 14 0.01 4 [ 0.29] diameter_dict_util:choose/3 24 0.01 4 [ 0.17] diameter_dict_util:'-import_key/2-fun-0-'/2 14 0.01 4 [ 0.29] lists:merge3_1/6 28 0.01 5 [ 0.18] lists:rmerge3_1/6 22 0.01 5 [ 0.23] diameter_codegen:c_imported_avp/2 44 0.01 5 [ 0.11] diameter_codegen:include/2 42 0.01 5 [ 0.12] diameter_codegen:'-include/2-fun-1-'/1 23 0.01 5 [ 0.22] diameter_codegen:'-avp_arities/1-fun-1-'/1 17 0.01 5 [ 0.29] diameter_dict_util:'-explode_imports/2-fun-0-'/4 50 0.01 5 [ 0.10] diameter_dict_parser:yeccpars2_52/7 51 0.01 5 [ 0.10] gen:call/4 5 0.01 6 [ 1.20] lists:delete/2 31 0.01 6 [ 0.19] lists:rmerge2_1/4 43 0.01 6 [ 0.14] diameter_codegen:c_imported_avp_header/3 50 0.01 6 [ 0.12] diameter_codegen:rec_name/2 27 0.01 6 [ 0.22] diameter_codegen:'-empty_value/1-fun-3-'/1 29 0.01 6 [ 0.21] dict:'-append/3-fun-0-'/3 28 0.01 6 [ 0.21] diameter_dict_util:report/2 21 0.01 6 [ 0.29] erlang:whereis/1 5 0.01 6 [ 1.20] erlang:atom_to_list/1 28 0.01 6 [ 0.21] lists:split_1_1/6 34 0.01 7 [ 0.21] lists:rmerge2_2/5 54 0.01 7 [ 0.13] beam_lib:pread/3 15 0.01 7 [ 0.47] diameter_codegen:imported_avp/3 50 0.01 7 [ 0.14] dict:fold_segs/4 51 0.01 7 [ 0.14] diameter_dict_util:xi/4 50 0.01 7 [ 0.14] diameter_dict_util:getr/1 21 0.01 7 [ 0.33] diameter_dict_parser:yeccpars2_54/7 51 0.01 7 [ 0.14] erlang:send/3 5 0.01 7 [ 1.40] diameter_codegen:get_value/2 33 0.02 8 [ 0.24] os:type/0 24 0.02 8 [ 0.33] diameter_dict_scanner:section/1 19 0.02 9 [ 0.47] diameter_codegen:eaf/2 80 0.02 9 [ 0.11] diameter_codegen:'-pp/2-fun-0-'/2 42 0.02 9 [ 0.21] filename:dirname/4 63 0.02 9 [ 0.14] diameter_dict_util:'-p2/3-fun-0-'/2 82 0.02 9 [ 0.11] diameter_dict_parser:yeccpars2_41/7 77 0.02 9 [ 0.12] diameter_dict_parser:yeccpars2_45/7 77 0.02 9 [ 0.12] diameter_dict_parser:yeccpars2_121/7 82 0.02 9 [ 0.11] diameter_dict_parser:yeccpars2_123/7 82 0.02 9 [ 0.11] diameter_dict_parser:yeccpars2_126/7 82 0.02 9 [ 0.11] diameter_dict_parser:yeccpars2_128/7 80 0.02 9 [ 0.11] diameter_dict_parser:yeccgoto_avp_defs/7 82 0.02 9 [ 0.11] diameter_dict_parser:yeccgoto_avp_flags/7 82 0.02 9 [ 0.11] diameter_dict_parser:yeccgoto_avp_type/7 82 0.02 9 [ 0.11] diameter_dict_parser:yeccgoto_qual/7 77 0.02 9 [ 0.12] erlang:system_info/1 24 0.02 9 [ 0.38] diameter_codegen:c_avp_header/3 83 0.02 10 [ 0.12] diameter_dict_util:'-import_avps/2-lc$^1/1-0-'/1 51 0.02 10 [ 0.20] diameter_dict_util:'-p1/3-fun-2-'/3 94 0.02 10 [ 0.11] diameter_dict_parser:yeccpars2_63/7 77 0.02 10 [ 0.13] diameter_dict_parser:yeccpars2_124/7 82 0.02 10 [ 0.12] diameter_dict_parser:yeccpars2_127/7 82 0.02 10 [ 0.12] diameter_dict_parser:yeccgoto_avp_def/7 82 0.02 10 [ 0.12] diameter_codegen:'-files/2-fun-0-'/1 50 0.02 11 [ 0.22] diameter_codegen:'-c_avp_header/3-fun-0-'/3 50 0.02 11 [ 0.22] diameter_codegen:'-cs_imported_avp/3-fun-0-'/2 50 0.02 11 [ 0.22] diameter_codegen:'-cs_imported_avp/3-fun-1-'/3 50 0.02 11 [ 0.22] diameter_codegen:'-avp/4-fun-1-'/2 50 0.02 11 [ 0.22] orddict:reverse_pairs/2 93 0.02 11 [ 0.12] diameter_dict_util:vendor/1 82 0.02 11 [ 0.13] diameter_dict_util:foldl/3 59 0.02 11 [ 0.19] diameter_dict_util:'-find_avps/2-lc$^0/1-0-'/1 51 0.02 11 [ 0.22] diameter_dict_util:'-make/3-fun-0-'/3 94 0.02 11 [ 0.12] diameter_codegen:'-c_imported_avp_name/2-fun-0-'/3 50 0.02 12 [ 0.24] diameter_dict_parser:yeccpars2_6/7 107 0.02 12 [ 0.11] diameter_dict_parser:yeccpars2_129/7 82 0.02 12 [ 0.15] erlang:demonitor/2 10 0.02 12 [ 1.20] proplists:get_value/2 53 0.03 13 [ 0.25] erlang:'--'/2 83 0.03 13 [ 0.16] diameter_codegen:not_in/2 132 0.03 14 [ 0.11] diameter_codegen:c_base_avp/1 88 0.03 14 [ 0.16] diameter_dict_util:explode/3 94 0.03 14 [ 0.15] diameter_dict_parser:yeccpars2_108/7 131 0.03 14 [ 0.11] diameter_dict_util:'-p1/3-fun-1-'/4 131 0.03 15 [ 0.11] diameter_dict_parser:yeccpars2_110/7 131 0.03 15 [ 0.11] diameter_dict_parser:yeccgoto_enum_def/7 131 0.03 15 [ 0.11] diameter_dict_parser:yeccgoto_enum_defs/7 131 0.03 15 [ 0.11] diameter_codegen:c_avp_name_/3 132 0.03 16 [ 0.12] diameter_codegen:c_enumerated_avp/2 131 0.03 16 [ 0.12] diameter_dict_util:qual/2 77 0.03 16 [ 0.21] diameter_dict_parser:yeccpars2_111/7 131 0.03 16 [ 0.12] lists:ukeysplit_2/5 81 0.04 17 [ 0.21] diameter_codegen:c_avp_name/3 132 0.04 17 [ 0.13] diameter_dict_util:explode_avps/2 82 0.04 17 [ 0.21] diameter_dict_parser:yeccpars2_112/7 131 0.04 17 [ 0.13] diameter_codegen:'-encode_avp_flags/1-fun-0-'/2 80 0.04 18 [ 0.23] diameter_codegen:'-avp/4-fun-5-'/2 82 0.04 18 [ 0.22] diameter_codegen:'-types/1-fun-0-'/1 82 0.04 18 [ 0.22] dict:append_bkt/3 85 0.04 18 [ 0.21] diameter_make:is_path/1 136 0.04 18 [ 0.13] lists:split_1/5 96 0.04 19 [ 0.20] diameter_codegen:'-avp_header/1-fun-0-'/3 83 0.04 19 [ 0.23] diameter_codegen:'-avp/4-fun-6-'/1 82 0.04 19 [ 0.23] diameter_codegen:'-avp_name/1-fun-1-'/3 82 0.04 19 [ 0.23] diameter_dict_util:explode2/4 131 0.04 19 [ 0.15] diameter_codegen:encode_avp_flags/1 82 0.04 20 [ 0.24] diameter_dict_util:vendor_id/2 82 0.04 20 [ 0.24] diameter_dict_util:'-avp_flags_valid/3-fun-0-'/1 80 0.04 20 [ 0.25] diameter_dict_util:avp_type_known/3 82 0.04 21 [ 0.26] diameter_dict_util:avp_vendor_id/4 82 0.04 21 [ 0.26] orddict:find/2 156 0.05 22 [ 0.14] diameter_dict_util:avp_flags_valid/3 82 0.05 22 [ 0.27] lists:filter/2 86 0.05 23 [ 0.27] diameter_codegen:v/4 214 0.05 23 [ 0.11] lists:keymember/3 56 0.05 23 [ 0.41] diameter_dict_scanner:'-split/1-fun-1-'/1 109 0.05 24 [ 0.22] erlang:monitor/2 10 0.05 24 [ 2.40] filename:basename1/3 251 0.06 28 [ 0.11] diameter_dict_util:mk/2 109 0.06 28 [ 0.26] diameter_codegen:'-cs_enumerated_avp/1-fun-0-'/2 131 0.06 30 [ 0.23] diameter_dict_util:'-mk/2-fun-1-'/1 131 0.06 30 [ 0.23] erlang:list_to_integer/1 226 0.07 32 [ 0.14] diameter_dict_parser:yeccpars2_57/7 314 0.07 34 [ 0.11] diameter_dict_parser:yeccpars2_43/7 298 0.08 38 [ 0.13] diameter_dict_parser:yeccpars2_51/7 354 0.08 40 [ 0.11] diameter_dict_parser:yeccgoto_avps/7 375 0.08 40 [ 0.11] erlang:bitstring_to_list/1 284 0.08 40 [ 0.14] dict:maybe_expand_segs/1 152 0.09 41 [ 0.27] diameter_dict_parser:yeccpars2_47/7 365 0.09 41 [ 0.11] diameter_dict_util:delim/2 375 0.09 42 [ 0.11] diameter_dict_parser:yeccpars2_50/7 364 0.09 43 [ 0.12] diameter_dict_parser:yeccpars2_58/7 314 0.09 43 [ 0.14] diameter_dict_parser:yeccgoto_avp_name/7 365 0.09 43 [ 0.12] diameter_codegen:field/1 385 0.09 44 [ 0.11] diameter_dict_parser:yeccgoto_avp/7 375 0.09 44 [ 0.12] diameter_dict_parser:yeccgoto_avp_spec/7 364 0.09 44 [ 0.12] diameter_codegen:c_arity/2 385 0.09 45 [ 0.12] file:file_name_1/2 196 0.09 45 [ 0.23] diameter_dict_util:avp/1 375 0.09 45 [ 0.12] diameter_dict_parser:yeccpars2_23/7 374 0.09 45 [ 0.12] diameter_dict_parser:yeccgoto_avp_ref/7 375 0.09 45 [ 0.12] diameter_dict_parser:yeccgoto_diameter_name/7 374 0.09 45 [ 0.12] diameter_dict_parser:yeccpars2_44/7 375 0.10 46 [ 0.12] diameter_dict_parser:yeccpars2_62/7 375 0.10 47 [ 0.13] diameter_codegen:'-c_avp_arities/2-lc$^0/1-0-'/1 402 0.10 50 [ 0.12] diameter_dict_util:is_uint32/2 228 0.11 51 [ 0.22] lists:'-filter/2-lc$^0/1-0-'/2 308 0.11 52 [ 0.17] diameter_dict_util:avp/2 375 0.11 52 [ 0.14] diameter_codegen:vid/4 214 0.11 54 [ 0.25] diameter_codegen:arity/2 243 0.11 55 [ 0.23] diameter_dict_scanner:read_int/1 226 0.12 59 [ 0.26] diameter_dict_util:type/1 164 0.13 61 [ 0.37] filename:join1/4 476 0.14 66 [ 0.14] erlang:list_to_tuple/1 541 0.14 67 [ 0.12] filename:do_flatten/2 314 0.15 70 [ 0.22] diameter_dict_util:xa/5 422 0.15 72 [ 0.17] lists:member/2 554 0.16 75 [ 0.14] diameter_dict_util:key/1 131 0.16 76 [ 0.58] diameter_dict_util:'-make_body/1-fun-0-'/1 375 0.17 80 [ 0.21] diameter_dict_util:avp_is_defined/3 364 0.17 83 [ 0.23] diameter_dict_util:make_code/3 734 0.17 84 [ 0.11] diameter_codegen:'-a_record/2-fun-0-'/1 385 0.18 86 [ 0.22] diameter_dict_util:mo/3 838 0.18 86 [ 0.10] diameter_dict_parser:yeccpars2_19/7 766 0.18 89 [ 0.12] diameter_codegen:'-c_avp_arity/2-fun-0-'/2 385 0.19 90 [ 0.23] diameter_dict_scanner:'-split/1-fun-2-'/1 395 0.19 91 [ 0.23] lists:flatmap/2 396 0.20 95 [ 0.24] dict:put_bucket_s/3 304 0.20 95 [ 0.31] diameter_dict_parser:yeccgoto_ident/7 766 0.20 96 [ 0.13] dict:store/3 829 0.21 101 [ 0.12] dict:maybe_expand/2 857 0.21 101 [ 0.12] dict:fold_seg/4 816 0.22 106 [ 0.13] erlang:'++'/2 762 0.23 110 [ 0.14] lists:foldl/3 842 0.23 112 [ 0.13] diameter_dict_util:eval/1 845 0.24 115 [ 0.14] dict:maybe_expand_aux/2 857 0.25 122 [ 0.14] diameter_dict_util:x/3 838 0.27 128 [ 0.15] diameter_codegen:'-c_avp_arities/2-lc$^1/1-1-'/2 770 0.27 129 [ 0.17] diameter_dict_util:'-foldl/3-fun-0-'/3 543 0.27 132 [ 0.24] diameter_dict_util:'-insert_codes/1-fun-0-'/3 734 0.32 152 [ 0.21] diameter_dict_util:find/2 561 0.32 155 [ 0.28] erl_parse:not_string/4 1467 0.32 155 [ 0.11] diameter_dict_util:'-make_orddict/1-fun-1-'/3 838 0.36 175 [ 0.21] diameter_dict_util:store_new/5 821 0.37 177 [ 0.22] erlang:binary_to_list/1 12 0.37 180 [ 15.00] diameter_dict_util:'-examine/1-fun-0-'/4 838 0.38 183 [ 0.22] erlang:make_fun/3 1046 0.38 185 [ 0.18] dict:'-store/3-fun-0-'/3 829 0.42 200 [ 0.24] erlang:binary_to_term/1 1 0.44 214 [ 214.00] epp:default_encoding/0 1044 0.47 226 [ 0.22] diameter_dict_parser:yeccpars2/7 1954 0.47 227 [ 0.12] diameter_dict_scanner:is_digit/1 2119 0.49 235 [ 0.11] lists:dropwhile/2 1847 0.51 244 [ 0.13] erl_parse:enc_func/1 1044 0.51 244 [ 0.23] diameter_dict_parser:yeccpars1/7 1953 0.51 245 [ 0.13] lists:reverse/2 1594 0.52 249 [ 0.16] diameter_dict_scanner:splitwith/2 1040 0.52 251 [ 0.24] lists:splitwith/2 1067 0.54 258 [ 0.24] dict:on_bucket/3 857 0.54 259 [ 0.30] dict:get_bucket_s/2 1538 0.54 261 [ 0.17] dict:rehash/4 1206 0.58 280 [ 0.23] erlang:tuple_to_list/1 2311 0.62 297 [ 0.13] erl_parse:'-abstract/3-lc$^0/1-0-'/2 1420 0.64 309 [ 0.22] diameter_dict_scanner:scan/2 2695 0.65 315 [ 0.12] diameter_dict_scanner:word/1 814 0.66 318 [ 0.39] dict:get_bucket/2 1386 0.68 325 [ 0.23] dict:find/2 1384 0.68 326 [ 0.24] erl_parse:abstract/2 1044 0.68 326 [ 0.31] erl_parse:abstract_byte/2 1136 0.69 331 [ 0.29] diameter_codegen:avp_info/1 1398 0.83 400 [ 0.29] lists:map/2 1900 0.89 428 [ 0.23] diameter_dict_scanner:is_eol_ch/1 1847 0.91 440 [ 0.24] diameter_dict_scanner:'-split/1-fun-0-'/1 1847 0.93 446 [ 0.24] diameter_dict_scanner:tok/2 1953 0.93 449 [ 0.23] dict:fold_bucket/3 3178 1.00 483 [ 0.15] lists:reverse/1 2080 1.04 499 [ 0.24] dict:get_slot/2 2243 1.18 568 [ 0.25] diameter_dict_scanner:acc/3 2694 1.28 618 [ 0.23] diameter_codegen:remod/2 2872 1.32 633 [ 0.22] proplists:get_value/3 3492 1.39 668 [ 0.19] diameter_codegen:'-remod/2-lc$^0/1-0-'/2 3058 1.41 677 [ 0.22] erl_anno:new_location/1 7098 1.51 728 [ 0.10] erlang:spawn_opt/1 1 1.52 731 [ 731.00] orddict:store/3 3559 1.74 837 [ 0.24] erlang:setelement/3 3892 1.78 856 [ 0.22] diameter_dict_scanner:is_upper/1 4100 1.83 882 [ 0.22] erlang:phash/2 3297 1.95 940 [ 0.29] dict:find_val/2 7390 2.16 1041 [ 0.14] dict:store_bkt_val/3 4859 2.32 1114 [ 0.23] diameter_dict_scanner:is_name_ch/1 9714 2.32 1116 [ 0.11] lists:splitwith/3 9792 2.53 1216 [ 0.12] erl_parse:abstract_tuple_list/3 5646 2.72 1311 [ 0.23] erlang:list_to_atom/1 3609 2.76 1327 [ 0.37] erl_anno:new/1 7098 3.44 1653 [ 0.23] erl_parse:abstract_list/4 13813 3.77 1814 [ 0.13] erl_parse:abstract/3 7851 3.82 1840 [ 0.23] diameter_dict_scanner:'-split/1-fun-3-'/1 9210 4.24 2040 [ 0.22] diameter_dict_scanner:is_lower/1 9714 4.43 2133 [ 0.22] diameter_dict_scanner:is_alphanum/1 9714 4.50 2164 [ 0.22] diameter_codegen:is_printable_ascii/1 11352 5.25 2525 [ 0.22] diameter_dict_scanner:split/1 13351 5.39 2593 [ 0.19] ------------------------------------------------------------ ------ ------- ----- [----------] Total: 246336 100.00% 48114 [ 0.20] ****** Process <0.107.0> -- 98.89 % of profiled time *** FUNCTION CALLS % TIME [uS / CALLS] -------- ----- ------- ---- [----------] sys_core_bsm:module/2 1 0.00 0 [ 0.00] sys_core_bsm:bsm_an_2/4 1 0.00 0 [ 0.00] sys_core_bsm:bsm_an_3/4 1 0.00 0 [ 0.00] sys_core_bsm:bsm_nonempty/2 1 0.00 0 [ 0.00] sys_core_bsm:bsm_ensure_no_partition/2 1 0.00 0 [ 0.00] sys_core_bsm:bsm_ensure_no_partition_1/3 1 0.00 0 [ 0.00] erl_internal:add_predefined_functions/1 1 0.00 0 [ 0.00] erl_internal:predefined_functions/1 1 0.00 0 [ 0.00] erl_internal:get_optional_callbacks/1 1 0.00 0 [ 0.00] erl_internal:module_predef_func_beh_info/2 1 0.00 0 [ 0.00] erl_internal:module_predef_funcs_mod_info/1 1 0.00 0 [ 0.00] v3_core:expr_map/4 3 0.00 0 [ 0.00] v3_core:badmap_term/2 3 0.00 0 [ 0.00] v3_life:module/2 1 0.00 0 [ 0.00] v3_life:'-literal/2-lc$^0/1-1-'/2 3 0.00 0 [ 0.00] beam_record:module/2 1 0.00 0 [ 0.00] sys_core_fold:module/2 1 0.00 0 [ 0.00] sets:from_list/1 1 0.00 0 [ 0.00] sets:mk_seg/1 2 0.00 0 [ 0.00] lists:nth/2 3 0.00 0 [ 0.00] lists:sort/2 1 0.00 0 [ 0.00] lists:rukeymerge3_2/11 2 0.00 0 [ 0.00] lists:rukeymerge3_21_3/11 3 0.00 0 [ 0.00] lists:ukeymerge2_1/7 3 0.00 0 [ 0.00] diameter_exprecs:parse_transform/2 1 0.00 0 [ 0.00] diameter_exprecs:a_export/1 1 0.00 0 [ 0.00] diameter_exprecs:f_accessors/2 1 0.00 0 [ 0.00] diameter_exprecs:'#info-/1'/0 1 0.00 0 [ 0.00] diameter_exprecs:'#info-/2'/1 1 0.00 0 [ 0.00] diameter_exprecs:'#new-/1'/1 1 0.00 0 [ 0.00] diameter_exprecs:'#new-/2'/1 1 0.00 0 [ 0.00] diameter_exprecs:'#get-/1'/1 1 0.00 0 [ 0.00] diameter_exprecs:'#get-/2'/1 1 0.00 0 [ 0.00] diameter_exprecs:'#set-/2'/1 1 0.00 0 [ 0.00] diameter_exprecs:'-#set-/2/1-lc$^0/1-0-'/1 3 0.00 0 [ 0.00] diameter_exprecs:'-#get-/2/1-lc$^0/1-0-'/1 3 0.00 0 [ 0.00] diameter_exprecs:'-#get-/1/1-lc$^0/1-0-'/1 2 0.00 0 [ 0.00] diameter_exprecs:'-#new-/2/1-lc$^0/1-0-'/1 3 0.00 0 [ 0.00] diameter_exprecs:'-#new-/1/1-lc$^0/1-0-'/1 2 0.00 0 [ 0.00] diameter_exprecs:'-#info-/2/1-lc$^0/1-0-'/1 3 0.00 0 [ 0.00] zlib:open/0 1 0.00 0 [ 0.00] zlib:close/1 1 0.00 0 [ 0.00] zlib:deflateInit/2 1 0.00 0 [ 0.00] zlib:deflate/3 1 0.00 0 [ 0.00] zlib:deflateEnd/1 1 0.00 0 [ 0.00] zlib:compress/1 1 0.00 0 [ 0.00] zlib:collect/1 1 0.00 0 [ 0.00] zlib:arg_flush/1 1 0.00 0 [ 0.00] zlib:arg_level/1 1 0.00 0 [ 0.00] zlib:reverse/1 1 0.00 0 [ 0.00] zlib:reverse/2 3 0.00 0 [ 0.00] zlib:'-compress/1-after$^0/0-0-'/1 1 0.00 0 [ 0.00] code_server:call/1 1 0.00 0 [ 0.00] beam_peep:module/2 1 0.00 0 [ 0.00] beam_jump:module/2 1 0.00 0 [ 0.00] beam_flatten:module/2 1 0.00 0 [ 0.00] v3_codegen:functions/2 1 0.00 0 [ 0.00] v3_codegen:'-set_cg/6-anonymous-2-'/2 3 0.00 0 [ 0.00] v3_kernel:module/2 1 0.00 0 [ 0.00] v3_kernel:expr_map/5 3 0.00 0 [ 0.00] v3_kernel:map_group_pairs/5 3 0.00 0 [ 0.00] v3_kernel:'-map_split_pairs_1/5-anonymous-0-'/1 3 0.00 0 [ 0.00] v3_kernel:'-map_split_pairs/5-anonymous-1-'/3 3 0.00 0 [ 0.00] beam_z:module/2 1 0.00 0 [ 0.00] filename:skip_prefix/2 1 0.00 0 [ 0.00] filename:basename/2 1 0.00 0 [ 0.00] filename:basename/4 1 0.00 0 [ 0.00] filename:dirname/1 1 0.00 0 [ 0.00] filename:dirname/4 1 0.00 0 [ 0.00] filename:separators/0 2 0.00 0 [ 0.00] filename:do_flatten/2 3 0.00 0 [ 0.00] beam_receive:module/2 1 0.00 0 [ 0.00] code:ensure_loaded/1 1 0.00 0 [ 0.00] code:call/1 1 0.00 0 [ 0.00] dict:fetch_keys/1 1 0.00 0 [ 0.00] dict:'-to_list/1-fun-0-'/3 3 0.00 0 [ 0.00] os:type/0 2 0.00 0 [ 0.00] beam_opcodes:format_number/0 1 0.00 0 [ 0.00] beam_validator:validate/2 1 0.00 0 [ 0.00] beam_validator:verify_put_map/6 3 0.00 0 [ 0.00] beam_validator:assert_unique_map_keys/1 3 0.00 0 [ 0.00] beam_bsm:module/2 1 0.00 0 [ 0.00] beam_asm:module/5 1 0.00 0 [ 0.00] beam_asm:assemble/5 1 0.00 0 [ 0.00] beam_asm:on_load/2 1 0.00 0 [ 0.00] beam_asm:build_file/9 1 0.00 0 [ 0.00] beam_asm:finalize_fun_table/2 1 0.00 0 [ 0.00] beam_asm:build_form/2 1 0.00 0 [ 0.00] beam_asm:flatten_exports/1 2 0.00 0 [ 0.00] beam_asm:flatten_imports/1 1 0.00 0 [ 0.00] beam_asm:build_attributes/4 1 0.00 0 [ 0.00] beam_asm:build_line_table/1 1 0.00 0 [ 0.00] beam_asm:set_vsn_attribute/2 1 0.00 0 [ 0.00] beam_asm:'-build_line_table/1-lbc$^1/2-1-'/2 2 0.00 0 [ 0.00] beam_asm:'-build_line_table/1-lc$^0/1-0-'/1 1 0.00 0 [ 0.00] beam_asm:'-build_file/9-lc$^1/1-1-'/1 2 0.00 0 [ 0.00] beam_block:module/2 1 0.00 0 [ 0.00] unicode:characters_to_binary/1 1 0.00 0 [ 0.00] beam_dict:new/0 1 0.00 0 [ 0.00] beam_dict:atom_table/2 1 0.00 0 [ 0.00] beam_dict:string_table/1 1 0.00 0 [ 0.00] beam_dict:lambda_table/1 1 0.00 0 [ 0.00] beam_dict:line_table/1 1 0.00 0 [ 0.00] beam_dict:'-line_table/1-lc$^0/1-0-'/1 3 0.00 0 [ 0.00] orddict:to_list/1 3 0.00 0 [ 0.00] erl_lint:value_option/7 1 0.00 0 [ 0.00] erl_lint:module/3 1 0.00 0 [ 0.00] erl_lint:compiler_options/1 1 0.00 0 [ 0.00] erl_lint:start/2 1 0.00 0 [ 0.00] erl_lint:return_status/1 1 0.00 0 [ 0.00] erl_lint:pack_errors/1 1 0.00 0 [ 0.00] erl_lint:pack_warnings/1 2 0.00 0 [ 0.00] erl_lint:forms/2 1 0.00 0 [ 0.00] erl_lint:includes_qlc_hrl/2 1 0.00 0 [ 0.00] erl_lint:eval_file_attribute/2 1 0.00 0 [ 0.00] erl_lint:start_state/2 1 0.00 0 [ 0.00] erl_lint:eof/2 1 0.00 0 [ 0.00] erl_lint:bif_clashes/2 1 0.00 0 [ 0.00] erl_lint:not_deprecated/2 1 0.00 0 [ 0.00] erl_lint:disallowed_compile_flags/2 1 0.00 0 [ 0.00] erl_lint:post_traversal_check/2 1 0.00 0 [ 0.00] erl_lint:check_behaviour/1 1 0.00 0 [ 0.00] erl_lint:behaviour_check/2 1 0.00 0 [ 0.00] erl_lint:all_behaviour_callbacks/3 1 0.00 0 [ 0.00] erl_lint:behaviour_missing_callbacks/2 1 0.00 0 [ 0.00] erl_lint:behaviour_conflicting/2 1 0.00 0 [ 0.00] erl_lint:behaviour_add_conflicts/2 1 0.00 0 [ 0.00] erl_lint:check_deprecated/2 1 0.00 0 [ 0.00] erl_lint:check_imports/2 1 0.00 0 [ 0.00] erl_lint:check_inlines/2 1 0.00 0 [ 0.00] erl_lint:check_unused_functions/2 1 0.00 0 [ 0.00] erl_lint:check_undefined_functions/1 1 0.00 0 [ 0.00] erl_lint:check_undefined_types/1 1 0.00 0 [ 0.00] erl_lint:check_bif_clashes/2 1 0.00 0 [ 0.00] erl_lint:check_option_functions/4 3 0.00 0 [ 0.00] erl_lint:nowarn_function/2 1 0.00 0 [ 0.00] erl_lint:func_line_warning/3 1 0.00 0 [ 0.00] erl_lint:func_line_error/3 3 0.00 0 [ 0.00] erl_lint:check_untyped_records/2 1 0.00 0 [ 0.00] erl_lint:check_unused_records/2 1 0.00 0 [ 0.00] erl_lint:check_callback_information/1 1 0.00 0 [ 0.00] erl_lint:export/3 2 0.00 0 [ 0.00] erl_lint:exports/1 1 0.00 0 [ 0.00] erl_lint:check_on_load/1 1 0.00 0 [ 0.00] erl_lint:check_specs_without_function/1 1 0.00 0 [ 0.00] erl_lint:check_functions_without_spec/2 1 0.00 0 [ 0.00] erl_lint:check_unused_types/2 1 0.00 0 [ 0.00] erl_lint:check_local_opaque_types/1 1 0.00 0 [ 0.00] erl_lint:check_dialyzer_attribute/2 1 0.00 0 [ 0.00] erl_lint:merge_state/2 1 0.00 0 [ 0.00] erl_lint:local_functions/1 1 0.00 0 [ 0.00] erl_lint:auto_import_suppressed/1 1 0.00 0 [ 0.00] erl_lint:'-auto_import_suppressed/1-lc$^1/1-1-'/1 1 0.00 0 [ 0.00] erl_lint:'-auto_import_suppressed/1-lc$^0/1-0-'/1 6 0.00 0 [ 0.00] erl_lint:'-vtmerge_pat/2-fun-0-'/3 1 0.00 0 [ 0.00] erl_lint:'-expr/3-fun-1-'/3 3 0.00 0 [ 0.00] erl_lint:'-nowarn_function/2-lc$^0/1-0-'/2 6 0.00 0 [ 0.00] erl_lint:'-check_option_functions/4-lc$^4/1-4-'/2 3 0.00 0 [ 0.00] erl_lint:'-check_option_functions/4-lc$^1/1-1-'/4 6 0.00 0 [ 0.00] erl_lint:'-check_undefined_types/1-lc$^0/1-0-'/3 1 0.00 0 [ 0.00] erl_lint:'-behaviour_check/2-lc$^2/1-1-'/2 1 0.00 0 [ 0.00] erl_lint:'-not_deprecated/2-lc$^5/1-5-'/1 1 0.00 0 [ 0.00] erl_lint:'-not_deprecated/2-lc$^4/1-4-'/1 1 0.00 0 [ 0.00] erl_lint:'-not_deprecated/2-lc$^3/1-3-'/1 1 0.00 0 [ 0.00] erl_lint:'-not_deprecated/2-lc$^1/1-1-'/3 2 0.00 0 [ 0.00] erl_lint:'-pack_warnings/1-lc$^0/1-2-'/1 2 0.00 0 [ 0.00] erl_lint:'-pack_warnings/1-lc$^1/1-0-'/2 2 0.00 0 [ 0.00] erl_expand_records:module/2 1 0.00 0 [ 0.00] erl_expand_records:compiler_options/1 1 0.00 0 [ 0.00] erl_expand_records:init_calltype/1 1 0.00 0 [ 0.00] erl_expand_records:record_match/6 2 0.00 0 [ 0.00] sofs:from_external/2 1 0.00 0 [ 0.00] sofs:set/2 1 0.00 0 [ 0.00] sofs:converse/1 1 0.00 0 [ 0.00] sofs:drestriction/2 1 0.00 0 [ 0.00] sofs:drestriction/3 1 0.00 0 [ 0.00] sofs:family_to_relation/1 1 0.00 0 [ 0.00] sofs:family_specification/2 1 0.00 0 [ 0.00] sofs:is_element_type/1 1 0.00 0 [ 0.00] sofs:rel_type/3 1 0.00 0 [ 0.00] sofs:relprod/2 1 0.00 0 [ 0.00] sofs:relprod1/2 1 0.00 0 [ 0.00] sofs:converse/2 1 0.00 0 [ 0.00] sofs:restrict/2 1 0.00 0 [ 0.00] sofs:family2rel/2 1 0.00 0 [ 0.00] sofs:fam_spec/4 1 0.00 0 [ 0.00] beam_a:module/2 1 0.00 0 [ 0.00] beam_trim:module/2 1 0.00 0 [ 0.00] sys_core_dsetel:module/2 1 0.00 0 [ 0.00] sys_core_dsetel:visit_module/1 1 0.00 0 [ 0.00] cerl:module_name/1 1 0.00 0 [ 0.00] cerl:map_es/1 3 0.00 0 [ 0.00] cerl:map_arg/1 3 0.00 0 [ 0.00] cerl:update_c_map/3 3 0.00 0 [ 0.00] cerl:map_pair_key/1 3 0.00 0 [ 0.00] cerl:map_pair_val/1 3 0.00 0 [ 0.00] cerl:map_pair_op/1 3 0.00 0 [ 0.00] cerl:ann_c_map_pair/4 3 0.00 0 [ 0.00] cerl:update_c_map_pair/4 3 0.00 0 [ 0.00] cerl:is_c_fname/1 3 0.00 0 [ 0.00] erlang:open_port/2 1 0.00 0 [ 0.00] erlang:port_command/2 1 0.00 0 [ 0.00] erlang:port_close/1 1 0.00 0 [ 0.00] erl_parse:new_anno/1 2 0.00 0 [ 0.00] compile:expand_opts/1 1 0.00 0 [ 0.00] compile:expand_opt/2 5 0.00 0 [ 0.00] compile:internal/2 1 0.00 0 [ 0.00] compile:build_compile/1 1 0.00 0 [ 0.00] compile:internal_comp/5 1 0.00 0 [ 0.00] compile:comp_ret_ok/2 1 0.00 0 [ 0.00] compile:werror/1 1 0.00 0 [ 0.00] compile:messages_per_file/1 1 0.00 0 [ 0.00] compile:mpf/1 2 0.00 0 [ 0.00] compile:passes/2 1 0.00 0 [ 0.00] compile:fix_first_pass/1 1 0.00 0 [ 0.00] compile:standard_passes/0 1 0.00 0 [ 0.00] compile:core_passes/0 1 0.00 0 [ 0.00] compile:kernel_passes/0 1 0.00 0 [ 0.00] compile:transform_module/2 1 0.00 0 [ 0.00] compile:foldl_transform/3 2 0.00 0 [ 0.00] compile:core_transforms/2 1 0.00 0 [ 0.00] compile:foldl_core_transforms/3 1 0.00 0 [ 0.00] compile:get_module/1 1 0.00 0 [ 0.00] compile:add_default_base/2 1 0.00 0 [ 0.00] compile:lint_module/2 1 0.00 0 [ 0.00] compile:expand_records/2 1 0.00 0 [ 0.00] compile:core/2 1 0.00 0 [ 0.00] compile:v3_kernel/2 1 0.00 0 [ 0.00] compile:test_old_inliner/1 2 0.00 0 [ 0.00] compile:test_core_inliner/1 2 0.00 0 [ 0.00] compile:test_any_inliner/1 1 0.00 0 [ 0.00] compile:save_abstract_code/2 1 0.00 0 [ 0.00] compile:keep_compile_option/1 4 0.00 0 [ 0.00] compile:beam_asm/2 1 0.00 0 [ 0.00] compile:test_native/1 1 0.00 0 [ 0.00] compile:is_native_enabled/1 6 0.00 0 [ 0.00] compile:report_warnings/1 1 0.00 0 [ 0.00] compile:outfile/3 1 0.00 0 [ 0.00] compile:objfile/2 1 0.00 0 [ 0.00] compile:'-core_transforms/2-lc$^0/1-0-'/1 6 0.00 0 [ 0.00] compile:'-foldl_transform/3-anonymous-2-'/3 1 0.00 0 [ 0.00] compile:'-foldl_transform/3-anonymous-0-'/3 1 0.00 0 [ 0.00] compile:'-asm_passes/0-anonymous-4-'/2 1 0.00 0 [ 0.00] compile:'-asm_passes/0-anonymous-0-'/1 1 0.00 0 [ 0.00] compile:'-kernel_passes/0-anonymous-2-'/2 1 0.00 0 [ 0.00] compile:'-core_passes/0-anonymous-7-'/2 1 0.00 0 [ 0.00] compile:'-core_passes/0-anonymous-5-'/1 1 0.00 0 [ 0.00] compile:'-core_passes/0-anonymous-3-'/1 1 0.00 0 [ 0.00] compile:'-core_passes/0-anonymous-1-'/1 1 0.00 0 [ 0.00] compile:'-standard_passes/0-anonymous-6-'/2 1 0.00 0 [ 0.00] compile:'-standard_passes/0-anonymous-5-'/2 1 0.00 0 [ 0.00] compile:'-standard_passes/0-anonymous-4-'/2 1 0.00 0 [ 0.00] compile:'-standard_passes/0-anonymous-3-'/2 1 0.00 0 [ 0.00] compile:'-standard_passes/0-anonymous-0-'/2 1 0.00 0 [ 0.00] compile:'-messages_per_file/1-anonymous-5-'/1 2 0.00 0 [ 0.00] compile:'-messages_per_file/1-lc$^0/1-0-'/1 1 0.00 0 [ 0.00] compile:'-expand_opts/1-anonymous-0-'/2 5 0.00 0 [ 0.00] compile:'-do_compile/2-anonymous-1-'/1 1 0.00 0 [ 0.00] compile:'-do_compile/2-anonymous-0-'/2 1 0.00 0 [ 0.00] beam_dead:module/2 1 0.00 0 [ 0.00] beam_dead:equal_ops/2 1 0.00 0 [ 0.00] beam_dead:shortcut_bs_ctb_1/4 1 0.00 0 [ 0.00] beam_split:module/2 1 0.00 0 [ 0.00] beam_except:module/2 1 0.00 0 [ 0.00] ordsets:new/0 1 0.00 0 [ 0.00] erlang:system_info/1 2 0.00 0 [ 0.00] erlang:make_tuple/2 2 0.00 0 [ 0.00] erlang:exit/1 1 0.00 0 [ 0.00] beam_clean:module/2 1 0.00 1 [ 1.00] beam_clean:rootset/3 1 0.00 1 [ 1.00] beam_clean:bs_fix/1 1 0.00 1 [ 1.00] sys_core_bsm:bsm_ensure_no_partition_2/5 3 0.00 1 [ 0.33] erl_internal:'-predefined_functions/1-lc$^3/1-3-'/1 3 0.00 1 [ 0.33] erl_internal:'-predefined_functions/1-lc$^2/1-2-'/1 3 0.00 1 [ 0.33] v3_core:module/2 1 0.00 1 [ 1.00] v3_core:map_build_pairs/4 3 0.00 1 [ 0.33] v3_core:maybe_warn_repeated_keys/4 3 0.00 1 [ 0.33] v3_core:is_valid_map_src/1 3 0.00 1 [ 0.33] v3_core:'-is_simple/1-anonymous-1-'/1 8 0.00 1 [ 0.13] v3_core:'-is_simple/1-anonymous-0-'/1 6 0.00 1 [ 0.17] sys_core_fold:sub_subst_scope/1 3 0.00 1 [ 0.33] cerl_sets:to_list/1 3 0.00 1 [ 0.33] cerl_sets:union/2 3 0.00 1 [ 0.33] beam_bs:module/2 1 0.00 1 [ 1.00] sets:new/0 2 0.00 1 [ 0.50] sets:expand_segs/2 2 0.00 1 [ 0.50] lists:usort_1/2 3 0.00 1 [ 0.33] zlib:collect/2 3 0.00 1 [ 0.33] zlib:call/3 3 0.00 1 [ 0.33] v3_codegen:module/2 1 0.00 1 [ 1.00] v3_codegen:set_cg_map/7 3 0.00 1 [ 0.33] v3_codegen:cg_build_args/2 5 0.00 1 [ 0.20] v3_codegen:fetch_var_prefer_y/2 6 0.00 1 [ 0.17] v3_codegen:find_stack/3 6 0.00 1 [ 0.17] v3_codegen:'-set_cg/6-lc$^1/1-1-'/1 6 0.00 1 [ 0.17] v3_codegen:'-set_cg/6-lc$^0/1-0-'/1 6 0.00 1 [ 0.17] v3_kernel:map_split_pairs/5 3 0.00 1 [ 0.33] v3_kernel:map_split_pairs_1/5 6 0.00 1 [ 0.17] v3_kernel:map_remove_dup_keys/2 6 0.00 1 [ 0.17] v3_kernel:map_key_clean/1 3 0.00 1 [ 0.33] v3_kernel:lit_list_vars/1 8 0.00 1 [ 0.13] v3_kernel:'-map_group_pairs/5-lc$^1/1-1-'/1 6 0.00 1 [ 0.17] v3_kernel:'-map_group_pairs/5-lc$^0/1-0-'/1 6 0.00 1 [ 0.17] filename:flatten/1 3 0.00 1 [ 0.33] dict:to_list/1 4 0.00 1 [ 0.25] beam_reorder:module/2 1 0.00 1 [ 1.00] beam_validator:module/2 1 0.00 1 [ 1.00] beam_validator:extract_map_keys/1 3 0.00 1 [ 0.33] beam_validator:validate_bs_get/6 13 0.00 1 [ 0.08] beam_validator:'-verify_put_map/6-anonymous-0-'/2 6 0.00 1 [ 0.17] beam_asm:'-finalize_fun_table/2-lc$^0/1-0-'/2 7 0.00 1 [ 0.14] beam_asm:'-build_file/9-lc$^0/1-0-'/1 7 0.00 1 [ 0.14] beam_type:module/2 1 0.00 1 [ 1.00] beam_dict:highest_opcode/1 1 0.00 1 [ 1.00] beam_dict:local_table/1 1 0.00 1 [ 1.00] beam_dict:import_table/1 1 0.00 1 [ 1.00] beam_dict:literal_table/1 1 0.00 1 [ 1.00] erl_lint:map_fields/4 6 0.00 1 [ 0.17] erl_lint:'-check_option_functions/4-lc$^3/1-3-'/1 3 0.00 1 [ 0.33] erl_lint:'-start/2-lc$^1/1-1-'/1 4 0.00 1 [ 0.25] erl_lint:'-value_option/7-fun-0-'/7 5 0.00 1 [ 0.20] erl_expand_records:record_upd_fs/3 4 0.00 1 [ 0.25] sofs:relation/1 2 0.00 1 [ 0.50] sofs:relative_product1/2 1 0.00 1 [ 1.00] sofs:image/2 1 0.00 1 [ 1.00] sofs:check_for_sort/2 1 0.00 1 [ 1.00] sys_core_dsetel:'-visit/2-anonymous-6-'/2 3 0.00 1 [ 0.33] cerl:is_c_atom/1 6 0.00 1 [ 0.17] erlang:apply/2 1 0.00 1 [ 1.00] erlang:port_control/3 3 0.00 1 [ 0.33] erl_parse:anno_to_term/1 1 0.00 1 [ 1.00] compile:passes_1/1 5 0.00 1 [ 0.20] compile:pass/1 4 0.00 1 [ 0.25] compile:select_list_passes/2 3 0.00 1 [ 0.33] compile:asm_passes/0 1 0.00 1 [ 1.00] compile:debug_info/1 1 0.00 1 [ 1.00] compile:effects_code_generation/1 8 0.00 1 [ 0.13] compile:'-debug_info/1-anonymous-0-'/1 4 0.00 1 [ 0.25] compile:'-transform_module/2-lc$^0/1-0-'/1 7 0.00 1 [ 0.14] compile:'-messages_per_file/1-anonymous-3-'/2 3 0.00 1 [ 0.33] maps:merge/2 3 0.00 1 [ 0.33] unicode:characters_to_binary/2 1 0.00 1 [ 1.00] erlang:demonitor/2 1 0.00 1 [ 1.00] erlang:function_exported/3 1 0.00 1 [ 1.00] beam_clean:clean_labels/1 3 0.00 2 [ 0.67] v3_core:map_build_pairs_1/3 6 0.00 2 [ 0.33] sys_core_fold:body/2 9 0.00 2 [ 0.22] sys_core_fold:pair_list/3 9 0.00 2 [ 0.22] sys_core_fold:'-pair_list/3-lc$^0/1-0-'/3 9 0.00 2 [ 0.22] beam_utils:is_not_used/3 17 0.00 2 [ 0.12] lists:ukeymergel/3 10 0.00 2 [ 0.20] lists:ukeymerge3_1/11 7 0.00 2 [ 0.29] diameter_exprecs:is_head/1 23 0.00 2 [ 0.09] diameter_exprecs:export/1 17 0.00 2 [ 0.12] diameter_exprecs:accessors/2 17 0.00 2 [ 0.12] diameter_exprecs:'info-'/1 17 0.00 2 [ 0.12] diameter_exprecs:'new-'/1 17 0.00 2 [ 0.12] diameter_exprecs:'new--'/1 17 0.00 2 [ 0.12] diameter_exprecs:'get--'/1 17 0.00 2 [ 0.12] diameter_exprecs:'get-'/1 17 0.00 2 [ 0.12] diameter_exprecs:'set-'/1 17 0.00 2 [ 0.12] v3_kernel:'-map_split_pairs/5-lc$^0/1-0-'/1 6 0.00 2 [ 0.33] proplists:get_value/2 5 0.00 2 [ 0.40] erl_lint:pseudolocals/0 6 0.00 2 [ 0.33] erl_lint:record_def/4 18 0.00 2 [ 0.11] erl_lint:check_type/2 18 0.00 2 [ 0.11] erl_lint:handle_comprehension/4 17 0.00 2 [ 0.12] sofs:relprod2/5 17 0.00 2 [ 0.12] sofs:relprod/8 17 0.00 2 [ 0.12] beam_trim:frame_layout_2/1 17 0.00 2 [ 0.12] cerl:ann_c_map/3 15 0.00 2 [ 0.13] compile:'-beam_asm/2-lc$^0/1-0-'/1 5 0.00 2 [ 0.40] compile:'-test_core_inliner/1-anonymous-1-'/1 10 0.00 2 [ 0.20] compile:'-test_core_inliner/1-anonymous-0-'/1 10 0.00 2 [ 0.20] compile:'-test_old_inliner/1-anonymous-0-'/1 10 0.00 2 [ 0.20] erlang:make_ref/0 13 0.00 2 [ 0.15] erlang:binary_to_list/1 13 0.00 2 [ 0.15] erl_internal:'-get_optional_callbacks/1-lc$^0/1-0-'/1 24 0.00 3 [ 0.13] erl_internal:'-predefined_functions/1-lc$^1/1-1-'/1 24 0.00 3 [ 0.13] sys_core_fold:pair/3 9 0.00 3 [ 0.33] lists:flatten/1 9 0.00 3 [ 0.33] lists:keysplit_1_1/10 22 0.00 3 [ 0.14] lists:rkeymergel/4 19 0.00 3 [ 0.16] diameter_exprecs:'-#set-/2/1-fun-1-'/1 17 0.00 3 [ 0.18] diameter_exprecs:'-#get-/2/1-fun-1-'/1 17 0.00 3 [ 0.18] diameter_exprecs:'-#get-/1/1-fun-1-'/1 17 0.00 3 [ 0.18] diameter_exprecs:'-#new-/2/1-fun-1-'/1 17 0.00 3 [ 0.18] diameter_exprecs:'-#new-/1/1-fun-1-'/1 17 0.00 3 [ 0.18] diameter_exprecs:'-#info-/2/1-fun-1-'/1 17 0.00 3 [ 0.18] diameter_exprecs:'-parse_transform/2-lc$^2/1-1-'/1 23 0.00 3 [ 0.13] v3_codegen:select_cons/6 18 0.00 3 [ 0.17] v3_codegen:select_nil/6 17 0.00 3 [ 0.18] v3_codegen:get_bin_size_reg/2 13 0.00 3 [ 0.23] v3_codegen:bif_cg/7 17 0.00 3 [ 0.18] v3_codegen:'-cg_build_args/2-lc$^0/1-0-'/2 15 0.00 3 [ 0.20] v3_kernel:'-lit_list_vars/1-anonymous-0-'/2 16 0.00 3 [ 0.19] dict:expand_segs/2 8 0.00 3 [ 0.38] beam_asm:finalize_fun_table_1/2 7 0.00 3 [ 0.43] beam_asm:chunk/2 5 0.00 3 [ 0.60] beam_asm:filter_essentials/1 8 0.00 3 [ 0.38] beam_type:simplify_select_val_int/2 13 0.00 3 [ 0.23] beam_dict:export_table/1 1 0.00 3 [ 3.00] erl_lint:'-start/2-lc$^0/1-0-'/1 16 0.00 3 [ 0.19] beam_dead:shortcut_bs_start_match_1/4 13 0.00 3 [ 0.23] erlang:monitor/2 1 0.00 3 [ 3.00] v3_core:attribute/1 20 0.00 4 [ 0.20] sys_core_fold:sub_subst_scope_1/3 25 0.00 4 [ 0.16] lists:do_flatten/2 21 0.00 4 [ 0.19] lists:rukeymergel/3 22 0.00 4 [ 0.18] lists:rukeymerge2_2/8 27 0.00 4 [ 0.15] diameter_exprecs:fields/2 17 0.00 4 [ 0.24] diameter_exprecs:fname/1 14 0.00 4 [ 0.29] diameter_exprecs:'#new-X/0'/1 17 0.00 4 [ 0.24] diameter_exprecs:'#new-X/1'/1 17 0.00 4 [ 0.24] diameter_exprecs:'#set-X/2'/2 17 0.00 4 [ 0.24] diameter_exprecs:'#get-X/1'/2 17 0.00 4 [ 0.24] diameter_exprecs:'#get-X/2'/2 17 0.00 4 [ 0.24] diameter_exprecs:'#info-X/1'/2 17 0.00 4 [ 0.24] diameter_exprecs:'-f_accessors/2-fun-0-'/2 17 0.00 4 [ 0.24] diameter_exprecs:'-a_export/1-fun-0-'/1 17 0.00 4 [ 0.24] epp:default_encoding/0 17 0.00 4 [ 0.24] v3_codegen:select_binary/6 14 0.00 4 [ 0.29] v3_codegen:put_reg/2 18 0.00 4 [ 0.22] v3_kernel:include_attribute/1 20 0.00 4 [ 0.20] v3_kernel:iletrec_funs_gen/3 17 0.00 4 [ 0.24] v3_kernel:'-uexpr/3-anonymous-18-'/2 17 0.00 4 [ 0.24] v3_kernel:'-iletrec_funs/2-anonymous-1-'/2 17 0.00 4 [ 0.24] proplists:get_bool/2 18 0.00 4 [ 0.22] proplists:delete/2 15 0.00 4 [ 0.27] beam_validator:verify_no_ct_1/1 17 0.00 4 [ 0.24] beam_validator:is_bif_safe/2 17 0.00 4 [ 0.24] beam_block:alloc_may_pass/1 17 0.00 4 [ 0.24] erl_lint:bool_option/4 15 0.00 4 [ 0.27] erl_lint:attribute_state/2 22 0.00 4 [ 0.18] erl_lint:vt_no_unsafe/1 17 0.00 4 [ 0.24] erl_lint:vt_no_unused/1 17 0.00 4 [ 0.24] erl_expand_records:strict_record_access/2 34 0.00 4 [ 0.12] erl_expand_records:opt_rec_vars/2 34 0.00 4 [ 0.12] sofs:relprod1/5 36 0.00 4 [ 0.11] beam_trim:trim_instructions/1 17 0.00 4 [ 0.24] beam_trim:config_cost_1/2 17 0.00 4 [ 0.24] beam_trim:expand_config/2 17 0.00 4 [ 0.24] beam_trim:create_map/2 17 0.00 4 [ 0.24] beam_trim:try_remap/3 17 0.00 4 [ 0.24] beam_trim:frame_layout/3 17 0.00 4 [ 0.24] beam_trim:'-frame_layout/3-anonymous-0-'/3 17 0.00 4 [ 0.24] beam_trim:'-expand_config/2-lc$^0/1-0-'/1 17 0.00 4 [ 0.24] sys_core_dsetel:visit_def_list/2 17 0.00 4 [ 0.24] sys_core_dsetel:'-visit_def_list/2-anonymous-0-'/2 17 0.00 4 [ 0.24] cerl:update_c_letrec/3 17 0.00 4 [ 0.24] cerl:letrec_defs/1 17 0.00 4 [ 0.24] cerl:letrec_body/1 17 0.00 4 [ 0.24] erl_parse:enc_func/1 17 0.00 4 [ 0.24] erl_parse:'-new_anno/1-fun-0-'/2 16 0.00 4 [ 0.25] erts_internal:port_close/1 1 0.00 4 [ 4.00] v3_core:preprocess_quals/3 17 0.00 5 [ 0.29] v3_core:list_gen_pattern/3 17 0.00 5 [ 0.29] v3_core:'-uexpr/3-anonymous-3-'/1 17 0.00 5 [ 0.29] v3_core:'-uexpr/3-anonymous-2-'/3 17 0.00 5 [ 0.29] v3_life:is_gc_bif/2 17 0.00 5 [ 0.29] v3_life:'-k_bif/4-lc$^0/1-2-'/1 17 0.00 5 [ 0.29] sys_core_fold:v_is_value/2 34 0.00 5 [ 0.15] lists:keymergel/4 32 0.00 5 [ 0.16] lists:ukeymerge3_21_3/9 36 0.00 5 [ 0.14] diameter_exprecs:'-parse_transform/2-lc$^1/1-0-'/1 23 0.00 5 [ 0.22] diameter_exprecs:'-parse_transform/2-fun-0-'/1 23 0.00 5 [ 0.22] v3_codegen:select_extract_bin/12 13 0.00 5 [ 0.38] v3_codegen:build_bs_instr/8 13 0.00 5 [ 0.38] v3_codegen:internal_cg/7 17 0.00 5 [ 0.29] v3_codegen:'-match_cg/5-anonymous-0-'/4 37 0.00 5 [ 0.14] v3_kernel:group_bin_seg/2 26 0.00 5 [ 0.19] v3_kernel:'-uexpr/3-anonymous-19-'/2 17 0.00 5 [ 0.29] v3_kernel:'-uexpr/3-anonymous-15-'/2 17 0.00 5 [ 0.29] v3_kernel:'-uexpr/3-anonymous-14-'/2 17 0.00 5 [ 0.29] v3_kernel:'-uexpr/3-anonymous-3-'/2 17 0.00 5 [ 0.29] beam_validator:bif_type/3 17 0.00 5 [ 0.29] beam_asm:finalize_fun_table_2/3 18 0.00 5 [ 0.28] beam_block:alloc_live_regs/2 17 0.00 5 [ 0.29] beam_type:get_bs_integer_type/1 13 0.00 5 [ 0.38] beam_dict:string/2 13 0.00 5 [ 0.38] beam_dict:'-lambda_table/1-lc$^1/1-1-'/1 18 0.00 5 [ 0.28] beam_dict:'-import_table/1-lc$^0/1-0-'/1 23 0.00 5 [ 0.22] erl_lint:normalise_fields/1 18 0.00 5 [ 0.28] erl_lint:nowarn/0 18 0.00 5 [ 0.28] erl_lint:lc_quals/3 17 0.00 5 [ 0.29] erl_lint:handle_generator/5 17 0.00 5 [ 0.29] erl_lint:shadow_vars/4 17 0.00 5 [ 0.29] erl_expand_records:normalise_fields/1 18 0.00 5 [ 0.28] erl_expand_records:make_list/2 17 0.00 5 [ 0.29] beam_trim:trim_instructions_1/4 34 0.00 5 [ 0.15] beam_trim:save_config/4 17 0.00 5 [ 0.29] sys_core_dsetel:'-visit/2-lc$^0/1-0-'/1 17 0.00 5 [ 0.29] compile:select_cond/5 36 0.00 5 [ 0.14] compile:clean_parse_transforms_1/2 48 0.00 5 [ 0.10] erlang:term_to_binary/1 2 0.00 5 [ 2.50] v3_core:generator/4 17 0.00 6 [ 0.35] v3_core:lc_guard_tests/2 17 0.00 6 [ 0.35] v3_core:new_fun_name/2 17 0.00 6 [ 0.35] v3_core:'-cexpr/3-anonymous-0-'/2 17 0.00 6 [ 0.35] sets:maybe_expand_segs/1 18 0.00 6 [ 0.33] v3_codegen:select_extract_cons/6 18 0.00 6 [ 0.33] v3_kernel:ubody_used_vars/2 17 0.00 6 [ 0.35] v3_kernel:iletrec_funs/2 17 0.00 6 [ 0.35] v3_kernel:'-expr/3-anonymous-2-'/4 17 0.00 6 [ 0.35] beam_asm:bif_type/2 17 0.00 6 [ 0.35] beam_asm:'-flatten_imports/1-anonymous-0-'/1 22 0.00 6 [ 0.27] beam_block:x_live/2 34 0.00 6 [ 0.18] erl_lint:guard_test2/3 34 0.00 6 [ 0.18] erl_lint:def_fields/3 18 0.00 6 [ 0.33] erl_lint:exist_record/3 34 0.00 6 [ 0.18] erl_lint:check_type/3 18 0.00 6 [ 0.33] erl_expand_records:field_names/1 17 0.00 6 [ 0.35] erl_expand_records:record_info_call/3 34 0.00 6 [ 0.18] compile:compile_options/1 48 0.00 6 [ 0.13] erlang:list_to_bitstring/1 14 0.00 6 [ 0.43] lists:rkeymerge2_1/6 56 0.00 7 [ 0.13] lists:rkeymerge2_2/7 53 0.00 7 [ 0.13] eval_bits:match_field/10 56 0.00 7 [ 0.13] v3_codegen:protected_cg/7 37 0.00 7 [ 0.19] v3_codegen:test_cg/7 37 0.00 7 [ 0.19] v3_codegen:maybe_adjust_stack/5 20 0.00 7 [ 0.35] v3_codegen:'-select_bin_segs/5-anonymous-0-'/5 56 0.00 7 [ 0.13] v3_codegen:'-turn_yreg/2-lc$^0/1-0-'/2 37 0.00 7 [ 0.19] v3_kernel:attributes/1 21 0.00 7 [ 0.33] v3_kernel:new_fun_name/2 17 0.00 7 [ 0.41] v3_kernel:bif_returns/3 17 0.00 7 [ 0.41] v3_kernel:'-iletrec_funs_gen/3-anonymous-0-'/3 17 0.00 7 [ 0.41] v3_kernel:'-iletrec_funs/2-anonymous-3-'/3 17 0.00 7 [ 0.41] v3_kernel:'-iletrec_funs/2-lc$^0/1-0-'/1 17 0.00 7 [ 0.41] v3_kernel:'-iletrec_funs/2-anonymous-2-'/3 17 0.00 7 [ 0.41] beam_validator:test_heap/3 56 0.00 7 [ 0.13] beam_validator:branch_arities/3 45 0.00 7 [ 0.16] beam_block:live_regs/2 51 0.00 7 [ 0.14] beam_dict:lambda/3 17 0.00 7 [ 0.41] erl_lint:'-expr/3-fun-3-'/6 51 0.00 7 [ 0.14] erl_expand_records:in_guard/1 34 0.00 7 [ 0.21] compile:select_list_passes_1/3 55 0.00 7 [ 0.13] erts_internal:open_port/2 1 0.00 7 [ 7.00] v3_core:gexpr_test/3 34 0.00 8 [ 0.24] v3_life:'-k_bif/4-lc$^0/1-3-'/1 34 0.00 8 [ 0.24] lists:keymerge3_21_3/9 67 0.00 8 [ 0.12] lists:ukeysplit_1_1/10 37 0.00 8 [ 0.22] eval_bits:match_check_size/4 56 0.00 8 [ 0.14] v3_codegen:select_bin_segs/5 56 0.00 8 [ 0.14] v3_codegen:guard_clause_cg/4 37 0.00 8 [ 0.22] v3_kernel:'-uexpr/3-anonymous-2-'/2 34 0.00 8 [ 0.24] beam_validator:set_type/3 53 0.00 8 [ 0.15] beam_asm:'-encode_arg/2-anonymous-0-'/2 39 0.00 8 [ 0.21] beam_type:eq_ranges/3 56 0.00 8 [ 0.14] beam_dict:old_string/2 13 0.00 8 [ 0.62] beam_dict:'-lambda_table/1-lc$^0/1-0-'/1 38 0.00 8 [ 0.21] erl_lint:vtsubtract/2 34 0.00 8 [ 0.24] beam_trim:'-trim_instructions/1-lc$^0/1-0-'/1 34 0.00 8 [ 0.24] beam_trim:'-trim/3-anonymous-0-'/1 34 0.00 8 [ 0.24] beam_dead:shortcut_selectval/4 35 0.00 8 [ 0.23] erlang:erase/1 36 0.00 8 [ 0.22] v3_life:'-k_bif/4-lc$^0/1-5-'/1 34 0.00 9 [ 0.26] v3_life:'-k_bif/4-lc$^0/1-4-'/1 34 0.00 9 [ 0.26] v3_life:'-protected/3-lc$^0/1-1-'/1 37 0.00 9 [ 0.24] lists:keysplit_1/8 64 0.00 9 [ 0.14] v3_codegen:'-select_binary/6-anonymous-1-'/2 42 0.00 9 [ 0.21] v3_kernel:add_local_function/2 17 0.00 9 [ 0.53] beam_validator:upgrade_tuple_type/2 35 0.00 9 [ 0.26] beam_asm:chunk/3 9 0.00 9 [ 1.00] beam_asm:flag_to_bit/1 39 0.00 9 [ 0.23] beam_block:x_dead/2 51 0.00 9 [ 0.18] beam_dict:local/4 37 0.00 9 [ 0.24] erl_lint:pattern_fields/7 51 0.00 9 [ 0.18] erl_lint:init_fields/6 51 0.00 9 [ 0.18] erl_lint:lc_quals/4 34 0.00 9 [ 0.26] erl_lint:check_old_unused_vars/3 34 0.00 9 [ 0.26] erl_lint:'-gexpr_list/3-fun-0-'/3 34 0.00 9 [ 0.26] core_lib:'-vu_var_list/2-anonymous-0-'/2 34 0.00 9 [ 0.26] beam_trim:remap_block/3 51 0.00 9 [ 0.18] erl_internal:old_type_test/2 34 0.00 10 [ 0.29] v3_core:preprocess_quals/4 34 0.00 10 [ 0.29] v3_core:new_fun_name/1 17 0.00 10 [ 0.59] lists:rkeymerge3_21_3/9 81 0.00 10 [ 0.12] v3_codegen:'-guard_cg_list/6-anonymous-0-'/4 37 0.00 10 [ 0.27] v3_kernel:'-uexpr/3-anonymous-20-'/2 37 0.00 10 [ 0.27] beam_validator:'-verify_no_ct/1-lc$^0/1-0-'/1 51 0.00 10 [ 0.20] erl_lint:guard_test/3 34 0.00 10 [ 0.29] erl_lint:check_record_info_call/4 34 0.00 10 [ 0.29] erl_lint:is_imported_function/2 34 0.00 10 [ 0.29] erl_lint:'-vt_no_unused/1-lc$^0/1-0-'/1 51 0.00 10 [ 0.20] erl_expand_records:guard_tests/2 34 0.00 10 [ 0.29] erl_expand_records:guard_test/2 34 0.00 10 [ 0.29] erl_expand_records:normalise_test/2 34 0.00 10 [ 0.29] core_lib:vu_var_list/2 34 0.00 10 [ 0.29] beam_trim:frame_layout_1/5 51 0.00 10 [ 0.20] binary:match/2 13 0.00 10 [ 0.77] v3_core:guard_tests/1 34 0.00 11 [ 0.32] v3_core:gexpr_top/2 34 0.00 11 [ 0.32] v3_core:force_booleans_1/4 68 0.00 11 [ 0.16] v3_core:lc_tq/5 34 0.00 11 [ 0.32] v3_life:protected/3 37 0.00 11 [ 0.30] v3_life:guard_clause/5 37 0.00 11 [ 0.30] v3_life:'-expr/3-lc$^0/1-4-'/1 37 0.00 11 [ 0.30] sys_core_fold:'-expr/3-anonymous-4-'/3 34 0.00 11 [ 0.32] sets:put_bucket_s/3 36 0.00 11 [ 0.31] lists:keymerge3_12_3/9 85 0.00 11 [ 0.13] lists:keymerge2_1/6 83 0.00 11 [ 0.13] lists:keymerge2_2/7 85 0.00 11 [ 0.13] v3_kernel:integers/2 34 0.00 11 [ 0.32] v3_kernel:'-forest_pre_seq/2-lc$^2/1-2-'/1 37 0.00 11 [ 0.30] beam_validator:'-valfun_1/2-lc$^0/1-0-'/2 51 0.00 11 [ 0.22] erl_lint:gexpr/3 34 0.00 11 [ 0.32] erl_lint:no_guard_bif_clash/2 34 0.00 11 [ 0.32] erl_lint:'-vt_no_unsafe/1-lc$^0/1-0-'/1 51 0.00 11 [ 0.22] cerl_trees:mapfold_pairs/4 34 0.00 11 [ 0.32] erl_expand_records:lc_tq/3 34 0.00 11 [ 0.32] erl_expand_records:pattern_fields/2 51 0.00 11 [ 0.22] erl_expand_records:opt_rec_vars_2/2 34 0.00 11 [ 0.32] erl_expand_records:'-guard_test/2-fun-0-'/2 34 0.00 11 [ 0.32] beam_trim:frame_size/2 68 0.00 11 [ 0.16] maps:is_key/2 37 0.00 11 [ 0.30] erlang:iolist_to_binary/1 9 0.00 11 [ 1.22] beam_utils:bif_to_test/3 34 0.00 12 [ 0.35] beam_jump:extract_seq_1/2 69 0.00 12 [ 0.17] v3_codegen:turn_yregs/3 91 0.00 12 [ 0.13] v3_codegen:guard_cg_list/6 37 0.00 12 [ 0.32] v3_codegen:'-save_stack/4-anonymous-2-'/2 85 0.00 12 [ 0.14] v3_kernel:'-new_clauses/3-anonymous-2-'/3 51 0.00 12 [ 0.24] v3_kernel:'-expr/3-anonymous-1-'/2 17 0.00 12 [ 0.71] v3_kernel:'-extract_vars/1-anonymous-1-'/2 37 0.00 12 [ 0.32] v3_kernel:'-extract_all_vars/3-lc$^1/1-1-'/2 37 0.00 12 [ 0.32] v3_kernel:'-forest_pre_seq/2-lc$^0/1-0-'/1 37 0.00 12 [ 0.32] beam_validator:call/3 85 0.00 12 [ 0.14] beam_validator:allocate/5 69 0.00 12 [ 0.17] erl_lint:gexpr_list/3 34 0.00 12 [ 0.35] beam_trim:'-remap_block/3-lc$^0/1-0-'/2 51 0.00 12 [ 0.24] cerl:update_c_values/2 47 0.00 12 [ 0.26] cerl:update_c_alias/3 51 0.00 12 [ 0.24] lists:ukeymerge2_2/6 100 0.00 13 [ 0.13] eval_bits:add_bin_binding/4 56 0.00 13 [ 0.23] v3_kernel:wrap_guard/2 37 0.00 13 [ 0.35] v3_kernel:flatten_alias/1 51 0.00 13 [ 0.25] v3_kernel:'-new_clauses/3-anonymous-1-'/3 51 0.00 13 [ 0.25] v3_kernel:'-match_vars/2-anonymous-0-'/2 91 0.00 13 [ 0.14] erl_lint:obsolete_guard/2 34 0.00 13 [ 0.38] erl_expand_records:guard_test1/2 34 0.00 13 [ 0.38] erl_expand_records:opt_rec_vars_1/2 68 0.00 13 [ 0.19] erts_internal:port_command/3 1 0.00 13 [ 13.00] v3_core:pat_alias/2 51 0.00 14 [ 0.27] v3_life:test_op/1 37 0.00 14 [ 0.38] v3_life:k_bif/4 34 0.00 14 [ 0.41] lists:keysplit_2_1/10 101 0.00 14 [ 0.14] eval_bits:coerce_to_float/2 56 0.00 14 [ 0.25] eval_bits:match_check_size/3 56 0.00 14 [ 0.25] v3_codegen:select_bin_seg/5 56 0.00 14 [ 0.25] v3_kernel:guard/3 37 0.00 14 [ 0.38] dict:is_key/2 52 0.00 14 [ 0.27] dict:find_key/2 116 0.00 14 [ 0.12] beam_dict:'-line_table/1-lc$^1/1-1-'/1 67 0.00 14 [ 0.21] erl_lint:reject_invalid_alias/4 51 0.00 14 [ 0.27] erl_lint:init_fields/3 51 0.00 14 [ 0.27] erl_expand_records:record_inits/2 51 0.00 14 [ 0.27] beam_trim:remap/3 85 0.00 14 [ 0.16] sys_core_dsetel:rewrite/3 88 0.00 14 [ 0.16] maps:keys/1 40 0.00 14 [ 0.35] eval_bits:match_bits/6 56 0.00 15 [ 0.27] v3_codegen:select_extract_int/11 43 0.00 15 [ 0.35] v3_kernel:'-match_fun/1-anonymous-0-'/3 56 0.00 15 [ 0.27] beam_validator:set_type_y/3 85 0.00 15 [ 0.18] beam_validator:return_type_1/4 103 0.00 15 [ 0.15] beam_validator:return_type_erl/2 103 0.00 15 [ 0.15] beam_asm:encode_line_items/2 67 0.00 15 [ 0.22] erl_lint:'-bool_option/4-fun-0-'/4 75 0.00 15 [ 0.20] sys_core_dsetel:'-visit/2-anonymous-3-'/2 71 0.00 15 [ 0.21] lists:rkeymerge3_12_3/9 125 0.00 16 [ 0.13] lists:ukeysplit_1/8 80 0.00 16 [ 0.20] lists:rukeymerge3_1/11 91 0.00 16 [ 0.18] lists:rukeymerge2_1/6 109 0.00 16 [ 0.15] v3_kernel:translate_match_fail_1/4 68 0.00 16 [ 0.24] v3_kernel:store_free/4 17 0.00 16 [ 0.94] v3_kernel:'-expr/3-lc$^0/1-0-'/1 51 0.00 16 [ 0.31] beam_validator:bsm_get_context/2 59 0.00 16 [ 0.27] core_lib:'-vu_expr/2-anonymous-0-'/2 68 0.00 16 [ 0.24] beam_trim:'-remap_block/3-lc$^1/1-1-'/2 68 0.00 16 [ 0.24] beam_trim:'-create_map/2-anonymous-2-'/2 68 0.00 16 [ 0.24] cerl:primop_name/1 71 0.00 16 [ 0.23] cerl:primop_args/1 71 0.00 16 [ 0.23] eval_bits:get_value/6 56 0.00 17 [ 0.30] v3_kernel:guard_opt/2 37 0.00 17 [ 0.46] v3_kernel:select_assert_match_possible/3 56 0.00 17 [ 0.30] v3_kernel:'-extract_var_list/1-lc$^0/1-0-'/1 37 0.00 17 [ 0.46] proplists:get_value/3 82 0.00 17 [ 0.21] erl_lint:pre_scan/2 157 0.00 17 [ 0.11] erl_lint:'-check_dialyzer_attribute/2-lc$^2/1-0-'/1 157 0.00 17 [ 0.11] erl_lint:'-check_unused_types/2-lc$^0/1-0-'/1 157 0.00 17 [ 0.11] erl_lint:'-check_unused_records/2-lc$^0/1-0-'/1 157 0.00 17 [ 0.11] erl_lint:'-disallowed_compile_flags/2-lc$^2/1-2-'/2 157 0.00 17 [ 0.11] erl_lint:'-disallowed_compile_flags/2-lc$^0/1-0-'/2 157 0.00 17 [ 0.11] erl_lint:'-not_deprecated/2-lc$^0/1-0-'/1 157 0.00 17 [ 0.11] erl_lint:'-includes_qlc_hrl/2-lc$^0/1-0-'/1 157 0.00 17 [ 0.11] cerl:update_c_cons_skel/3 68 0.00 17 [ 0.25] cerl:update_c_primop/3 71 0.00 17 [ 0.24] v3_core:unforce/3 68 0.00 18 [ 0.26] sys_core_fold:sub_new/1 65 0.00 18 [ 0.28] sys_core_fold:eval_case_warn/1 65 0.00 18 [ 0.28] lists:unzip/1 65 0.00 18 [ 0.28] eval_bits:match_field_1/6 56 0.00 18 [ 0.32] v3_codegen:call_cg/7 68 0.00 18 [ 0.26] v3_kernel:match_fun/1 56 0.00 18 [ 0.32] beam_validator:eat_heap/2 66 0.00 18 [ 0.27] beam_block:gen_init/4 102 0.00 18 [ 0.18] erl_lint:'-bif_clashes/2-lc$^0/1-0-'/1 157 0.00 18 [ 0.11] erl_expand_records:init_calltype_imports/2 157 0.00 18 [ 0.11] compile:'-core/2-lc$^0/1-0-'/2 157 0.00 18 [ 0.11] v3_life:'-match/5-lc$^1/1-0-'/5 74 0.00 19 [ 0.26] lists:unzip/3 120 0.00 19 [ 0.16] beam_validator:heap_alloc/2 125 0.00 19 [ 0.15] erl_lint:'-compiler_options/1-lc$^0/1-0-'/1 157 0.00 19 [ 0.12] erl_expand_records:guard_tests1/2 68 0.00 19 [ 0.28] beam_except:dig_out_block_fc/1 68 0.00 19 [ 0.28] erl_internal:'-predefined_functions/1-lc$^0/1-0-'/1 157 0.00 20 [ 0.13] v3_codegen:put_stack/2 119 0.00 20 [ 0.17] v3_kernel:rewrite_bool/4 37 0.00 20 [ 0.54] v3_kernel:extract_vars/1 37 0.00 20 [ 0.54] erl_lint:clauses/2 132 0.00 20 [ 0.15] erl_lint:pattern_bin/5 131 0.00 20 [ 0.15] erl_lint:'-vtnew/2-fun-0-'/3 85 0.00 20 [ 0.24] sofs:restrict/5 130 0.00 20 [ 0.15] sofs:diff_restrict_n/6 134 0.00 20 [ 0.15] cerl:let_vars/1 88 0.00 20 [ 0.23] compile:select_passes/2 80 0.00 20 [ 0.25] beam_clean:find_all_used/3 169 0.00 21 [ 0.12] beam_bs:function/2 168 0.00 21 [ 0.13] v3_codegen:free_dead/1 68 0.00 21 [ 0.31] v3_kernel:'-uexpr/3-anonymous-12-'/2 85 0.00 21 [ 0.25] beam_block:'-init_yreg/2-anonymous-0-'/2 85 0.00 21 [ 0.25] erl_lint:function_state/2 133 0.00 21 [ 0.16] erl_lint:function/5 132 0.00 21 [ 0.16] sys_core_dsetel:'-visit/2-anonymous-8-'/2 91 0.00 21 [ 0.23] cerl:update_c_let/4 88 0.00 21 [ 0.24] cerl:let_body/1 88 0.00 21 [ 0.24] beam_clean:bs_fix/2 169 0.00 22 [ 0.13] v3_kernel:forest_pre_seq/2 37 0.00 22 [ 0.59] v3_kernel:extract_all_vars/3 74 0.00 22 [ 0.30] erl_lint:'-check_deprecated/2-lc$^0/1-0-'/3 157 0.00 22 [ 0.14] compile:fold_comp/4 38 0.00 22 [ 0.58] erl_internal:type_test/2 68 0.00 23 [ 0.34] v3_core:force_booleans/4 68 0.00 23 [ 0.34] sys_core_fold:make_var_name/0 55 0.00 23 [ 0.42] v3_codegen:select_bin_end/5 131 0.00 23 [ 0.18] v3_codegen:build_call/3 68 0.00 23 [ 0.34] v3_codegen:need_stack_frame/1 69 0.00 23 [ 0.33] v3_codegen:'-load_vars/2-anonymous-0-'/2 85 0.00 23 [ 0.27] beam_bsm:btb_index_find_start_match/1 168 0.00 23 [ 0.14] erl_lint:format_function/5 171 0.00 23 [ 0.13] lists:keymerge3_2/10 184 0.00 24 [ 0.13] erl_lint:expr_bin/4 153 0.00 24 [ 0.16] cerl:make_list/1 68 0.00 24 [ 0.35] v3_life:'-expr/3-lc$^0/1-9-'/1 102 0.00 25 [ 0.25] eval_bits:match_bits_1/6 112 0.00 25 [ 0.22] eval_bits:get_integer/4 56 0.00 25 [ 0.45] beam_bsm:btb_index_1/2 169 0.00 25 [ 0.15] lists:rmerge3_21_3/6 158 0.00 26 [ 0.16] v3_kernel:'-group_bin_seg/2-anonymous-0-'/2 117 0.00 26 [ 0.22] v3_kernel:'-extract_all_vars/3-lc$^0/1-0-'/2 74 0.00 26 [ 0.35] beam_validator:index_bs_start_match/2 169 0.00 26 [ 0.15] erl_expand_records:record_wildcard_init/1 102 0.00 26 [ 0.25] v3_core:cfun/3 151 0.00 27 [ 0.18] sys_core_fold:returns_integer/2 102 0.00 27 [ 0.26] sets:maybe_expand/1 185 0.00 27 [ 0.15] lists:rkeymerge3_1/10 190 0.00 27 [ 0.14] erl_lint:vtold/2 102 0.00 27 [ 0.26] compile:'-internal_comp/5-anonymous-1-'/3 31 0.00 27 [ 0.87] beam_dead:function/2 168 0.00 27 [ 0.16] erlang:put/2 92 0.00 27 [ 0.29] lists:keysplit_2/8 218 0.00 28 [ 0.13] v3_kernel:make_forest_1/4 74 0.00 28 [ 0.38] v3_kernel:extract_var_list/1 74 0.00 28 [ 0.38] v3_kernel:'-select_bin_con_2/1-anonymous-0-'/2 117 0.00 28 [ 0.24] sofs:restrict/4 169 0.00 28 [ 0.17] v3_core:'-module/2-lc$^1/1-1-'/1 132 0.00 29 [ 0.22] sys_core_fold:opt_bool_case_in_let_1/5 210 0.00 29 [ 0.14] sets:'-from_list/1-fun-0-'/2 131 0.00 29 [ 0.22] lists:keymerge3_1/10 195 0.00 29 [ 0.15] v3_codegen:guard_cg/5 74 0.00 29 [ 0.39] beam_block:count_ones/2 137 0.00 29 [ 0.21] erl_lint:form/2 156 0.00 29 [ 0.19] erl_expand_records:'-compiler_options/1-lc$^0/1-0-'/1 157 0.00 29 [ 0.18] sys_core_dsetel:visit_module_1/3 135 0.00 29 [ 0.21] cerl:binary_segments/1 131 0.00 29 [ 0.22] compile:'-select_passes/2-anonymous-2-'/3 23 0.00 29 [ 1.26] v3_kernel:pattern_bin/4 131 0.00 30 [ 0.23] beam_block:init_yreg/2 138 0.00 30 [ 0.22] v3_core:form/3 159 0.00 31 [ 0.19] v3_life:'-guard_clause/5-lc$^0/1-0-'/3 116 0.00 31 [ 0.27] sys_core_fold:opt_bool_case_in_let/2 210 0.00 31 [ 0.15] v3_codegen:free_dead/4 136 0.00 31 [ 0.23] dict:maybe_expand_segs/1 101 0.00 31 [ 0.31] beam_asm:assemble_1/4 169 0.00 31 [ 0.18] erl_lint:'-local_functions/1-lc$^0/1-0-'/1 157 0.00 31 [ 0.20] erl_lint:'-export/3-fun-0-'/3 129 0.00 31 [ 0.24] cerl:update_c_binary/2 131 0.00 31 [ 0.24] v3_core:pat_bin/2 131 0.00 32 [ 0.24] v3_life:'-expr/3-lc$^0/1-10-'/1 136 0.00 32 [ 0.24] beam_jump:extract_seq/2 138 0.00 32 [ 0.23] v3_codegen:set_cg/6 144 0.00 32 [ 0.22] beam_validator:validate_0/3 169 0.00 32 [ 0.19] beam_validator:heap_alloc_1/2 125 0.00 32 [ 0.26] erl_expand_records:pattern_bin/2 131 0.00 32 [ 0.24] erl_expand_records:'-init_calltype/1-lc$^0/1-0-'/1 157 0.00 32 [ 0.20] v3_life:functions/2 169 0.00 33 [ 0.20] sys_core_fold:make_vars/3 120 0.00 33 [ 0.28] v3_core:gexpr/3 68 0.00 34 [ 0.50] v3_core:constant_bin_1/1 153 0.00 34 [ 0.22] v3_core:bin_expand_strings/1 131 0.00 34 [ 0.26] v3_core:upat_bin/3 131 0.00 34 [ 0.26] v3_life:'-protected/3-lc$^0/1-0-'/3 116 0.00 34 [ 0.29] lists:rkeymerge3_2/10 260 0.00 34 [ 0.13] v3_codegen:function/3 168 0.00 34 [ 0.20] v3_kernel:translate_match_fail/4 71 0.00 34 [ 0.48] beam_dict:export/4 131 0.00 34 [ 0.26] erl_lint:eval_file_attr/2 157 0.00 34 [ 0.22] erl_lint:define_function/4 132 0.00 34 [ 0.26] sets:rehash/4 153 0.00 35 [ 0.23] v3_codegen:save_carefully/3 248 0.00 35 [ 0.14] v3_kernel:function/2 134 0.00 35 [ 0.26] erl_lint:'-forms/2-fun-0-'/2 156 0.00 35 [ 0.22] cerl:c_let/3 130 0.00 35 [ 0.27] beam_clean:'-module/2-lc$^0/1-0-'/1 169 0.00 36 [ 0.21] beam_bs:bsm_subst_labels_1/4 286 0.00 36 [ 0.13] v3_codegen:extend_stack/4 107 0.00 36 [ 0.34] beam_z:'-module/2-lc$^0/1-0-'/1 169 0.00 36 [ 0.21] cerl:fun_vars/1 151 0.00 36 [ 0.24] cerl:fun_body/1 151 0.00 36 [ 0.24] beam_clean:remove_unused/3 169 0.00 37 [ 0.22] sys_core_fold:'-body/3-lc$^0/1-0-'/3 133 0.00 37 [ 0.28] beam_flatten:'-module/2-lc$^0/1-0-'/1 169 0.00 37 [ 0.22] v3_codegen:cg_basic_block/7 107 0.00 37 [ 0.35] v3_codegen:x0_vars/4 107 0.00 37 [ 0.35] v3_kernel:'-module/2-anonymous-0-'/1 131 0.00 37 [ 0.28] beam_receive:'-module/2-lc$^0/1-0-'/1 169 0.00 37 [ 0.22] beam_validator:validate_2/5 168 0.00 37 [ 0.22] erl_lint:function_check_max_args/3 132 0.00 37 [ 0.28] cerl:update_c_fun/3 151 0.00 37 [ 0.25] cerl:alias_var/1 159 0.00 37 [ 0.23] cerl:alias_pat/1 159 0.00 37 [ 0.23] v3_core:expr_bin/3 153 0.00 38 [ 0.25] beam_flatten:block/1 168 0.00 38 [ 0.23] beam_flatten:norm_allocate/2 160 0.00 38 [ 0.24] v3_codegen:max_reg/1 123 0.00 38 [ 0.31] v3_kernel:'-module/2-anonymous-1-'/2 134 0.00 38 [ 0.28] beam_reorder:'-module/2-lc$^0/1-0-'/1 169 0.00 38 [ 0.22] erl_expand_records:expr_bin/2 153 0.00 38 [ 0.25] sofs:to_external/1 172 0.00 38 [ 0.22] beam_trim:'-module/2-lc$^0/1-0-'/1 169 0.00 38 [ 0.22] erlang:min/2 128 0.00 38 [ 0.30] beam_clean:'-rootset/3-lc$^0/1-0-'/1 169 0.00 39 [ 0.23] v3_core:new_vars/3 134 0.00 39 [ 0.29] sys_core_fold:'-module/2-lc$^0/1-0-'/1 135 0.00 39 [ 0.29] beam_peep:'-module/2-lc$^0/1-0-'/1 169 0.00 39 [ 0.23] v3_codegen:reserve_x0/2 141 0.00 39 [ 0.28] beam_bsm:btb_index_2/4 168 0.00 39 [ 0.23] beam_bsm:'-module/2-lc$^0/1-0-'/2 169 0.00 39 [ 0.23] erl_lint:set_form_file/2 156 0.00 39 [ 0.25] erl_lint:check_get_stacktrace/5 171 0.00 39 [ 0.23] beam_split:'-module/2-lc$^0/1-0-'/1 169 0.00 39 [ 0.23] beam_except:function/1 168 0.00 39 [ 0.23] beam_except:dig_out_fc/3 170 0.00 39 [ 0.23] beam_except:'-module/2-lc$^0/1-0-'/1 169 0.00 39 [ 0.23] beam_clean:bs_clean_saves/1 168 0.00 40 [ 0.24] v3_life:vdb_store_new/3 171 0.00 40 [ 0.23] beam_record:'-module/2-lc$^0/1-0-'/1 169 0.00 40 [ 0.24] beam_bs:'-module/2-anonymous-0-'/2 168 0.00 40 [ 0.24] v3_kernel:gexpr_test/2 74 0.00 40 [ 0.54] beam_validator:validate_3/7 168 0.00 40 [ 0.24] beam_validator:verify_local_call/3 257 0.00 40 [ 0.16] cerl_trees:mapfold/3 134 0.00 40 [ 0.30] sofs:rel2family/1 169 0.00 40 [ 0.24] gb_sets:balance/2 279 0.00 40 [ 0.14] beam_a:'-module/2-lc$^0/1-0-'/1 169 0.00 40 [ 0.24] sys_core_dsetel:'-visit/2-anonymous-7-'/2 180 0.00 40 [ 0.22] erl_parse:not_string/4 385 0.00 40 [ 0.10] beam_clean:make_save_point_dict/2 168 0.00 41 [ 0.24] beam_clean:'-module/2-anonymous-1-'/2 168 0.00 41 [ 0.24] v3_core:constant_bin/1 153 0.00 41 [ 0.27] beam_record:function/1 168 0.00 41 [ 0.24] lists:umergel/3 246 0.00 41 [ 0.17] beam_jump:find_fixpoint/2 276 0.00 41 [ 0.15] beam_validator:'-validate_3/7-lc$^0/1-0-'/2 168 0.00 41 [ 0.24] erl_lint:is_latin1_name/1 172 0.00 41 [ 0.24] erl_lint:check_qlc_hrl/5 171 0.00 41 [ 0.24] erl_lint:is_format_function/2 171 0.00 41 [ 0.24] erl_lint:'-vtold/2-fun-0-'/3 170 0.00 41 [ 0.24] sofs:relation_to_family/1 169 0.00 41 [ 0.24] sofs:rel/3 172 0.00 41 [ 0.24] beam_split:split_blocks/1 168 0.00 41 [ 0.24] beam_clean:bs_function/1 168 0.00 42 [ 0.25] beam_jump:'-module/2-lc$^0/1-0-'/1 169 0.00 42 [ 0.25] sofs:relation/2 171 0.00 42 [ 0.25] erl_anno:is_filename/1 385 0.00 42 [ 0.11] v3_core:'-module/2-anonymous-2-'/3 159 0.00 43 [ 0.27] beam_bs:bsm_opt/2 168 0.00 43 [ 0.26] beam_utils:empty_label_index/0 168 0.00 43 [ 0.26] beam_peep:function/1 168 0.00 43 [ 0.26] v3_codegen:fetch_stack/3 238 0.00 43 [ 0.18] beam_bsm:function/2 168 0.00 43 [ 0.26] beam_asm:'-flatten_exports/1-anonymous-0-'/1 168 0.00 43 [ 0.26] beam_type:'-module/2-lc$^0/1-0-'/1 169 0.00 43 [ 0.25] erl_lint:check_module_name/3 172 0.00 43 [ 0.25] erl_expand_records:'-make_list/2-fun-0-'/3 385 0.00 43 [ 0.11] beam_jump:function/1 168 0.00 44 [ 0.26] beam_flatten:function/1 168 0.00 44 [ 0.26] beam_z:function/1 168 0.00 44 [ 0.26] beam_validator:prune_x_regs/2 154 0.00 44 [ 0.29] gb_trees:keys/1 168 0.00 44 [ 0.26] beam_dead:'-module/2-anonymous-0-'/2 168 0.00 44 [ 0.26] beam_jump:share/1 168 0.00 45 [ 0.27] v3_kernel:'-uexpr/3-anonymous-23-'/2 178 0.00 45 [ 0.25] beam_receive:function/1 168 0.00 45 [ 0.27] beam_validator:index_bs_start_match_1/3 168 0.00 45 [ 0.27] beam_validator:init_state/1 168 0.00 45 [ 0.27] beam_block:opt_alloc/4 176 0.00 45 [ 0.26] erl_expand_records:forms/2 157 0.00 45 [ 0.29] sofs:diff_restrict_n/5 276 0.00 45 [ 0.16] beam_trim:function/1 168 0.00 45 [ 0.27] erl_parse:abstract_list/4 385 0.00 45 [ 0.12] v3_core:'-uexpr/3-anonymous-0-'/2 188 0.00 46 [ 0.24] diameter_exprecs:'info-X'/2 385 0.00 46 [ 0.12] v3_codegen:'-functions/2-anonymous-0-'/3 168 0.00 46 [ 0.27] v3_kernel:'-select_assert_match_possible/3-anonymous-0-'/2 168 0.00 46 [ 0.27] beam_validator:validate_1/5 168 0.00 46 [ 0.27] beam_block:function/1 168 0.00 46 [ 0.27] beam_block:'-module/2-lc$^0/1-0-'/1 169 0.00 46 [ 0.27] erl_lint:check_remote_function/5 171 0.00 46 [ 0.27] v3_core:ufun_clauses/3 151 0.00 47 [ 0.31] lists:rumergel/3 318 0.00 47 [ 0.15] diameter_exprecs:'set-X'/2 385 0.00 47 [ 0.12] diameter_exprecs:'get-X'/2 385 0.00 47 [ 0.12] beam_a:function/1 168 0.00 47 [ 0.28] lists:seq_loop/3 307 0.00 48 [ 0.16] beam_validator:validate_fun_info_branches/3 336 0.00 48 [ 0.14] beam_type:function/1 168 0.00 48 [ 0.29] beam_type:'-simplify_select_val_int/2-lc$^0/1-0-'/1 273 0.00 48 [ 0.18] gb_sets:to_list/1 174 0.00 48 [ 0.28] core_lib:is_var_used/2 210 0.00 48 [ 0.23] v3_core:cbody/2 134 0.00 49 [ 0.37] beam_reorder:function/1 168 0.00 49 [ 0.29] beam_validator:return_type/2 171 0.00 49 [ 0.29] erl_lint:'-expr/3-fun-2-'/4 385 0.00 49 [ 0.13] erl_lint:'-record_def/4-lc$^0/1-0-'/1 412 0.00 50 [ 0.12] erl_lint:'-expr/3-fun-4-'/4 385 0.00 50 [ 0.13] beam_dead:shortcut_label/2 198 0.00 50 [ 0.25] beam_except:function_1/1 168 0.00 50 [ 0.30] v3_core:body/4 134 0.00 51 [ 0.38] cerl:values_es/1 199 0.00 51 [ 0.26] v3_core:new_vars/2 151 0.00 52 [ 0.34] sets:update_bucket/3 185 0.00 52 [ 0.28] v3_codegen:cg_fun/7 168 0.00 52 [ 0.31] v3_codegen:'-x0_vars/4-lc$^0/1-0-'/3 289 0.00 52 [ 0.18] cerl:ann_make_list/3 170 0.00 52 [ 0.31] sys_core_bsm:function/3 135 0.00 53 [ 0.39] v3_kernel:ensure_return_vars/2 178 0.00 53 [ 0.30] orddict:is_key/2 340 0.00 53 [ 0.16] erl_expand_records:get_record_field/5 385 0.00 53 [ 0.14] v3_core:function/4 134 0.00 54 [ 0.40] eval_bits:expr_grp/3 209 0.00 54 [ 0.26] v3_codegen:cg_reg_args/2 187 0.00 54 [ 0.29] v3_kernel:c_apply/5 263 0.00 54 [ 0.21] erl_lint:'-expr/3-fun-5-'/5 385 0.00 54 [ 0.14] erl_lint:'-check_option_functions/4-lc$^0/1-0-'/2 471 0.00 54 [ 0.11] erl_expand_records:strict_get_record_field/5 385 0.00 54 [ 0.14] sys_core_fold:update_types_from_expr/3 210 0.00 55 [ 0.26] sys_core_fold:'-sub_add_scope/2-anonymous-0-'/2 213 0.00 55 [ 0.26] lists:seq/2 185 0.00 55 [ 0.30] cerl:apply_args/1 246 0.00 55 [ 0.22] sys_core_fold:'-expr/3-lc$^0/1-0-'/3 200 0.00 56 [ 0.28] cerl:let_arg/1 226 0.00 56 [ 0.25] cerl:apply_op/1 246 0.00 56 [ 0.23] erl_eval:new_bindings/0 209 0.00 57 [ 0.27] sys_core_fold:delay_build_1/2 385 0.00 57 [ 0.15] beam_block:opt_move_rev/3 470 0.00 57 [ 0.12] erl_lint:update_fields/5 385 0.00 57 [ 0.15] otp_internal:obsolete/3 239 0.00 58 [ 0.24] otp_internal:obsolete_1/3 239 0.00 58 [ 0.24] sys_core_fold:delay_build_expr/2 385 0.00 58 [ 0.15] lists:merge3_12_3/6 394 0.00 58 [ 0.15] v3_codegen:'-max_reg/1-anonymous-0-'/2 252 0.00 58 [ 0.23] v3_codegen:'-save_carefully/4-lc$^1/1-1-'/1 248 0.00 58 [ 0.23] beam_validator:verify_live/2 223 0.00 58 [ 0.26] erl_expand_records:'-pattern_bin/2-fun-0-'/2 524 0.00 58 [ 0.11] sys_core_fold:function_1/1 134 0.00 59 [ 0.44] sys_core_fold:opt_not_in_let_1/3 196 0.00 59 [ 0.30] beam_validator:kill_heap_allocation/1 293 0.00 59 [ 0.20] beam_type:live_regs_1/2 315 0.00 59 [ 0.19] sys_core_fold:delay_build_expr_1/2 385 0.00 60 [ 0.16] erl_expand_records:'-record_setel/4-fun-2-'/2 383 0.00 60 [ 0.16] cerl:update_c_apply/3 246 0.00 60 [ 0.24] v3_life:function/1 168 0.00 61 [ 0.36] sofs:is_types/2 513 0.00 61 [ 0.12] erlang:atom_to_binary/2 320 0.00 61 [ 0.19] sys_core_fold:extract_type/2 210 0.00 62 [ 0.30] erl_lint:call_function/4 212 0.00 62 [ 0.29] gb_sets:add_element/2 261 0.00 62 [ 0.24] beam_clean:add_to_work_list/2 274 0.00 63 [ 0.23] sys_core_fold:find_fixpoint/3 251 0.00 63 [ 0.25] v3_codegen:'-cg_fun/7-anonymous-0-'/2 239 0.00 65 [ 0.27] dict:put_bucket_s/3 202 0.00 65 [ 0.32] erl_eval:ret_expr/2 636 0.00 66 [ 0.10] sys_core_fold:is_any_var_used/2 210 0.00 66 [ 0.31] beam_validator:init_regs/2 237 0.00 66 [ 0.28] gb_sets:to_list_1/1 279 0.00 66 [ 0.24] v3_life:'-expr/3-lc$^0/1-12-'/1 288 0.00 67 [ 0.23] lists:split_2_1/6 318 0.00 67 [ 0.21] beam_clean:function_replace/3 507 0.00 68 [ 0.13] erl_lint:deprecated_function/5 239 0.00 68 [ 0.28] erl_expand_records:'-expr_bin/2-fun-0-'/2 612 0.00 68 [ 0.11] beam_dict:'-atom_table/2-lc$^0/1-0-'/2 321 0.00 69 [ 0.21] lists:merge3_21_3/6 462 0.00 70 [ 0.15] v3_codegen:cg_basic_block/4 355 0.00 71 [ 0.20] beam_dict:'-literal_table/1-anonymous-1-'/3 329 0.00 71 [ 0.22] erl_lint:pattern_element_1/4 524 0.00 71 [ 0.14] erl_expand_records:pattern_element/2 524 0.00 71 [ 0.14] beam_clean:function_renumber/3 507 0.00 72 [ 0.14] v3_codegen:save_carefully/4 248 0.00 72 [ 0.29] v3_kernel:'-ubody/3-anonymous-0-'/2 314 0.00 72 [ 0.23] gb_sets:from_ordset/1 508 0.00 73 [ 0.14] lists:rumerge3_21_3/7 529 0.00 75 [ 0.14] diameter_exprecs:fname/2 323 0.00 75 [ 0.23] beam_bsm:drop_to_label/2 504 0.00 75 [ 0.15] erl_internal:bool_op/2 315 0.00 76 [ 0.24] diameter_exprecs:fname_prefix/1 337 0.00 77 [ 0.23] beam_validator:verify_live_1/2 587 0.00 77 [ 0.13] erl_expand_records:bin_element/2 612 0.00 77 [ 0.13] v3_codegen:make_reservation/2 282 0.00 78 [ 0.28] v3_codegen:'-x0_vars/4-lc$^0/1-1-'/3 635 0.00 78 [ 0.12] beam_validator:'-init_regs/2-lc$^0/1-0-'/2 307 0.00 78 [ 0.25] beam_dict:'-literal_table/1-lc$^1/1-0-'/1 330 0.00 78 [ 0.24] dict:fetch/2 336 0.00 79 [ 0.24] erl_lint:pattern_element/4 524 0.00 79 [ 0.15] beam_validator:validate_fun_info_branches_1/3 407 0.00 80 [ 0.20] sofs:rel/4 669 0.00 80 [ 0.12] erl_expand_records:'-field_names/1-fun-0-'/1 385 0.00 83 [ 0.22] beam_except:translate_exception/4 773 0.00 83 [ 0.11] erl_lint:is_autoimport_suppressed/2 314 0.00 84 [ 0.27] sofs:atoms_only/2 512 0.00 84 [ 0.16] core_lib:'-vu_expr/2-anonymous-4-'/2 389 0.00 84 [ 0.22] beam_validator:merge_types/2 280 0.00 85 [ 0.30] erl_lint:is_warn_enabled/2 305 0.00 85 [ 0.28] sys_core_fold:sub_set_name/3 495 0.00 86 [ 0.17] diameter_exprecs:'-#info-X/1/2-fun-0-'/2 385 0.00 86 [ 0.22] diameter_exprecs:'-#set-X/2/2-fun-0-'/2 385 0.00 86 [ 0.22] beam_asm:encode1/2 702 0.00 86 [ 0.12] erl_lint:bin_element/4 612 0.00 86 [ 0.14] diameter_exprecs:'-#get-X/2/2-fun-0-'/2 385 0.00 87 [ 0.23] v3_codegen:'-cg_basic_block/7-anonymous-0-'/4 355 0.00 87 [ 0.25] erl_lint:'-update_fields/5-fun-0-'/3 385 0.00 91 [ 0.24] erl_expand_records:is_simple_val/1 385 0.00 91 [ 0.24] beam_validator:tail_call/3 639 0.00 92 [ 0.14] erl_lint:'-normalise_fields/1-fun-0-'/1 394 0.00 92 [ 0.23] lists:keymember/3 596 0.00 92 [ 0.15] v3_core:new_vars_1/4 661 0.00 93 [ 0.14] lists:rmerge3_12_3/6 677 0.00 93 [ 0.14] erl_lint:check_fields/6 487 0.00 93 [ 0.19] diameter_exprecs:'-fields/2-fun-0-'/1 385 0.00 94 [ 0.24] erl_expand_records:'-normalise_fields/1-fun-0-'/1 394 0.00 94 [ 0.24] erl_anno:set_file/2 385 0.00 94 [ 0.24] erl_internal:comp_op/2 315 0.00 96 [ 0.30] beam_validator:assert_type/2 553 0.00 96 [ 0.17] maps:to_list/1 532 0.00 96 [ 0.18] v3_core:'-cexpr/3-anonymous-1-'/3 770 0.00 97 [ 0.13] v3_codegen:allocate_x0/3 389 0.00 97 [ 0.25] erl_lint:'-def_fields/3-fun-0-'/3 394 0.00 97 [ 0.25] sys_core_fold:opt_simple_let/3 725 0.00 99 [ 0.14] lists:dropwhile/2 672 0.00 99 [ 0.15] beam_jump:opt/2 168 0.00 99 [ 0.59] erl_lint:is_local_function/2 331 0.00 99 [ 0.30] sets:is_element/2 442 0.00 100 [ 0.23] v3_life:'-function/1-lc$^0/1-1-'/1 407 0.00 101 [ 0.25] erl_anno:set_generated/2 788 0.00 101 [ 0.13] beam_jump:insert_fc_labels/2 552 0.00 103 [ 0.19] erl_expand_records:is_in_guard/0 385 0.00 103 [ 0.27] erl_expand_records:'-record_setel/4-fun-0-'/4 383 0.00 103 [ 0.27] sys_core_fold:delay_build/2 385 0.00 105 [ 0.27] v3_life:'-function/1-lc$^0/1-0-'/1 407 0.00 106 [ 0.26] v3_codegen:'-cg_reg_args/2-lc$^0/1-0-'/2 510 0.00 106 [ 0.21] beam_validator:verify_y_init_1/1 846 0.00 106 [ 0.13] beam_validator:'-prune_x_regs/2-lc$^0/1-0-'/2 467 0.00 106 [ 0.23] v3_core:'-bin_expand_strings/1-anonymous-1-'/2 524 0.00 107 [ 0.20] sys_core_fold:opt_simple_let_2/7 725 0.00 107 [ 0.15] dict:store/3 518 0.00 107 [ 0.21] beam_except:translate_1/5 856 0.00 107 [ 0.13] erl_expand_records:record_update/5 385 0.00 108 [ 0.28] beam_bs:bsm_subst_label/3 444 0.00 109 [ 0.25] gb_sets:from_list/1 508 0.00 109 [ 0.21] v3_kernel:get_free/3 297 0.00 110 [ 0.37] beam_dict:literal/2 400 0.00 110 [ 0.28] erl_lint:record_field/4 770 0.00 110 [ 0.14] binary:encode_unsigned/1 702 0.00 110 [ 0.16] sys_core_fold:move_let_into_expr/3 728 0.00 111 [ 0.15] sys_core_fold:opt_simple_let_0/3 725 0.00 111 [ 0.15] beam_utils:is_killed_block/2 436 0.00 111 [ 0.25] erl_expand_records:record_setel/4 383 0.00 111 [ 0.29] erlang:iolist_size/1 703 0.00 111 [ 0.16] erl_lint:bif_clash_specifically_disabled/2 280 0.00 112 [ 0.40] erl_expand_records:record_exprs/4 770 0.00 112 [ 0.15] cerl:data_type/1 385 0.00 112 [ 0.29] sys_core_fold:opt_simple_let_1/4 725 0.00 113 [ 0.16] erl_expand_records:call_error/2 385 0.00 113 [ 0.29] cerl:bitstr_type/1 524 0.00 113 [ 0.22] v3_core:verify_suitable_fields/1 765 0.00 114 [ 0.15] v3_core:fold_match/2 385 0.00 114 [ 0.30] lists:usplit_2_1/6 641 0.00 114 [ 0.18] cerl:bitstr_size/1 524 0.00 114 [ 0.22] cerl:bitstr_unit/1 524 0.00 114 [ 0.22] eval_bits:expr_grp/4 877 0.00 115 [ 0.13] beam_jump:'-opt/2-anonymous-0-'/2 276 0.00 115 [ 0.42] erl_lint:'-pattern_bin/5-fun-0-'/4 524 0.00 117 [ 0.22] cerl:bitstr_flags/1 524 0.00 117 [ 0.22] erlang:'--'/2 172 0.00 117 [ 0.68] sys_core_fold:is_compiler_generated/1 990 0.00 118 [ 0.12] cerl:bitstr_val/1 524 0.00 118 [ 0.23] erl_lint:pattern_list/5 770 0.00 119 [ 0.15] sofs:is_type/1 514 0.00 119 [ 0.23] sys_core_bsm:bsm_an_1/2 859 0.00 120 [ 0.14] cerl_sets:from_list/1 949 0.00 122 [ 0.13] sys_core_fold:update_let_types_1/3 935 0.00 123 [ 0.13] lists:merge2_1/4 837 0.00 123 [ 0.15] lists:rmerge2_2/5 863 0.00 123 [ 0.14] v3_core:'-uexpr/3-anonymous-1-'/2 550 0.00 124 [ 0.23] sys_core_dsetel:'-visit/2-anonymous-1-'/2 550 0.00 124 [ 0.23] lists:duplicate/2 497 0.00 125 [ 0.25] cerl:update_c_bitstr/6 524 0.00 126 [ 0.24] v3_core:coerce_to_float/2 524 0.00 127 [ 0.24] v3_codegen:'-extend_stack/4-lc$^0/1-0-'/4 924 0.00 127 [ 0.14] erl_lint:pat_bit_size/4 524 0.00 127 [ 0.24] sys_core_fold:eval_setelement/4 766 0.00 129 [ 0.17] sys_core_fold:'-let_substs/3-anonymous-1-'/2 495 0.00 130 [ 0.26] beam_block:eliminate_use_of_from_reg/4 487 0.00 130 [ 0.27] v3_core:upat_element/4 524 0.00 131 [ 0.25] dict:'-store/3-fun-0-'/3 518 0.00 132 [ 0.25] v3_core:lit_list_vars/2 906 0.00 133 [ 0.15] beam_jump:remove_unused_labels/1 504 0.00 133 [ 0.26] erl_lint:is_pattern_expr/1 524 0.00 133 [ 0.25] sys_core_fold:bit_pat_warn_int/4 1048 0.00 134 [ 0.13] sys_core_fold:'-sub_subst_var/3-lc$^0/1-0-'/3 598 0.00 134 [ 0.22] v3_codegen:turn_yregs/2 531 0.00 134 [ 0.25] v3_kernel:pat_list_vars/1 894 0.00 134 [ 0.15] erl_lint:is_pattern_expr_1/1 524 0.00 134 [ 0.26] v3_core:pat_segment/2 524 0.00 135 [ 0.26] erl_lint:'-expr_bin/4-fun-0-'/4 612 0.00 135 [ 0.22] beam_validator:assert_type/3 553 0.00 136 [ 0.25] erl_lint:pat_bit_expr/4 524 0.00 136 [ 0.26] erl_anno:reset_simplify/1 1173 0.00 136 [ 0.12] erl_lint:imported/3 412 0.00 138 [ 0.33] v3_core:'-pat_bin/2-lc$^0/1-0-'/2 655 0.00 139 [ 0.21] erl_anno:simplify/1 1173 0.00 139 [ 0.12] sys_core_fold:kill_types2/2 529 0.00 140 [ 0.26] v3_kernel:select_bin_int_1/4 407 0.00 140 [ 0.34] sys_core_dsetel:bind_vars/3 549 0.00 141 [ 0.26] lists:rmergel/2 934 0.00 143 [ 0.15] beam_validator:'-index_bs_start_match/2-anonymous-0-'/2 672 0.00 143 [ 0.21] erl_anno:set_anno/3 1173 0.00 143 [ 0.12] erl_lint:has_wildcard_field/1 770 0.00 144 [ 0.19] erl_lint:'-expr/3-fun-6-'/3 612 0.00 144 [ 0.24] lists:merge2_2/5 935 0.00 145 [ 0.16] beam_jump:insert_labels/4 1191 0.00 145 [ 0.12] erl_lint:bit_size/4 612 0.00 145 [ 0.24] erl_eval:line/1 636 0.00 146 [ 0.23] beam_dict:import/4 535 0.00 146 [ 0.27] sys_core_fold:'-expr/3-lc$^0/1-3-'/2 626 0.00 148 [ 0.24] beam_utils:live_opt/1 504 0.00 148 [ 0.29] beam_validator:all_ms_in_x_regs/2 863 0.00 148 [ 0.17] sys_core_fold:copy_type/3 495 0.00 149 [ 0.30] sys_core_fold:bin_pattern/2 1048 0.00 150 [ 0.14] lists:merge3_2/6 870 0.00 150 [ 0.17] erl_parse:map_anno/2 1155 0.00 150 [ 0.13] sys_core_fold:sub_subst_var/3 495 0.00 152 [ 0.31] erl_eval:partial_eval/1 636 0.00 153 [ 0.24] gb_sets:balance_list/2 787 0.00 153 [ 0.19] erl_anno:set/3 1173 0.00 154 [ 0.13] v3_core:bin_element/1 612 0.00 155 [ 0.25] v3_kernel:'-select_bin_con_1/1-anonymous-0-'/1 655 0.00 157 [ 0.24] v3_core:upat_bin/4 655 0.00 158 [ 0.24] eval_bits:eval_exp_field1/6 668 0.00 158 [ 0.24] erl_internal:bif/2 497 0.00 159 [ 0.32] sys_core_fold:fold_call_2/5 945 0.00 159 [ 0.17] v3_codegen:match_cg/6 938 0.00 159 [ 0.17] v3_codegen:'-top_level_block/4-anonymous-0-'/3 600 0.00 159 [ 0.27] erl_eval:ev_expr/1 636 0.00 160 [ 0.25] v3_kernel:'-uexpr/3-anonymous-11-'/2 669 0.00 160 [ 0.24] beam_asm:to_bytes/1 702 0.00 162 [ 0.23] v3_kernel:pattern_bin_1/4 655 0.00 165 [ 0.25] v3_codegen:turn_yreg/2 690 0.00 167 [ 0.24] lists:merge3_1/6 924 0.00 168 [ 0.18] erl_expand_records:mark_record/2 489 0.00 168 [ 0.34] beam_validator:labels_1/2 843 0.00 172 [ 0.20] v3_core:'-expr_bin/3-lc$^0/1-0-'/1 765 0.00 174 [ 0.23] sys_core_fold:fold_non_lit_args/5 945 0.00 174 [ 0.18] lists:mergel/2 1262 0.00 174 [ 0.14] eval_bits:eval_exp_field/6 668 0.00 174 [ 0.26] erl_parse:abstract/3 787 0.00 174 [ 0.22] lists:flatmap/2 732 0.00 175 [ 0.24] core_lib:vu_expr/2 956 0.00 177 [ 0.19] beam_validator:deallocate/1 673 0.00 178 [ 0.26] erl_lint:warn_invalid_record/3 770 0.00 178 [ 0.23] erl_expand_records:record_exprs/2 385 0.00 178 [ 0.46] maps:get/2 1291 0.00 178 [ 0.14] beam_bs:bsm_reroute/4 1362 0.00 179 [ 0.13] v3_codegen:finish_select_binary/1 775 0.00 179 [ 0.23] dict:fetch_val/2 1124 0.00 179 [ 0.16] beam_validator:verify_no_ct/1 673 0.00 179 [ 0.27] sys_core_fold:update_let_types/3 725 0.00 181 [ 0.25] beam_utils:is_killed/3 773 0.00 181 [ 0.23] erl_lint:vtmerge/1 612 0.00 181 [ 0.30] v3_core:'-new_in_all/1-anonymous-0-'/2 770 0.00 182 [ 0.24] erl_lint:is_valid_record/1 770 0.00 185 [ 0.24] maps:size/1 391 0.00 187 [ 0.48] beam_utils:is_killed_at/3 1176 0.00 188 [ 0.16] beam_record:is_killed/4 772 0.00 189 [ 0.24] beam_validator:verify_call_args/3 724 0.00 191 [ 0.26] v3_codegen:local_func_label/2 448 0.00 193 [ 0.43] sys_core_fold:simplify_let/2 728 0.00 194 [ 0.27] beam_block:is_killed_or_used/2 801 0.00 194 [ 0.24] erl_expand_records:new_var/2 772 0.00 194 [ 0.25] v3_core:'-uexpr/3-anonymous-6-'/3 770 0.00 196 [ 0.25] beam_validator:verify_y_init/1 727 0.00 196 [ 0.27] beam_utils:index_labels/1 840 0.00 197 [ 0.23] dict:rehash/4 848 0.00 199 [ 0.23] sys_core_fold:opt_not_in_let_0/3 667 0.00 200 [ 0.30] erl_expand_records:no_compiler_warning/1 770 0.00 200 [ 0.26] lists:usplit_1_1/6 1023 0.00 201 [ 0.20] eval_bits:make_bit_type/3 724 0.00 201 [ 0.28] cerl:case_arg/1 859 0.00 201 [ 0.23] erl_expand_records:index_expr/4 768 0.00 202 [ 0.26] sys_core_fold:opt_case_in_let/1 728 0.00 204 [ 0.28] v3_core:'-uexpr/3-anonymous-4-'/2 924 0.00 205 [ 0.22] sys_core_fold:opt_not_in_let/1 725 0.00 205 [ 0.28] lists:umerge3_2/7 1314 0.00 206 [ 0.16] sys_core_fold:let_substs/3 728 0.00 208 [ 0.29] erl_expand_records:new_var_name/1 772 0.00 208 [ 0.27] erlang:make_fun/3 962 0.00 208 [ 0.22] sys_core_fold:let_substs_1/3 728 0.00 210 [ 0.29] sys_core_fold:case_opt_arg_1/3 1705 0.00 211 [ 0.12] cerl:update_c_case/3 859 0.00 211 [ 0.25] lists:umerge3_1/7 1183 0.01 216 [ 0.18] beam_except:dig_out/2 856 0.01 218 [ 0.25] v3_core:uclauses/3 773 0.01 219 [ 0.28] sys_core_fold:add_warning/2 853 0.01 220 [ 0.26] erl_lint:record_expr/4 770 0.01 220 [ 0.29] sys_core_fold:should_suppress_warning/1 853 0.01 222 [ 0.26] sys_core_fold:'-let_substs/3-lc$^0/1-0-'/1 941 0.01 223 [ 0.24] sys_core_fold:'-warn_no_clause_match/2-anonymous-1-'/1 990 0.01 225 [ 0.23] ordsets:del_element/2 892 0.01 225 [ 0.25] v3_life:'-expr/3-lc$^0/1-8-'/1 938 0.01 226 [ 0.24] sys_core_fold:matches_data/2 728 0.01 228 [ 0.31] beam_validator:verify_call_args_1/2 1746 0.01 229 [ 0.13] lists:last/2 1769 0.01 231 [ 0.13] erl_lint:expr_var/4 1858 0.01 231 [ 0.12] beam_validator:merge_y_regs_1/3 2020 0.01 232 [ 0.11] v3_kernel:handle_reuse_annos/2 876 0.01 233 [ 0.27] erl_expand_records:'-record_setel/4-lc$^1/1-0-'/1 766 0.01 233 [ 0.30] sys_core_fold:sub_add_scope/2 728 0.01 235 [ 0.32] lists:rmerge3_1/6 1174 0.01 236 [ 0.20] beam_utils:check_liveness_block_1/6 1971 0.01 237 [ 0.12] erl_lint:pat_var/5 1823 0.01 237 [ 0.13] v3_core:new_in_all/1 773 0.01 239 [ 0.31] lists:umerge2_1/5 1700 0.01 239 [ 0.14] v3_core:cclauses/3 924 0.01 242 [ 0.26] dict:fold/3 1930 0.01 242 [ 0.13] erl_lint:elemtype_check/4 1136 0.01 242 [ 0.21] sys_core_fold:'-pattern/3-anonymous-1-'/2 1048 0.01 246 [ 0.23] erl_lint:'-copy_expr/2-fun-0-'/2 1155 0.01 246 [ 0.21] beam_jump:maps_append_list/3 985 0.01 248 [ 0.25] erlang:md5/1 1 0.01 248 [ 248.00] v3_kernel:kmatch/4 859 0.01 250 [ 0.29] beam_dead:shortcut_specific_label/3 1840 0.01 252 [ 0.14] lists:rmerge3_2/6 1557 0.01 254 [ 0.16] v3_kernel:'-expand_pat_lit/2-lc$^0/1-0-'/2 1155 0.01 255 [ 0.22] v3_kernel:flatten_seq/1 859 0.01 257 [ 0.30] beam_dict:line/2 758 0.01 257 [ 0.34] v3_codegen:select_extract_tuple/6 846 0.01 258 [ 0.30] lists:rmerge2_1/4 1873 0.01 261 [ 0.14] eval_bits:eval_field/3 668 0.01 262 [ 0.39] v3_codegen:return_cg/5 1844 0.01 262 [ 0.14] beam_except:dig_out_block/1 788 0.01 262 [ 0.33] v3_kernel:is_in_guard/1 913 0.01 266 [ 0.29] beam_dead:combine_eqs/4 962 0.01 266 [ 0.28] lists:rumerge3_12_3/7 2027 0.01 268 [ 0.13] erl_lint:check_record/4 1206 0.01 268 [ 0.22] v3_kernel:force_variable/2 903 0.01 270 [ 0.30] sys_core_fold:signedness/1 1048 0.01 271 [ 0.26] v3_codegen:'-save_carefully/4-lc$^0/1-0-'/4 2018 0.01 271 [ 0.13] beam_utils:check_liveness_at/3 1182 0.01 272 [ 0.23] v3_kernel:match_pre/3 859 0.01 272 [ 0.32] lists:rumerge2_2/5 2033 0.01 273 [ 0.13] erl_lint:bit_type/4 1136 0.01 274 [ 0.24] erl_expand_records:'-pattern_fields/2-fun-0-'/3 1155 0.01 274 [ 0.24] erl_anno:default/2 1173 0.01 275 [ 0.23] v3_kernel:build_match/2 859 0.01 282 [ 0.33] sys_core_fold:'-var_list/2-anonymous-0-'/2 1067 0.01 283 [ 0.27] erl_expand_records:'-record_inits/2-fun-0-'/3 1155 0.01 284 [ 0.25] v3_core:novars/2 1155 0.01 285 [ 0.25] maps:from_list/1 1479 0.01 285 [ 0.19] v3_codegen:'-match_cg/5-anonymous-1-'/6 2140 0.01 287 [ 0.13] v3_kernel:match_vars/2 859 0.01 288 [ 0.34] erl_lint:bit_size_check/4 1136 0.01 288 [ 0.25] erl_lint:add_bit_size/5 1136 0.01 288 [ 0.25] v3_kernel:select_bin_con_2/1 2206 0.01 289 [ 0.13] erl_lint:copy_expr/2 1155 0.01 291 [ 0.25] erl_parse:'-map_anno/2-fun-0-'/3 1155 0.01 292 [ 0.25] beam_peep:is_test_redundant_1/3 2768 0.01 293 [ 0.11] v3_core:make_bit_type/3 1136 0.01 295 [ 0.26] beam_flatten:move_past_kill/3 1329 0.01 296 [ 0.22] gb_trees:balance/2 2225 0.01 296 [ 0.13] sys_core_fold:let_subst_list/3 1436 0.01 302 [ 0.21] sys_core_fold:var_list/2 1013 0.01 302 [ 0.30] io_lib:latin1_char_list/1 2698 0.01 302 [ 0.11] sys_core_fold:bin_pat_warn/1 1048 0.01 306 [ 0.29] beam_validator:validate_src/2 1153 0.01 306 [ 0.27] v3_core:force_novars/2 1155 0.01 308 [ 0.27] cerl:c_tuple/1 1158 0.01 308 [ 0.27] lists:usort/1 2390 0.01 309 [ 0.13] v3_codegen:load_vars/2 1023 0.01 310 [ 0.30] erl_lint:warn_unused_vars/3 2645 0.01 310 [ 0.12] beam_jump:drop_upto_label/1 1534 0.01 311 [ 0.20] v3_codegen:enter_cg/6 1610 0.01 311 [ 0.19] cerl:call_name/1 1398 0.01 311 [ 0.22] beam_validator:'-validate_src/2-anonymous-0-'/2 1344 0.01 312 [ 0.23] erl_lint:guard_tests/3 2628 0.01 312 [ 0.12] erl_bits:set_bit_1/2 2784 0.01 314 [ 0.11] sets:get_bucket_s/2 1919 0.01 315 [ 0.16] beam_validator:kill_state/1 2080 0.01 315 [ 0.15] erl_anno:is_settable/2 1173 0.01 316 [ 0.27] cerl:update_c_tuple_skel/2 1279 0.01 316 [ 0.25] cerl:call_args/1 1398 0.01 316 [ 0.23] erl_expand_records:strict_record_tests/1 2310 0.01 319 [ 0.14] cerl:call_module/1 1398 0.01 321 [ 0.23] lists:umerge3_12_3/6 2562 0.01 323 [ 0.13] beam_validator:merge_states_1/2 2020 0.01 323 [ 0.16] erl_lint:'-init_fields/3-lc$^0/1-0-'/3 1206 0.01 323 [ 0.27] v3_core:fail_clause/3 1158 0.01 324 [ 0.28] v3_core:'-constant_bin_1/1-anonymous-0-'/2 1224 0.01 324 [ 0.26] sys_core_fold:will_fail/1 1218 0.01 326 [ 0.27] v3_kernel:match_con_1/4 2105 0.01 327 [ 0.16] erl_expand_records:strict_record_updates/1 2310 0.01 329 [ 0.14] erl_lint:check_unused_vars/3 2611 0.01 330 [ 0.13] v3_kernel:select_bin_con_1/1 2105 0.01 335 [ 0.16] v3_kernel:'-handle_reuse_annos/2-lc$^0/1-0-'/2 1796 0.01 337 [ 0.19] erl_expand_records:record_fields/2 1291 0.01 337 [ 0.26] erl_lint:'-init_fields/6-fun-1-'/3 1155 0.01 341 [ 0.30] cerl:ann_c_cons/3 2543 0.01 342 [ 0.13] cerl:update_c_call/4 1398 0.01 342 [ 0.24] lists:foreach/2 2506 0.01 345 [ 0.14] dict:fold_dict/3 1930 0.01 349 [ 0.18] beam_type:merge_type_info/2 1582 0.01 351 [ 0.22] beam_dead:shortcut_bs_test_1/4 3060 0.01 354 [ 0.12] v3_kernel:match_con/4 2105 0.01 356 [ 0.17] beam_jump:collect_labels_1/3 1976 0.01 359 [ 0.18] erl_lint:used_record/2 1291 0.01 359 [ 0.28] lists:rumerge2_1/4 2847 0.01 361 [ 0.13] beam_utils:'-live_opt/1-anonymous-0-'/1 1512 0.01 364 [ 0.24] v3_codegen:match_fmf/4 2233 0.01 369 [ 0.17] erl_bifs:is_safe/3 1414 0.01 370 [ 0.26] beam_dict:fname/2 755 0.01 371 [ 0.49] ordsets:from_list/1 2210 0.01 373 [ 0.17] v3_kernel:select_bin_int/1 2105 0.01 374 [ 0.18] erl_lint:'-pattern_list/5-fun-0-'/5 1540 0.01 374 [ 0.24] v3_core:ufun_clause/3 2747 0.01 376 [ 0.14] erl_lint:'-check_fields/6-fun-0-'/6 1540 0.01 376 [ 0.24] erl_lint:vtnew/2 2696 0.01 379 [ 0.14] beam_dead:shortcut_select_list/4 2588 0.01 384 [ 0.15] v3_core:'-uclauses/3-anonymous-0-'/3 1543 0.01 385 [ 0.25] v3_core:uclause/3 2316 0.01 386 [ 0.17] sys_core_fold:call_1/5 2860 0.01 387 [ 0.14] dict:get_bucket_s/2 2149 0.01 387 [ 0.18] lists:last/1 1352 0.01 389 [ 0.29] beam_block:opt_tuple_element_1/4 1665 0.01 389 [ 0.23] cerl:data_arity/1 385 0.01 389 [ 1.01] v3_codegen:trap_bif/3 1397 0.01 394 [ 0.28] erl_lint:'-vtmerge/1-fun-0-'/2 1836 0.01 394 [ 0.21] lists:partition/4 2764 0.01 398 [ 0.14] sys_core_fold:fold_call/5 2860 0.01 404 [ 0.14] v3_kernel:is_remote_bif/3 1397 0.01 408 [ 0.29] v3_core:'-lit_list_vars/2-anonymous-0-'/2 1869 0.01 411 [ 0.22] erl_lint:check_field/7 1540 0.01 413 [ 0.27] beam_dead:shortcut_rel_op_fp/3 3432 0.01 419 [ 0.12] erl_internal:op_type/2 1187 0.01 422 [ 0.36] cerl:var_name/1 1705 0.01 423 [ 0.25] beam_record:is_tagged_tuple/4 789 0.01 424 [ 0.54] erl_lint:'-unused_vars/3-fun-0-'/2 1874 0.01 432 [ 0.23] erl_internal:guard_bif/2 1241 0.01 434 [ 0.35] v3_core:new_var/2 1636 0.01 436 [ 0.27] v3_codegen:move_unsaved/4 3556 0.01 436 [ 0.12] lists:droplast/1 1734 0.01 440 [ 0.25] v3_kernel:op_vars/1 1486 0.01 440 [ 0.30] sets:get_bucket/2 1901 0.01 441 [ 0.23] v3_life:call_op/1 1678 0.01 443 [ 0.26] sys_core_fold:move_case_into_arg/2 1766 0.01 443 [ 0.25] cerl:is_c_var/1 1844 0.01 446 [ 0.24] beam_dead:normalize_op_1/3 3569 0.01 448 [ 0.13] v3_kernel:'-ubody/3-anonymous-1-'/2 1878 0.01 451 [ 0.24] dict:find/2 1660 0.01 451 [ 0.27] v3_life:'-expr/3-lc$^0/1-3-'/1 1844 0.01 455 [ 0.25] sys_core_fold:get_line/1 1891 0.01 460 [ 0.24] beam_utils:check_liveness_block_2/4 1954 0.01 460 [ 0.24] lists:umerge3_21_3/6 3631 0.01 460 [ 0.13] beam_dead:will_succeed/2 1739 0.01 461 [ 0.27] v3_codegen:enter_line/3 1609 0.01 463 [ 0.29] sets:get_slot/2 1901 0.01 464 [ 0.24] v3_codegen:func_vars/1 1678 0.01 468 [ 0.28] lists:rumerge3_2/7 2857 0.01 470 [ 0.16] v3_codegen:select_cg/6 2140 0.01 471 [ 0.22] sys_core_fold:is_boolean_type/2 1874 0.01 477 [ 0.25] dict:get_bucket/2 2048 0.01 479 [ 0.23] sys_core_fold:case_expand_var/2 1705 0.01 482 [ 0.28] beam_validator:min/2 2020 0.01 482 [ 0.24] sys_core_fold:warn_no_clause_match/2 1766 0.01 486 [ 0.28] beam_type:tdb_kill_xregs/1 3424 0.01 486 [ 0.14] gb_sets:add/2 2174 0.01 488 [ 0.22] cerl:is_c_values/1 1831 0.01 488 [ 0.27] erl_bits:as_list/1 1860 0.01 490 [ 0.26] sys_core_fold:'-call_1/5-lc$^0/1-0-'/2 1958 0.01 492 [ 0.25] erl_bits:merge_bittype/2 2060 0.01 495 [ 0.24] sys_core_fold:fold_call_1/5 2857 0.01 496 [ 0.17] dict:mk_seg/1 1933 0.01 497 [ 0.26] sys_core_fold:get_type/2 1874 0.01 499 [ 0.27] erl_expand_records:find_field/2 2312 0.01 499 [ 0.22] erl_bits:check_unit/1 2272 0.01 508 [ 0.22] sys_core_fold:case_opt_arg/4 1844 0.01 513 [ 0.28] beam_validator:merge_y_regs/2 2020 0.01 515 [ 0.25] sys_core_fold:case_opt/3 1766 0.01 518 [ 0.29] gb_trees:to_list_1/1 2225 0.01 518 [ 0.23] v3_kernel:match_guard/3 4142 0.01 524 [ 0.13] v3_codegen:'-saves/3-lc$^0/1-0-'/3 4562 0.01 525 [ 0.12] sys_core_fold:opt_bool_case/2 1766 0.01 526 [ 0.30] v3_life:type/1 2140 0.01 528 [ 0.25] beam_validator:valfun_2/2 3927 0.01 529 [ 0.13] beam_type:tag_literal/1 2146 0.01 532 [ 0.25] beam_split:make_block/2 3875 0.01 532 [ 0.14] sys_core_dsetel:'-visit/2-anonymous-4-'/2 2362 0.01 534 [ 0.23] dict:new/0 1933 0.01 536 [ 0.28] erlang:tuple_to_list/1 3749 0.01 537 [ 0.14] v3_core:'-uexpr/3-anonymous-5-'/2 2362 0.01 538 [ 0.23] erl_bifs:is_exit_bif/3 3414 0.01 538 [ 0.16] sys_core_fold:call/4 2860 0.01 539 [ 0.19] beam_jump:initial_labels/2 2844 0.01 539 [ 0.19] sys_core_fold:eval_case/2 1766 0.01 540 [ 0.31] sys_core_fold:shadow_warning/2 2619 0.01 541 [ 0.21] sys_core_fold:is_bool_expr/2 1976 0.01 542 [ 0.27] lists:partition/2 2109 0.01 543 [ 0.26] beam_validator:valfun_3/2 3927 0.01 543 [ 0.14] v3_codegen:build_enter/3 1609 0.01 544 [ 0.34] beam_utils:check_liveness_block/3 1996 0.01 545 [ 0.27] v3_kernel:call_type/3 1398 0.01 549 [ 0.39] dict:fold_segs/4 3826 0.01 549 [ 0.14] beam_validator:set_type_reg/3 3543 0.01 553 [ 0.16] sys_core_fold:case_will_var_match/1 1844 0.01 555 [ 0.30] v3_core:annotate_cons/4 2271 0.01 559 [ 0.25] cerl_sets:new/0 2155 0.01 565 [ 0.26] v3_kernel:uexpr/3 2891 0.01 572 [ 0.20] v3_codegen:find_loc/3 3500 0.01 576 [ 0.16] v3_kernel:'-uexpr/3-anonymous-13-'/2 2464 0.01 580 [ 0.24] orddict:erase/2 3096 0.01 580 [ 0.19] beam_validator:'-valfun_4/2-anonymous-1-'/2 2588 0.01 586 [ 0.23] beam_dead:prune_redundant/2 2588 0.01 591 [ 0.23] lists:usplit_1/5 3479 0.01 592 [ 0.17] erlang:atom_to_list/1 886 0.01 595 [ 0.67] v3_kernel:partition_intersection/4 2140 0.01 599 [ 0.28] v3_kernel:'-match_con_1/4-anonymous-0-'/3 2140 0.01 600 [ 0.28] erl_parse:abstract/2 17 0.01 602 [ 35.41] v3_life:type_clause/5 2140 0.01 607 [ 0.28] v3_codegen:select_val_cg/6 1904 0.01 608 [ 0.32] erl_lint:expr_list/3 4097 0.01 608 [ 0.15] erl_lint:'-clauses/2-fun-0-'/2 2560 0.01 609 [ 0.24] v3_codegen:fetch_reg/2 3078 0.01 611 [ 0.20] beam_dead:shortcut_select_label/4 2453 0.01 611 [ 0.25] v3_kernel:select_bin_con/1 2105 0.01 622 [ 0.30] sys_core_fold:'-signedness/1-lc$^0/1-0-'/1 3144 0.01 626 [ 0.20] beam_peep:prune_redundant_values/2 2633 0.01 629 [ 0.24] erl_lint:head/3 2560 0.01 632 [ 0.25] v3_kernel:build_select/2 2105 0.01 634 [ 0.30] erl_lint:clause/2 2560 0.01 638 [ 0.25] erl_lint:guard/3 2594 0.01 638 [ 0.25] v3_kernel:atomic_list/3 1784 0.01 639 [ 0.36] beam_validator:branch_state/2 3952 0.01 640 [ 0.16] v3_kernel:match_value/5 2140 0.02 642 [ 0.30] beam_validator:get_move_term_type/2 2612 0.02 642 [ 0.25] beam_type:tdb_update/2 4792 0.02 647 [ 0.14] erl_lint:unused_vars/3 2645 0.02 650 [ 0.25] beam_utils:check_liveness/3 2812 0.02 653 [ 0.23] v3_core:'-ufun_clauses/3-anonymous-0-'/3 2596 0.02 656 [ 0.25] erl_bits:type_to_record/1 2784 0.02 659 [ 0.24] erlang:throw/1 2616 0.02 661 [ 0.25] erlang:list_to_binary/1 21 0.02 662 [ 31.52] dict:store_bkt_val/3 3022 0.02 666 [ 0.22] v3_codegen:block_cg/4 4224 0.02 667 [ 0.16] beam_asm:encode_list/3 5137 0.02 672 [ 0.13] beam_peep:is_test_redundant/3 2768 0.02 684 [ 0.25] beam_dead:useful_to_insert_label/1 2912 0.02 684 [ 0.23] beam_flatten:norm/1 2607 0.02 690 [ 0.26] sys_core_fold:'-function_1/1-anonymous-0-'/1 251 0.02 694 [ 2.76] sys_core_fold:'-warn_no_clause_match/2-anonymous-0-'/1 2927 0.02 696 [ 0.24] cerl_sets:'-from_list/1-lc$^0/1-0-'/1 2976 0.02 696 [ 0.23] erl_bits:check_size_unit/3 2996 0.02 697 [ 0.23] erl_bits:set_bit/1 2996 0.02 699 [ 0.23] v3_kernel:'-match_pre/3-anonymous-0-'/3 4145 0.02 701 [ 0.17] lists:usplit_2/5 5084 0.02 716 [ 0.14] beam_dead:shortcut_bs_test/3 3060 0.02 717 [ 0.23] v3_life:'-expr/3-lc$^0/1-11-'/1 2963 0.02 718 [ 0.24] gb_trees:keys/2 4330 0.02 720 [ 0.17] cerl:update_c_tuple/2 2979 0.02 724 [ 0.24] v3_life:'-expr/3-lc$^0/1-7-'/3 3487 0.02 729 [ 0.21] beam_validator:valfun_4/2 3927 0.02 734 [ 0.19] gb_sets:count/1 3125 0.02 735 [ 0.24] gb_sets:is_element/2 3542 0.02 743 [ 0.21] lists:umerge2_2/4 5786 0.02 744 [ 0.13] v3_codegen:top_level_block/4 2684 0.02 745 [ 0.28] v3_codegen:cg/5 4745 0.02 749 [ 0.16] sys_core_fold:useless_call/2 2860 0.02 751 [ 0.26] erl_bifs:is_pure/3 2857 0.02 754 [ 0.26] v3_kernel:match_clause/4 4497 0.02 755 [ 0.17] lists:split_1/5 5072 0.02 762 [ 0.15] v3_kernel:expand_pat_lit/2 3339 0.02 764 [ 0.23] sys_core_bsm:bsm_leftmost_1/2 5004 0.02 765 [ 0.15] v3_core:cclause/3 5063 0.02 782 [ 0.15] sys_core_fold:case_opt_args/5 3610 0.02 785 [ 0.22] lists:any/2 4298 0.02 787 [ 0.18] v3_kernel:handle_reuse_anno/2 3042 0.02 787 [ 0.26] v3_kernel:'-atomic_list/3-anonymous-0-'/3 5982 0.02 792 [ 0.13] gb_trees:empty/0 3538 0.02 793 [ 0.22] dict:find_val/2 5799 0.02 800 [ 0.14] beam_dead:shortcut_any_label/2 3432 0.02 806 [ 0.23] v3_core:is_simple/1 3320 0.02 809 [ 0.24] dict:append/3 6241 0.02 811 [ 0.13] v3_kernel:literal/2 4667 0.02 813 [ 0.17] dict:maybe_expand/2 6759 0.02 814 [ 0.12] v3_codegen:put_reg_1/3 3274 0.02 820 [ 0.25] erl_internal:new_type_test/2 4564 0.02 824 [ 0.18] v3_kernel:'-umatch_list/3-anonymous-0-'/3 6742 0.02 837 [ 0.12] v3_codegen:line/1 3137 0.02 838 [ 0.27] erl_lint:merge_used/2 3765 0.02 841 [ 0.22] v3_codegen:unsaved_registers/5 3539 0.02 845 [ 0.24] erl_lint:merge_lines/2 3766 0.02 848 [ 0.23] erlang:list_to_tuple/1 5982 0.02 849 [ 0.14] beam_validator:check_limit/1 3543 0.02 857 [ 0.24] gb_trees:from_orddict/1 3637 0.02 862 [ 0.24] v3_codegen:gen_moves/2 3539 0.02 869 [ 0.25] lists:append/1 4010 0.02 883 [ 0.22] erts_internal:port_control/3 3 0.02 883 [ 294.33] beam_type:simplify_float/2 3855 0.02 890 [ 0.23] beam_flatten:norm_block/2 4760 0.02 902 [ 0.19] v3_codegen:move_unsaved/3 3539 0.02 905 [ 0.26] v3_kernel:group_value/3 2140 0.02 915 [ 0.43] erl_expand_records:head/2 4100 0.02 915 [ 0.22] v3_kernel:build_alt/2 4142 0.02 920 [ 0.22] lists:rumerge3_1/6 6334 0.02 925 [ 0.15] erl_lint:'-vtupdate/2-fun-0-'/3 3765 0.02 927 [ 0.25] erl_expand_records:guard/2 4134 0.02 931 [ 0.23] beam_validator:get_fls/1 3927 0.02 932 [ 0.24] erl_expand_records:opt_rec_vars/1 4100 0.02 937 [ 0.23] sets:add_element/2 1459 0.02 947 [ 0.65] cerl_clauses:match_list/2 3840 0.02 947 [ 0.25] sys_core_dsetel:'-visit/2-anonymous-2-'/2 4145 0.02 951 [ 0.23] beam_block:opt_block/1 3906 0.02 953 [ 0.24] v3_kernel:'-group_value/3-anonymous-1-'/4 4261 0.02 957 [ 0.22] gb_trees:size/1 4040 0.02 957 [ 0.24] v3_core:guard/2 4102 0.02 966 [ 0.24] gb_sets:is_member/2 5890 0.02 968 [ 0.16] v3_core:head/2 4102 0.02 972 [ 0.24] v3_codegen:cg_setup_call/4 3539 0.02 974 [ 0.28] beam_type:flt_liveness/1 3855 0.02 974 [ 0.25] beam_block:find_fixpoint/2 4325 0.02 975 [ 0.23] v3_core:'-cclauses/3-anonymous-0-'/3 4139 0.02 976 [ 0.24] beam_validator:merge_states/3 3932 0.02 981 [ 0.25] erl_expand_records:optimize_is_record/3 4100 0.02 995 [ 0.24] sys_core_fold:sub_get_var/2 7105 0.02 997 [ 0.14] erl_bits:set_bit_type/2 5268 0.02 999 [ 0.19] orddict:new/0 6525 0.02 1002 [ 0.15] erl_anno:record/1 4187 0.02 1003 [ 0.24] beam_type:simplify/2 3855 0.02 1014 [ 0.26] v3_life:'-match/5-lc$^0/1-1-'/5 4245 0.02 1015 [ 0.24] v3_kernel:ubody/3 6590 0.02 1020 [ 0.15] v3_kernel:pattern_list/4 5732 0.02 1026 [ 0.18] v3_codegen:select_labels/4 5741 0.02 1027 [ 0.18] beam_jump:skip_unreachable/3 7552 0.02 1030 [ 0.14] erl_anno:anno_info/3 8470 0.02 1032 [ 0.12] gb_sets:insert/2 4942 0.02 1035 [ 0.21] v3_core:record_anno/2 4187 0.02 1036 [ 0.25] v3_kernel:select_types/2 4210 0.02 1038 [ 0.25] dict:maybe_expand_aux/2 6759 0.02 1038 [ 0.15] beam_type:tdb_copy/3 6132 0.02 1046 [ 0.17] beam_validator:get_term_type/2 4982 0.02 1054 [ 0.21] beam_block:'-opt_block/1-anonymous-0-'/1 4325 0.02 1056 [ 0.24] cerl:tuple_es/1 4258 0.02 1060 [ 0.25] v3_kernel:'-op_vars/1-lc$^0/1-0-'/1 4458 0.02 1061 [ 0.24] v3_core:annotate_tuple/3 4187 0.02 1064 [ 0.25] erl_anno:new_location/1 10503 0.03 1072 [ 0.10] v3_kernel:is_enter_expr/1 4381 0.03 1074 [ 0.25] v3_codegen:cg_call_args/4 3539 0.03 1076 [ 0.30] beam_validator:'-valfun_4/2-lc$^0/1-0-'/2 5041 0.03 1076 [ 0.21] cerl:case_clauses/1 4391 0.03 1078 [ 0.25] erl_lint:vtmerge/2 5277 0.03 1079 [ 0.20] v3_codegen:collect_chain/4 4376 0.03 1103 [ 0.25] v3_codegen:saves/3 4477 0.03 1108 [ 0.25] v3_kernel:sub_size_var/2 4497 0.03 1114 [ 0.25] gb_trees:balance_list/2 5862 0.03 1114 [ 0.19] v3_core:clause/2 4102 0.03 1117 [ 0.27] sys_core_fold:clause_1/5 7922 0.03 1120 [ 0.14] beam_utils:index_label/3 4466 0.03 1120 [ 0.25] v3_kernel:pattern_list/3 4475 0.03 1123 [ 0.25] v3_codegen:'-select_val_cg/6-lc$^0/1-0-'/2 5041 0.03 1130 [ 0.22] v3_codegen:reserve/3 8344 0.03 1132 [ 0.14] v3_codegen:'-save_stack/4-lc$^1/1-1-'/1 4562 0.03 1136 [ 0.25] v3_kernel:arg_val/2 6371 0.03 1139 [ 0.18] beam_trim:trim/3 9557 0.03 1142 [ 0.12] v3_codegen:'-select_cg/6-anonymous-0-'/5 4261 0.03 1153 [ 0.27] v3_codegen:need_heap/2 4534 0.03 1166 [ 0.26] beam_type:'-tdb_update/2-anonymous-0-'/1 4828 0.03 1169 [ 0.24] v3_kernel:get_con/1 4497 0.03 1177 [ 0.26] v3_codegen:save_stack/4 4477 0.03 1179 [ 0.26] cerl:ann_c_tuple/2 6882 0.03 1181 [ 0.17] gb_trees:to_list/1 4973 0.03 1186 [ 0.24] v3_codegen:'-cg_list/5-anonymous-0-'/3 4390 0.03 1187 [ 0.27] v3_core:cguard/2 5063 0.03 1193 [ 0.24] lists:all/2 7433 0.03 1196 [ 0.16] v3_kernel:'-match_con/4-lc$^0/1-0-'/1 5473 0.03 1197 [ 0.22] v3_kernel:'-match_value/5-anonymous-0-'/3 4497 0.03 1199 [ 0.27] gb_trees:update/3 7316 0.03 1210 [ 0.17] sys_core_fold:update_types_2/3 4456 0.03 1211 [ 0.27] v3_codegen:gen_moves/4 8450 0.03 1213 [ 0.14] beam_flatten:block/2 9557 0.03 1217 [ 0.13] v3_codegen:match_cg/5 7260 0.03 1217 [ 0.17] v3_codegen:adjust_stack/4 4477 0.03 1220 [ 0.27] beam_block:opt_moves/2 5254 0.03 1228 [ 0.23] v3_kernel:umatch_list/3 4333 0.03 1229 [ 0.28] beam_type:flt_liveness_1/3 8435 0.03 1229 [ 0.15] v3_life:expr/3 4675 0.03 1240 [ 0.27] erl_expand_records:clauses/2 5002 0.03 1244 [ 0.25] v3_codegen:cg/4 4745 0.03 1255 [ 0.26] v3_codegen:basic_block/2 4470 0.03 1255 [ 0.28] v3_life:body/3 4675 0.03 1256 [ 0.27] v3_core:clauses/2 5006 0.03 1258 [ 0.25] beam_record:rewrite/3 9542 0.03 1259 [ 0.13] orddict:filter/2 7572 0.03 1262 [ 0.17] erl_lint:exprs/3 5120 0.03 1265 [ 0.25] cerl:is_literal/1 5246 0.03 1271 [ 0.24] beam_dead:shortcut_rel_op/4 5365 0.03 1274 [ 0.24] beam_block:opt_move_1/3 5782 0.03 1281 [ 0.22] v3_core:'-new_in_any/1-anonymous-0-'/2 5573 0.03 1288 [ 0.23] beam_validator:valfun/4 8912 0.03 1289 [ 0.14] v3_codegen:cg_list/5 4390 0.03 1294 [ 0.29] gb_sets:balance_list_1/2 5205 0.03 1300 [ 0.25] beam_dead:update_value_dict/3 2588 0.03 1300 [ 0.50] v3_core:uguard/4 5063 0.03 1309 [ 0.26] v3_codegen:collect_block/1 4470 0.03 1309 [ 0.29] sys_core_bsm:bsm_leftmost_2/4 10417 0.03 1313 [ 0.13] beam_asm:assemble_function/3 9587 0.03 1313 [ 0.14] beam_flatten:opt_1/2 10261 0.03 1316 [ 0.13] sys_core_fold:pattern_list/3 7925 0.03 1330 [ 0.17] lists:split_2/5 9072 0.03 1331 [ 0.15] v3_kernel:new_clauses/3 4497 0.03 1333 [ 0.30] erl_lint:exist_field/2 10254 0.03 1336 [ 0.13] v3_kernel:match_guard_1/3 4179 0.03 1337 [ 0.32] erl_lint:keyword_warning/3 5988 0.03 1338 [ 0.22] v3_codegen:'-clear_dead_stk/3-lc$^0/1-0-'/3 11348 0.03 1345 [ 0.12] sys_core_fold:'-case_will_var_match/1-anonymous-0-'/1 4569 0.03 1346 [ 0.29] beam_trim:safe_labels/2 9410 0.03 1359 [ 0.14] orddict:store/3 7603 0.03 1371 [ 0.18] v3_codegen:need_heap_1/2 4675 0.03 1372 [ 0.29] v3_codegen:select_val/5 4261 0.03 1380 [ 0.32] beam_bsm:btb_opt_1/3 11869 0.03 1385 [ 0.12] v3_codegen:order_moves/3 7414 0.03 1393 [ 0.19] beam_dict:atom/2 4805 0.03 1413 [ 0.29] beam_clean:bs_replace/3 11882 0.03 1414 [ 0.12] beam_receive:opt/3 11869 0.03 1425 [ 0.12] v3_kernel:get_match/2 4497 0.03 1428 [ 0.32] beam_utils:is_pure_test/1 4686 0.03 1430 [ 0.31] v3_kernel:atomic/3 5988 0.03 1431 [ 0.24] v3_codegen:add_vls/3 8098 0.03 1436 [ 0.18] beam_block:move_allocates_1/2 9109 0.03 1465 [ 0.16] v3_life:val_clause/5 4497 0.03 1471 [ 0.33] v3_core:'-safe_list/2-anonymous-0-'/2 12288 0.03 1477 [ 0.12] v3_kernel:'-partition/1-anonymous-0-'/2 6163 0.03 1478 [ 0.24] v3_core:cexprs/3 5590 0.04 1507 [ 0.27] v3_kernel:'-group_value/3-anonymous-0-'/2 6241 0.04 1513 [ 0.24] v3_core:uclause/4 5063 0.04 1516 [ 0.30] v3_codegen:longest/2 6572 0.04 1542 [ 0.23] v3_kernel:pre_seq/2 6656 0.04 1546 [ 0.23] v3_codegen:fetch_var/2 5770 0.04 1548 [ 0.27] erl_lint:head/4 7474 0.04 1548 [ 0.21] v3_codegen:'-block_cg/4-anonymous-0-'/2 7067 0.04 1560 [ 0.22] beam_split:split_block/3 8435 0.04 1562 [ 0.19] dict:'-append/3-fun-0-'/3 6241 0.04 1570 [ 0.25] v3_life:'-type_clause/5-lc$^0/1-0-'/5 6637 0.04 1577 [ 0.24] v3_kernel:lit_vars/1 6338 0.04 1577 [ 0.25] lists:keysort/2 8482 0.04 1583 [ 0.19] v3_codegen:find_scratch_reg/2 9018 0.04 1592 [ 0.18] beam_type:tdb_find/2 5902 0.04 1597 [ 0.27] beam_clean:update_work_list/2 11882 0.04 1602 [ 0.13] v3_kernel:clause_val/1 6371 0.04 1619 [ 0.25] v3_kernel:body/3 5476 0.04 1642 [ 0.30] erl_anno:anno_info/2 8488 0.04 1654 [ 0.19] beam_dead:get_literal/1 7173 0.04 1662 [ 0.23] lists:filter/2 6898 0.04 1674 [ 0.24] v3_kernel:'-select_bin_con/1-anonymous-0-'/1 7078 0.04 1684 [ 0.24] beam_asm:encode_op/3 9419 0.04 1706 [ 0.18] beam_validator:merge_regs_1/2 7234 0.04 1709 [ 0.24] v3_codegen:need_heap_0/3 9209 0.04 1738 [ 0.19] erl_bits:apply_defaults/5 12196 0.04 1752 [ 0.14] beam_peep:peep/3 11882 0.04 1758 [ 0.15] erl_lint:pattern/5 6607 0.04 1759 [ 0.27] beam_block:collect_block/2 9090 0.04 1765 [ 0.19] erl_bits:merge_field/3 8240 0.04 1777 [ 0.22] beam_type:x_live/2 9023 0.04 1788 [ 0.20] beam_jump:move_1/3 15981 0.04 1794 [ 0.11] v3_core:cexpr/3 6511 0.04 1845 [ 0.28] v3_codegen:clear_dead_reg/3 7328 0.04 1853 [ 0.25] beam_z:undo_rename/1 8780 0.04 1867 [ 0.21] gb_trees:enter/3 9837 0.04 1913 [ 0.19] sys_core_fold:pattern_list/2 7925 0.04 1916 [ 0.24] v3_codegen:clear_dead/3 7328 0.04 1916 [ 0.26] v3_kernel:maybe_add_warning/3 8216 0.04 1916 [ 0.23] v3_codegen:sr_merge/2 5190 0.04 1924 [ 0.37] beam_type:verify_type/1 7548 0.05 1936 [ 0.26] v3_kernel:expand_pat_lit_clause/2 7078 0.05 1947 [ 0.28] gb_sets:empty/0 9285 0.05 1954 [ 0.21] beam_clean:bs_clean_saves_1/3 17192 0.05 1978 [ 0.12] beam_validator:get_term_type_1/2 7594 0.05 1981 [ 0.26] sys_core_fold:clauses/5 7919 0.05 1993 [ 0.25] core_lib:make_values/1 8600 0.05 1997 [ 0.23] sys_core_fold:guard/2 7925 0.05 2011 [ 0.25] v3_life:atomic/1 7827 0.05 2015 [ 0.26] cerl_clauses:match/2 8713 0.05 2018 [ 0.23] v3_kernel:is_atomic/1 8141 0.05 2028 [ 0.25] erl_lint:'-expr_list/3-fun-0-'/3 8606 0.05 2030 [ 0.24] beam_bs:bsm_scan/4 16932 0.05 2044 [ 0.12] v3_life:match/5 7260 0.05 2046 [ 0.28] lists:duplicate/3 18233 0.05 2051 [ 0.11] beam_type:tdb_update1/2 10602 0.05 2059 [ 0.19] beam_bs:bsm_opt_2/2 17164 0.05 2063 [ 0.12] beam_block:embed_lines/2 17184 0.05 2073 [ 0.12] v3_codegen:cg_block/5 8553 0.05 2078 [ 0.24] erl_expand_records:exprs/2 9355 0.05 2082 [ 0.22] v3_core:lit_vars/2 10591 0.05 2091 [ 0.20] v3_kernel:force_atomic/2 8141 0.05 2092 [ 0.26] v3_codegen:match_cg/4 7260 0.05 2142 [ 0.30] beam_dict:opcode/2 9420 0.05 2157 [ 0.23] sys_core_fold:clause/4 7922 0.05 2159 [ 0.27] beam_utils:live_join_label/3 8843 0.05 2159 [ 0.24] dict:on_bucket/3 6759 0.05 2165 [ 0.32] v3_core:uexpr/3 6984 0.05 2167 [ 0.31] v3_codegen:load_arg_regs/3 8450 0.05 2172 [ 0.26] beam_block:opt_alloc/1 11937 0.05 2172 [ 0.18] cerl:make_lit_list/1 11523 0.05 2186 [ 0.19] beam_opcodes:opcode/2 9420 0.05 2223 [ 0.24] v3_core:exprs/2 8974 0.05 2231 [ 0.25] sys_core_fold:update_types/3 8234 0.05 2245 [ 0.27] beam_z:undo_renames/1 9587 0.05 2256 [ 0.24] erl_anno:new/1 10503 0.05 2262 [ 0.22] lists:split_1_1/6 784 0.05 2289 [ 2.92] beam_except:translate/3 17164 0.05 2311 [ 0.13] beam_split:split_blocks/2 17151 0.05 2325 [ 0.14] gb_sets:to_list/2 14255 0.05 2327 [ 0.16] sys_core_fold:will_match/2 7919 0.05 2331 [ 0.29] beam_validator:val_dsetel/2 8744 0.05 2341 [ 0.27] ordsets:add_element/2 12009 0.06 2397 [ 0.20] erlang:phash/2 11590 0.06 2445 [ 0.21] dict:get_slot/2 8807 0.06 2466 [ 0.28] beam_type:opt_fmoves/2 16145 0.06 2469 [ 0.15] sys_core_fold:'-case_opt/3-lc$^0/1-0-'/1 10538 0.06 2496 [ 0.24] beam_jump:is_unreachable_after/1 10395 0.06 2499 [ 0.24] beam_type:opt/3 17164 0.06 2501 [ 0.15] v3_core:'-used_in_any/1-anonymous-0-'/2 10516 0.06 2504 [ 0.24] beam_dead:backward/3 18352 0.06 2504 [ 0.14] sys_core_fold:'-case_opt/3-lc$^1/1-1-'/1 10538 0.06 2513 [ 0.24] v3_life:vdb_update/3 13239 0.06 2514 [ 0.19] beam_block:blockify/2 17185 0.06 2527 [ 0.15] beam_type:flt_alloc/2 11588 0.06 2529 [ 0.22] beam_utils:live_join_labels/3 15798 0.06 2538 [ 0.16] beam_validator:valfun_1/2 8744 0.06 2581 [ 0.30] ordsets:is_element/2 17110 0.06 2583 [ 0.15] beam_jump:eliminate_fallthroughs/2 17629 0.06 2590 [ 0.15] beam_reorder:reorder_1/3 19139 0.06 2598 [ 0.14] beam_dead:forward/4 17408 0.06 2599 [ 0.15] beam_block:opt_tuple_element/1 13904 0.06 2617 [ 0.19] beam_dead:normalize_op/1 10466 0.06 2617 [ 0.25] beam_jump:share_1/4 18255 0.06 2620 [ 0.14] lists:sort/1 10629 0.06 2632 [ 0.25] v3_codegen:clear_dead_stk/3 10974 0.06 2632 [ 0.24] beam_dead:move_move_into_block/2 20253 0.06 2644 [ 0.13] cerl_clauses:match_1/3 22337 0.06 2650 [ 0.12] v3_kernel:'-new_clauses/3-anonymous-3-'/2 7078 0.06 2688 [ 0.38] beam_jump:mark_used_list/2 20512 0.06 2703 [ 0.13] beam_asm:make_op/2 9436 0.06 2719 [ 0.29] beam_type:flt_need_heap_1/4 16145 0.06 2731 [ 0.17] sys_core_fold:body/3 11466 0.06 2769 [ 0.24] beam_type:simplify_float_1/4 16145 0.07 2781 [ 0.17] ordsets:union1/2 23048 0.07 2793 [ 0.12] maps:put/3 3108 0.07 2829 [ 0.91] beam_jump:'-function/1-anonymous-0-'/2 13709 0.07 2845 [ 0.21] ordsets:union/1 11598 0.07 2880 [ 0.25] cerl:clause_guard/1 12917 0.07 2917 [ 0.23] cerl:clause_body/1 12917 0.07 2918 [ 0.23] v3_core:uexprs/3 11092 0.07 2923 [ 0.26] beam_type:flt_need_heap_2/3 12165 0.07 2975 [ 0.24] beam_asm:encode_op_1/3 25603 0.07 2979 [ 0.12] sys_core_fold:'-pattern_list/3-anonymous-0-'/3 12691 0.07 2986 [ 0.24] v3_kernel:'-select_types/2-lc$^0/1-0-'/2 23155 0.07 3010 [ 0.13] v3_core:used_in_any/1 11840 0.07 3024 [ 0.26] v3_codegen:combine/1 12070 0.07 3026 [ 0.25] cerl:update_c_clause/4 12917 0.07 3055 [ 0.24] cerl:clause_pats/1 12917 0.07 3076 [ 0.24] lists:'-filter/2-lc$^0/1-0-'/2 18808 0.07 3101 [ 0.16] gb_sets:is_member_1/2 26104 0.08 3272 [ 0.13] v3_kernel:expr/3 12421 0.08 3336 [ 0.27] gb_trees:is_defined/2 14133 0.08 3374 [ 0.24] beam_clean:bs_restores/2 29074 0.08 3431 [ 0.12] cerl:update_c_cons/3 14636 0.08 3471 [ 0.24] beam_block:opt/1 13904 0.08 3503 [ 0.25] cerl:data_es/1 14418 0.08 3519 [ 0.24] beam_utils:live_regs_1/2 19169 0.08 3529 [ 0.18] erl_lint:expr/3 14519 0.08 3539 [ 0.24] cerl:cons_tl/1 14704 0.08 3552 [ 0.24] erl_expand_records:index_expr/3 27160 0.08 3572 [ 0.13] dict:append_bkt/3 18136 0.09 3668 [ 0.20] cerl:cons_hd/1 14704 0.09 3708 [ 0.25] beam_clean:'-replace/3-anonymous-0-'/2 14970 0.09 3744 [ 0.25] sys_core_fold:'-case_opt_args/5-lc$^0/1-0-'/1 15391 0.09 3744 [ 0.24] erl_expand_records:expr_list/2 16454 0.09 3792 [ 0.23] beam_a:rename_instr/1 16432 0.09 3835 [ 0.23] v3_kernel:umatch/3 14008 0.09 3857 [ 0.28] beam_block:opt_blocks/1 17164 0.09 3894 [ 0.23] erl_lint:vtupdate/2 15587 0.09 3903 [ 0.25] v3_core:safe/2 16839 0.09 3912 [ 0.23] beam_utils:code_at/2 16987 0.09 3944 [ 0.23] beam_bs:bs_put_opt/1 17062 0.09 3959 [ 0.23] beam_jump:is_exit_instruction/1 20021 0.09 3960 [ 0.20] beam_type:simplify_basic/2 16996 0.09 4009 [ 0.24] erl_expand_records:record_pattern/6 18108 0.10 4118 [ 0.23] sys_core_fold:letify/2 17544 0.10 4128 [ 0.24] v3_core:is_safe/1 18011 0.10 4129 [ 0.23] beam_block:move_allocates/1 17164 0.10 4155 [ 0.24] dict:fold_seg/4 32606 0.10 4210 [ 0.13] v3_kernel:select/2 18945 0.10 4283 [ 0.23] gb_trees:insert/3 18006 0.10 4348 [ 0.24] v3_core:force_safe/2 18011 0.10 4349 [ 0.24] sys_core_dsetel:visit/2 20233 0.11 4504 [ 0.22] cerl:lit_list_vals/1 20782 0.11 4544 [ 0.22] v3_kernel:new_vars/3 38405 0.11 4548 [ 0.12] beam_clean:label/2 17403 0.11 4561 [ 0.26] beam_a:rename_instrs/1 18150 0.11 4732 [ 0.26] maps:find/2 18432 0.11 4797 [ 0.26] v3_core:full_anno/2 19710 0.11 4801 [ 0.24] v3_codegen:'-clear_dead_reg/3-lc$^0/1-0-'/3 20503 0.11 4908 [ 0.24] cerl:is_lit_list/1 30858 0.12 5011 [ 0.16] beam_asm:encode/2 21230 0.12 5175 [ 0.24] v3_codegen:new_label/1 6214 0.12 5257 [ 0.85] v3_kernel:'-pattern_list/4-anonymous-0-'/3 45126 0.12 5279 [ 0.12] beam_utils:x_dead/2 28264 0.12 5286 [ 0.19] v3_kernel:'-match/4-anonymous-0-'/3 41079 0.13 5362 [ 0.13] gb_sets:insert_1/3 21011 0.13 5416 [ 0.26] v3_codegen:find_reg/2 12158 0.13 5458 [ 0.45] v3_kernel:match_var/4 38974 0.13 5487 [ 0.14] v3_kernel:match_varcon/4 41079 0.13 5493 [ 0.13] beam_jump:opt/3 39214 0.13 5560 [ 0.14] lists:member/2 36061 0.13 5567 [ 0.15] erl_lint:vtmerge_pat/2 23190 0.13 5609 [ 0.24] beam_utils:live_opt_block/4 29834 0.13 5621 [ 0.19] v3_codegen:'-unsaved_registers/5-lc$^0/1-0-'/5 49016 0.13 5695 [ 0.12] gb_trees:get/2 33999 0.13 5701 [ 0.17] erl_anno:to_term/1 27518 0.14 5803 [ 0.21] sys_core_dsetel:restore_vars/3 44018 0.14 5816 [ 0.13] v3_core:safe_list/2 4203 0.14 5830 [ 1.39] cerl:get_ann/1 24725 0.14 5839 [ 0.24] beam_block:collect/1 26107 0.14 5919 [ 0.23] beam_asm:encode_arg/2 21180 0.14 5922 [ 0.28] beam_type:simplify_basic_1/3 42427 0.14 6082 [ 0.14] beam_clean:replace/3 41489 0.14 6110 [ 0.15] erl_parse:'-anno_to_term/1-fun-0-'/2 27518 0.14 6163 [ 0.22] gb_trees:count/1 27728 0.15 6294 [ 0.23] erlang:max/2 24717 0.15 6340 [ 0.26] orddict:merge/3 49589 0.15 6357 [ 0.13] v3_core:expr/2 22900 0.15 6396 [ 0.28] sys_core_dsetel:visit_pats/3 50734 0.15 6451 [ 0.13] sys_core_fold:expr/3 33651 0.15 6468 [ 0.19] v3_kernel:match/4 44330 0.15 6474 [ 0.15] v3_codegen:'-save_stack/4-lc$^0/1-0-'/4 52503 0.16 6636 [ 0.13] lists:splitwith/3 48334 0.16 6742 [ 0.14] beam_clean:renumber_labels/3 42525 0.16 6750 [ 0.16] erl_expand_records:expr/2 23874 0.16 6796 [ 0.28] beam_jump:label_used/2 36676 0.16 6855 [ 0.19] gb_trees:lookup/2 30683 0.16 6975 [ 0.23] erl_lint:find_field/2 54324 0.16 7051 [ 0.13] v3_life:use_vars/3 24118 0.17 7110 [ 0.29] gb_trees:update_1/3 29635 0.17 7152 [ 0.24] dict:fold_bucket/3 35281 0.17 7284 [ 0.21] beam_utils:live_opt/4 49967 0.17 7339 [ 0.15] v3_core:new_in_any/1 10126 0.17 7483 [ 0.74] beam_jump:rem_unused/3 62921 0.18 7561 [ 0.12] ordsets:subtract/2 51321 0.18 7865 [ 0.15] lists:reverse/2 30707 0.19 8060 [ 0.26] cerl:is_data/1 34107 0.19 8117 [ 0.24] v3_life:'-literal/2-lc$^0/1-0-'/2 37431 0.19 8199 [ 0.22] beam_utils:index_labels_1/2 63625 0.20 8598 [ 0.14] v3_kernel:'-pat_list_vars/1-anonymous-0-'/2 37504 0.21 8838 [ 0.24] v3_codegen:'-select_extract_tuple/6-anonymous-0-'/5 37421 0.21 8998 [ 0.24] maps:remove/2 39703 0.21 9122 [ 0.23] beam_type:update/2 36624 0.21 9165 [ 0.25] v3_kernel:'-match_var/4-anonymous-1-'/3 39273 0.22 9426 [ 0.24] erlang:term_to_binary/2 330 0.23 9637 [ 29.20] v3_kernel:new_var_name/1 39420 0.23 9699 [ 0.25] gb_trees:is_defined_1/2 86526 0.23 9919 [ 0.11] v3_core:new_var_name/1 38685 0.24 10131 [ 0.26] beam_jump:'-remove_unused_labels/1-anonymous-0-'/2 48838 0.24 10148 [ 0.21] beam_utils:x_live/2 53759 0.24 10161 [ 0.19] beam_jump:ulbl/2 70747 0.24 10218 [ 0.14] v3_kernel:'-match_var/4-anonymous-0-'/3 39273 0.24 10237 [ 0.26] lists:splitwith/2 41778 0.24 10449 [ 0.25] v3_kernel:'-select/2-lc$^0/1-0-'/2 82647 0.25 10487 [ 0.13] orddict:from_list/1 78652 0.25 10596 [ 0.13] v3_kernel:pat_vars/1 42105 0.25 10645 [ 0.25] v3_codegen:flatmapfoldl/3 47583 0.27 11351 [ 0.24] erl_expand_records:pattern_list/2 51056 0.27 11369 [ 0.22] sys_core_bsm:'-function/3-anonymous-0-'/2 105235 0.27 11450 [ 0.11] v3_core:pattern_list/2 51110 0.27 11568 [ 0.23] erl_expand_records:pattern/2 46427 0.27 11677 [ 0.25] sys_core_bsm:bsm_an/2 105235 0.28 12181 [ 0.12] lists:ukeysort/2 77382 0.29 12218 [ 0.16] sys_core_fold:pattern/3 91530 0.29 12271 [ 0.13] v3_core:pattern/2 47003 0.29 12363 [ 0.26] v3_core:upattern/3 46333 0.29 12508 [ 0.27] v3_life:literal/2 44636 0.29 12582 [ 0.28] cerl:concrete/1 54877 0.29 12608 [ 0.23] v3_kernel:'-match_var/4-anonymous-2-'/2 39273 0.30 12847 [ 0.33] lists:keyfind/3 71791 0.30 12920 [ 0.18] v3_kernel:pattern/4 45769 0.30 12934 [ 0.28] beam_utils:delete_live_annos/1 63039 0.31 13115 [ 0.21] erlang:integer_to_list/1 79068 0.31 13136 [ 0.17] v3_core:upattern_list/3 51908 0.31 13167 [ 0.25] orddict:find/2 89347 0.34 14356 [ 0.16] v3_life:vdb_find/2 49254 0.34 14473 [ 0.29] gb_trees:to_list/2 94502 0.36 15491 [ 0.16] v3_core:lineno_anno/2 74513 0.37 15670 [ 0.21] erl_anno:location/1 74533 0.39 16540 [ 0.22] cerl_trees:mapfold_list/4 71716 0.39 16699 [ 0.23] erl_anno:line/1 74513 0.40 16895 [ 0.23] erl_anno:generated/1 74513 0.40 16986 [ 0.23] sys_core_fold:'-pattern/3-anonymous-2-'/3 76384 0.41 17600 [ 0.23] sys_core_dsetel:visit_pat/3 46419 0.42 17861 [ 0.38] gb_trees:balance_list_1/2 70256 0.43 18286 [ 0.26] sys_core_fold:sub_del_var/2 79753 0.45 19286 [ 0.24] sys_core_fold:sub_is_val/2 79753 0.45 19455 [ 0.24] v3_kernel:partition/1 81267 0.47 20263 [ 0.25] ordsets:intersection/2 144532 0.48 20342 [ 0.14] v3_kernel:is_var_clause/1 88321 0.52 22083 [ 0.25] cerl_clauses:match_list/3 176966 0.54 23136 [ 0.13] sys_core_bsm:bsm_an/1 105235 0.55 23434 [ 0.22] erlang:list_to_atom/1 79357 0.59 25074 [ 0.32] cerl_trees:'-mapfold/3-anonymous-0-'/2 105235 0.59 25195 [ 0.24] erl_parse:modify_anno1/3 99993 0.60 25693 [ 0.26] lists:map/2 110970 0.62 26584 [ 0.24] gb_trees:lookup_1/2 230216 0.64 27218 [ 0.12] lists:reverse/1 122044 0.65 27622 [ 0.23] cerl_trees:mapfold/4 105235 0.65 27889 [ 0.27] gb_trees:get_1/2 247600 0.66 28191 [ 0.11] cerl_clauses:'-match_1/3-lc$^0/1-0-'/1 150164 0.78 33173 [ 0.22] lists:mapfoldl/3 142434 0.78 33411 [ 0.23] gb_trees:insert_1/4 145420 0.82 35231 [ 0.24] v3_kernel:arg_con/1 160017 0.87 37325 [ 0.23] cerl_clauses:match/3 167587 0.88 37476 [ 0.22] lists:foldr/3 199608 0.98 41728 [ 0.21] erlang:'++'/2 337382 1.03 43981 [ 0.13] cerl_sets:is_element/2 219320 1.27 54228 [ 0.25] cerl_sets:add_element/2 158421 1.37 58422 [ 0.37] lists:foldl/3 384147 1.44 61755 [ 0.16] cerl:type/1 273982 1.55 66363 [ 0.24] erlang:setelement/3 409336 1.67 71545 [ 0.17] v3_life:vdb_update_vars/3 319576 1.69 72308 [ 0.23] v3_kernel:subst_vsub_2/3 343932 1.83 78060 [ 0.23] orddict:reverse_pairs/2 2497074 6.76 289258 [ 0.12] lists:ukeysplit_2/5 2336493 7.63 326450 [ 0.14] ordsets:union/2 1692288 8.93 381951 [ 0.23] v3_kernel:subst_vsub_1/3 2152994 11.93 510068 [ 0.24] ---------------------------------------------------------- -------- ------- ------- [----------] Total: 22253284 100.00% 4276149 [ 0.19] --- lib/diameter/test/diameter_util.erl | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/diameter/test/diameter_util.erl b/lib/diameter/test/diameter_util.erl index 03f79096ac..d249b0e4fa 100644 --- a/lib/diameter/test/diameter_util.erl +++ b/lib/diameter/test/diameter_util.erl @@ -32,7 +32,8 @@ foldl/3, scramble/1, unique_string/0, - have_sctp/0]). + have_sctp/0, + eprof/1]). %% diameter-specific -export([lport/2, @@ -48,6 +49,16 @@ -define(L, atom_to_list). +%% --------------------------------------------------------------------------- + +eprof(start) -> + eprof:start(), + eprof:start_profiling([self()]); + +eprof(stop) -> + eprof:stop_profiling(), + eprof:analyze(), + eprof:stop(). %% --------------------------------------------------------------------------- %% name/2 -- cgit v1.2.3 From 1339269fefcbd688b3eb4bde2bb6c55fe2e1b17d Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Wed, 19 Jul 2017 14:48:12 +0200 Subject: Increase init_per_group timetrap in traffic suite Connection establishment is slow on some test hosts. --- lib/diameter/test/diameter_traffic_SUITE.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index d676614084..eb2fdb7721 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -30,6 +30,7 @@ init_per_suite/0, init_per_suite/1, end_per_suite/1, + init_per_group/1, init_per_group/2, end_per_group/2, init_per_testcase/2, @@ -314,6 +315,9 @@ end_per_suite(_Config) -> %% -------------------- +init_per_group(_) -> + [{timetrap, {seconds, 30}}]. + init_per_group(Name, Config) when Name == shuffle; Name == parallel -> -- cgit v1.2.3 From fa233bb7bc4f37632166c468a0381e695433c318 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Wed, 12 Jul 2017 11:43:06 +0200 Subject: Limit SCTP testing in traffic suite All SCTP testing on Solaris/Sparc was disabled in commit 69c5a741, but there are issues on other platforms as well, resulting in failures in diameter_gen_sctp_SUITE and more. The problems seem to be load-related, when testcases are run in parallel, so only run a smallish subset sequentially until the gen_sctp suite runs without error. --- lib/diameter/test/diameter_traffic_SUITE.erl | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index eb2fdb7721..f05c850513 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -125,7 +125,7 @@ %% Positive number of testcases from which to select (randomly) from %% tc(), the list of testcases to run, or [] to run all. The random %% selection is to limit the time it takes for the suite to run. --define(LIMIT, 42). +-define(LIMIT, #{tcp => 42, sctp => 5}). -define(util, diameter_util). @@ -269,12 +269,15 @@ all() -> -define(GROUPS, []). %-define(GROUPS, [[tcp,rfc6733,record,map,false,false,false,false]]). +%% Issues with gen_sctp sporadically cause huge numbers of failed +%% testcases when running testcases in parallel. groups() -> Names = names(), [{P, [P], Ts} || Ts <- [tc()], P <- [shuffle, parallel]] ++ - [{?util:name(N), [], [{group, if S -> shuffle; not S -> parallel end}]} - || [_,_,_,_,S|_] = N <- Names] + [{?util:name(N), [], [{group, if T == sctp; S -> shuffle; + true -> parallel end}]} + || [T,_,_,_,S|_] = N <- Names] ++ [{T, [], [{group, ?util:name(N)} || N <- names(Names, ?GROUPS), T == hd(N)]} @@ -348,7 +351,7 @@ init_per_group(Name, Config) -> server_decoding = D, server_sender = SS, server_throttle = ST}, - [{group, G}, {runlist, select()} | Config]; + [{group, G}, {runlist, select(T)} | Config]; _ -> Config end. @@ -362,9 +365,10 @@ end_per_group(Name, Config) end_per_group(_, _) -> ok. -select() -> - try rand:uniform(?LIMIT) of - N -> lists:sublist(?util:scramble(tc()), max(N,5)) +select(T) -> + try maps:get(T, ?LIMIT) of + N -> + lists:sublist(?util:scramble(tc()), max(5, rand:uniform(N))) catch error:_ -> ?LIMIT end. -- cgit v1.2.3 From fbc0fe7de4af5d24bd5ccd3eaf2417fbb436da27 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 10 Aug 2017 12:01:38 +0200 Subject: Randomly disable traffic counters in traffic suite To exercise the new traffic_counters option. --- lib/diameter/test/diameter_traffic_SUITE.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 1760d7c5dc..4e70a960c5 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -460,12 +460,17 @@ start_services(Config) -> server_decoding = SD} = Grp = group(Config), - ok = diameter:start_service(SN, [{decode_format, SD} + ok = diameter:start_service(SN, [{traffic_counters, bool()}, + {decode_format, SD} | ?SERVICE(SN, Grp)]), - ok = diameter:start_service(CN, [{sequence, ?CLIENT_MASK}, + ok = diameter:start_service(CN, [{traffic_counters, bool()}, + {sequence, ?CLIENT_MASK}, {strict_arities, decode} | ?SERVICE(CN, Grp)]). +bool() -> + 0.5 =< rand:uniform(). + add_transports(Config) -> #group{transport = T, encoding = E, -- cgit v1.2.3 From a73254692913e689aedf1154c0ee378546502501 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Fri, 11 Aug 2017 01:46:30 +0200 Subject: Randomly skip groups in traffic suite To limit the number of testcases being run. --- lib/diameter/test/diameter_traffic_SUITE.erl | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 4e70a960c5..fde7e9b8c2 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -121,6 +121,9 @@ %% =========================================================================== +%% Fraction of shuffle/parallel groups to randomly skip. +-define(SKIP, 0.25). + %% Positive number of testcases from which to select (randomly) from %% tc(), the list of testcases to run, or [] to run all. The random %% selection is to limit the time it takes for the suite to run. @@ -316,9 +319,14 @@ end_per_suite(_Config) -> init_per_group(Name, Config) when Name == shuffle; Name == parallel -> - start_services(Config), - add_transports(Config), - [{sleep, Name == parallel} | Config]; + case rand:uniform() < ?SKIP of + true -> + {skip, random}; + false -> + start_services(Config), + add_transports(Config), + [{sleep, Name == parallel} | Config] + end; init_per_group(sctp = Name, Config) -> {_, Sctp} = lists:keyfind(Name, 1, Config), -- cgit v1.2.3 From d8d3c2a36045bcbe2ae5fd8f08b30a5d103f7bac Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 17 Aug 2017 00:53:36 +0200 Subject: Work around unexpected common_test behaviour diameter_traffic_SUITE has four layers of nested groups, so when a testcase is run it should get Config from four init_per_group plus one init_per_testcase. This isn't what happens: Config is being accumulated from several init_per_group in some manner that isn't clear, and the skip in the parent commit somehow results in growing test_server_gl:init/1 processes that eventually consume all memory. Replacing rather than prepending to Config in init_per_group works around this, but the common_test behaviour seems wrong. --- lib/diameter/test/diameter_traffic_SUITE.erl | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index fde7e9b8c2..a2c0f7fae6 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -325,7 +325,7 @@ init_per_group(Name, Config) false -> start_services(Config), add_transports(Config), - [{sleep, Name == parallel} | Config] + replace({sleep, Name == parallel}, Config) end; init_per_group(sctp = Name, Config) -> @@ -352,7 +352,7 @@ init_per_group(Name, Config) -> server_decoding = D, server_sender = SS, server_throttle = ST}, - [{group, G}, {runlist, select()} | Config]; + replace([{group, G}, {runlist, select()}], Config); _ -> Config end. @@ -396,6 +396,15 @@ init_per_testcase(Name, Config) -> end_per_testcase(_, _) -> ok. +%% replace/2 + +replace(Pairs, Config) + when is_list(Pairs) -> + lists:foldl(fun replace/2, Config, Pairs); + +replace({Key, _} = T, Config) -> + [T | lists:keydelete(Key, 1, Config)]. + %% -------------------- %% Testcases to run when services are started and connections -- cgit v1.2.3