mirror of
https://github.com/valitydev/woody_erlang.git
synced 2024-11-06 02:15:19 +00:00
Implement binary-first event formatter (#122)
* Add compex term formatting microbenchmark * Format FP numbers more neatly * Provide fast-path verbatim term formatter * Add memory pressure benchmark runner * Switch to rebar3_bench 0.2.1 package * Employ assert macros throughout main ct suite
This commit is contained in:
parent
f74bb6854c
commit
e615c85d70
2
Jenkinsfile
vendored
2
Jenkinsfile
vendored
@ -33,7 +33,7 @@ build('woody_erlang', 'docker-host', finalHook) {
|
||||
}
|
||||
runStage('dialyze') {
|
||||
withGithubPrivkey {
|
||||
withWsCache("_build/test/rebar3_21.3.8.4_plt") {
|
||||
withWsCache("_build/test/rebar3_21.3.8.7_plt") {
|
||||
sh 'make wc_dialyze'
|
||||
}
|
||||
}
|
||||
|
15
Makefile
15
Makefile
@ -7,9 +7,9 @@ UTILS_PATH := build_utils
|
||||
# with handling of the varriable in build_utils is fixed
|
||||
TEMPLATES_PATH := .
|
||||
SERVICE_NAME := woody
|
||||
BUILD_IMAGE_TAG := cd38c35976f3684fe7552533b6175a4c3460e88b
|
||||
BUILD_IMAGE_TAG := 4536c31941b9c27c134e8daf0fd18848809219c9
|
||||
|
||||
CALL_W_CONTAINER := all submodules rebar-update compile xref lint test dialyze clean distclean
|
||||
CALL_W_CONTAINER := all submodules compile xref lint test bench dialyze clean distclean
|
||||
|
||||
.PHONY: $(CALL_W_CONTAINER)
|
||||
|
||||
@ -23,10 +23,7 @@ $(SUBTARGETS): %/.git: %
|
||||
|
||||
submodules: $(SUBTARGETS)
|
||||
|
||||
rebar-update:
|
||||
$(REBAR) update
|
||||
|
||||
compile: submodules rebar-update
|
||||
compile: submodules
|
||||
$(REBAR) compile
|
||||
|
||||
test: submodules
|
||||
@ -54,4 +51,8 @@ dialyze:
|
||||
$(REBAR) as test dialyzer
|
||||
|
||||
bench:
|
||||
$(REBAR) as test bench -n 1000
|
||||
$(REBAR) as test bench -m bench_woody_event_handler -n 1000
|
||||
$(REBAR) as test bench -m bench_woody_formatter -n 10
|
||||
erl -pa _build/test/lib/*/ebin _build/test/lib/woody/test -noshell \
|
||||
-s benchmark_memory_pressure run \
|
||||
-s erlang halt
|
||||
|
@ -22,11 +22,10 @@
|
||||
{elvis_style, dont_repeat_yourself, #{min_complexity => 17, ignore => [woody_test_thrift, woody_tests_SUITE]}},
|
||||
{elvis_style, no_debug_call, #{
|
||||
ignore => [
|
||||
elvis,
|
||||
elvis_utils,
|
||||
woody_ssl_SUITE,
|
||||
woody_tests_SUITE,
|
||||
woody_transport_opts_SUITE
|
||||
woody_transport_opts_SUITE,
|
||||
benchmark_memory_pressure
|
||||
]
|
||||
}}
|
||||
]
|
||||
|
@ -51,7 +51,7 @@
|
||||
{profiles, [
|
||||
{test, [
|
||||
{plugins, [
|
||||
{rebar3_bench, "0.2.0"}
|
||||
{rebar3_bench, "0.2.1"}
|
||||
]},
|
||||
{cover_enabled, true},
|
||||
{provider_hooks, [
|
||||
@ -72,7 +72,7 @@
|
||||
{how_are_you, {git, "https://github.com/rbkmoney/how_are_you.git", {ref, "2bb4605"}}},
|
||||
{damsel , {git, "git@github.com:rbkmoney/damsel.git", {ref, "8911ac3"}}},
|
||||
{mg_proto , {git, "git@github.com:rbkmoney/machinegun_proto.git", {ref, "ebae56f"}}}
|
||||
]},
|
||||
]},
|
||||
{dialyzer, [
|
||||
{plt_extra_apps, [how_are_you, eunit]}
|
||||
]}
|
||||
|
@ -1,7 +1,6 @@
|
||||
-module(woody_event_formatter).
|
||||
|
||||
-export([
|
||||
format_call/4,
|
||||
format_call/5,
|
||||
format_reply/5,
|
||||
format_exception/5,
|
||||
@ -9,85 +8,68 @@
|
||||
]).
|
||||
|
||||
-define(MAX_PRINTABLE_LIST_LENGTH, 3).
|
||||
-define(MAX_FLOAT_DECIMALS, 8).
|
||||
%% Binaries under size below will log as-is.
|
||||
-define(MAX_BIN_SIZE, 40).
|
||||
|
||||
-type limit() :: non_neg_integer() | unlimited.
|
||||
|
||||
-type opts():: #{
|
||||
max_depth => integer(),
|
||||
max_length => integer(),
|
||||
max_depth => limit(),
|
||||
max_length => limit(),
|
||||
max_pritable_string_length => non_neg_integer()
|
||||
}.
|
||||
|
||||
-export_type([opts/0]).
|
||||
|
||||
-spec format_call(atom(), atom(), atom(), term()) ->
|
||||
woody_event_handler:msg().
|
||||
format_call(Module, Service, Function, Arguments) ->
|
||||
ConfigOpts = genlib_app:env(woody, event_formatter_options, #{}),
|
||||
format_call(Module, Service, Function, Arguments, ConfigOpts).
|
||||
|
||||
-spec format_call(atom(), atom(), atom(), term(), opts()) ->
|
||||
woody_event_handler:msg().
|
||||
format_call(Module, Service, Function, Arguments, Opts) ->
|
||||
case Module:function_info(Service, Function, params_type) of
|
||||
Opts1 = normalize_options(Opts),
|
||||
ServiceName = to_string(Service),
|
||||
FunctionName = to_string(Function),
|
||||
Result0 = <<ServiceName/binary, ":", FunctionName/binary, "(">>,
|
||||
Result = try Module:function_info(Service, Function, params_type) of
|
||||
{struct, struct, ArgTypes} ->
|
||||
Opts1 = normalize_options(Opts),
|
||||
ServiceName = to_string(Service),
|
||||
ServiceLength = length(ServiceName),
|
||||
FunctionName = to_string(Function),
|
||||
FunctionLength = length(FunctionName),
|
||||
NewCL = ServiceLength + FunctionLength + 3,
|
||||
{Result, _} =
|
||||
format_call_(ArgTypes, Arguments, "", 0, NewCL, Opts1, false),
|
||||
{"~s:~s(~ts)", [ServiceName, FunctionName, Result]};
|
||||
_Other ->
|
||||
{"~s:~s(~0tp)", [Service, Function, Arguments]}
|
||||
end.
|
||||
ML = maps:get(max_length, Opts1),
|
||||
MD = maps:get(max_depth, Opts1),
|
||||
MPSL = maps:get(max_pritable_string_length, Opts1),
|
||||
Result1 = format_call_(ArgTypes, Arguments, Result0, MD, dec(ML), MPSL, false),
|
||||
<<Result1/binary, ")">>
|
||||
catch error:badarg ->
|
||||
Result1 = format_verbatim(Arguments, Result0, Opts1),
|
||||
<<Result1/binary, ")">>
|
||||
end,
|
||||
{"~ts", [Result]}.
|
||||
|
||||
format_call_([], [], Result, _CurDepth, CL, _Opts, _AddDelimiter) ->
|
||||
{Result, CL};
|
||||
format_call_(_, ArgumentList, Result, _CurDepth, CL, #{max_length := ML}, AddDelimiter) when ML >= 0, CL > ML ->
|
||||
HasMoreArguments = AddDelimiter and ArgumentList =/= [],
|
||||
Delimiter = maybe_add_delimiter(HasMoreArguments),
|
||||
DelimiterLen = length(Delimiter),
|
||||
MoreArguments = maybe_add_more_marker(HasMoreArguments),
|
||||
MoreArgumentsLen = length(MoreArguments),
|
||||
{[Result | [Delimiter, MoreArguments]], CL + DelimiterLen + MoreArgumentsLen};
|
||||
format_call_([Type | RestType], [Argument | RestArgs], Acc, CurDepth, CL, Opts, AddDelimiter) ->
|
||||
Delimiter = maybe_add_delimiter(AddDelimiter),
|
||||
DelimiterLen = length(Delimiter),
|
||||
case format_argument(Type, Argument, CurDepth, CL + DelimiterLen, Opts) of
|
||||
{undefined, NewCL} ->
|
||||
format_call_(
|
||||
RestType,
|
||||
RestArgs,
|
||||
Acc,
|
||||
CurDepth,
|
||||
NewCL,
|
||||
Opts,
|
||||
AddDelimiter
|
||||
);
|
||||
{Result, NewCL} ->
|
||||
format_call_(
|
||||
RestType,
|
||||
RestArgs,
|
||||
[Acc | [Delimiter, Result]],
|
||||
CurDepth,
|
||||
NewCL + DelimiterLen,
|
||||
Opts,
|
||||
true
|
||||
)
|
||||
end.
|
||||
format_call_([], [], Result, _MD, _ML, _MPSL, _AD) ->
|
||||
Result;
|
||||
format_call_(_, ArgumentList, Result0, _MD, ML, _MPSL, AD) when byte_size(Result0) > ML ->
|
||||
HasMoreArguments = AD and (ArgumentList =/= []),
|
||||
Result1 = maybe_add_delimiter(HasMoreArguments, Result0),
|
||||
Result2 = maybe_add_more_marker(HasMoreArguments, Result1),
|
||||
Result2;
|
||||
format_call_([Type | RestType], [Argument | RestArgs], Result0, MD, ML, MPSL, AD) ->
|
||||
{Result1, AD1} = format_argument(Type, Argument, Result0, MD, ML, MPSL, AD),
|
||||
format_call_(
|
||||
RestType,
|
||||
RestArgs,
|
||||
Result1,
|
||||
MD,
|
||||
ML,
|
||||
MPSL,
|
||||
AD1
|
||||
).
|
||||
|
||||
format_argument({_Fid, _Required, _Type, _Name, undefined}, undefined, _CurDepth, CL, _Opts) ->
|
||||
{undefined, CL};
|
||||
format_argument({Fid, Required, Type, Name, Default}, undefined, CurDepth, CL, Opts) ->
|
||||
format_argument({Fid, Required, Type, Name, Default}, Default, CurDepth, CL, Opts);
|
||||
format_argument({_Fid, _Required, Type, Name, _Default}, Value, CurDepth, CL, Opts) ->
|
||||
NameStr = to_string(Name),
|
||||
NameStrLen = length(NameStr),
|
||||
{Format, NewCL} = format_thrift_value(Type, Value, CurDepth, CL + NameStrLen + 3, Opts),
|
||||
{[NameStr, "=", Format], NewCL}.
|
||||
format_argument({_Fid, _Required, _Type, _Name, undefined}, undefined, Result, _MD, _ML, _MPSL, AD) ->
|
||||
{Result, AD};
|
||||
format_argument({Fid, Required, Type, Name, Default}, undefined, Result, MD, ML, MPSL, AD) ->
|
||||
format_argument({Fid, Required, Type, Name, Default}, Default, Result, MD, ML, MPSL, AD);
|
||||
format_argument({_Fid, _Required, Type, Name, _Default}, Value, Result0, MD, ML, MPSL, AD) ->
|
||||
NameStr = atom_to_binary(Name, unicode),
|
||||
Result1 = maybe_add_delimiter(AD, Result0),
|
||||
Result2 = <<Result1/binary, NameStr/binary, "=">>,
|
||||
{format_thrift_value(Type, Value, Result2, MD, ML, MPSL), true}.
|
||||
|
||||
-spec format_reply(atom(), atom(), atom(), term(), woody:options()) ->
|
||||
woody_event_handler:msg().
|
||||
@ -103,125 +85,99 @@ format_exception(Module, Service, Function, Value, Opts) when is_tuple(Value) ->
|
||||
ReplyType = get_exception_type(Exception, ExceptionTypeList),
|
||||
format(ReplyType, Value, normalize_options(Opts));
|
||||
format_exception(_Module, _Service, _Function, Value, Opts) ->
|
||||
#{max_length := ML} = normalize_options(Opts),
|
||||
{"~ts", [io_lib:format("~0tp", [Value], [{chars_limit, ML}])]}.
|
||||
{"~ts", [format_verbatim(Value, <<>>, normalize_options(Opts))]}.
|
||||
|
||||
format(ReplyType, Value, #{max_length := ML} = Opts) ->
|
||||
format(ReplyType, Value, #{max_length := ML, max_depth := MD, max_pritable_string_length := MPSL}) ->
|
||||
try
|
||||
{ReplyFmt, _} = format_thrift_value(ReplyType, Value, 0, 0, Opts),
|
||||
{"~ts", [ReplyFmt]}
|
||||
ReplyValue = format_thrift_value(ReplyType, Value, <<>>, MD, ML, MPSL),
|
||||
{"~ts", [ReplyValue]}
|
||||
catch
|
||||
E:R:S ->
|
||||
WarningDetails = genlib_format:format_exception({E, R, S}),
|
||||
logger:warning("EVENT FORMATTER ERROR: ~tp", [WarningDetails]),
|
||||
{"~ts", [io_lib:format("~0tp", [Value], [{chars_limit, ML}])]}
|
||||
Details = genlib_format:format_exception({E, R, S}),
|
||||
_ = logger:warning("EVENT FORMATTER ERROR: ~ts", [Details]),
|
||||
{"~ts", [format_verbatim(Value, <<>>, MD, ML)]}
|
||||
end.
|
||||
|
||||
-spec format_thrift_value(term(), term(), non_neg_integer(), non_neg_integer(), opts()) ->
|
||||
{iolist(), non_neg_integer()}.
|
||||
format_thrift_value({struct, struct, []}, Value, _CurDepth, CL, Opts) ->
|
||||
-spec format_thrift_value(term(), term(), binary(), limit(), limit(), non_neg_integer()) ->
|
||||
binary().
|
||||
format_thrift_value(Type, Value, Result, MD, ML, MPSL) ->
|
||||
try format_thrift_value_(Type, Value, Result, MD, ML, MPSL) catch
|
||||
error:_ -> format_verbatim(Value, Result, MD, ML)
|
||||
end.
|
||||
|
||||
format_thrift_value_({struct, struct, []}, Value, Result, MD, ML, _MPSL) ->
|
||||
%% {struct,struct,[]} === thrift's void
|
||||
%% so just return Value
|
||||
#{max_length := ML} = normalize_options(Opts),
|
||||
ValueString = io_lib:format("~0tp", [Value], [{chars_limit, ML}]),
|
||||
Length = length(ValueString),
|
||||
{ValueString, CL + Length};
|
||||
format_thrift_value({struct, struct, {Module, Struct}}, Value, CurDepth, CL, Opts) ->
|
||||
format_struct(Module, Struct, Value, CurDepth + 1, CL, Opts);
|
||||
format_thrift_value({struct, union, {Module, Struct}}, Value, CurDepth, CL, Opts) ->
|
||||
format_union(Module, Struct, Value, CurDepth + 1, CL, Opts);
|
||||
format_thrift_value({struct, exception, {Module, Struct}}, Value, CurDepth, CL, Opts) ->
|
||||
format_struct(Module, Struct, Value, CurDepth + 1, CL, Opts);
|
||||
format_thrift_value({enum, {_Module, _Struct}}, Value, _CurDepth, CL, _Opts) ->
|
||||
format_verbatim(Value, Result, MD, ML);
|
||||
format_thrift_value_({struct, struct, {Module, Struct}}, Value, Result, MD, ML, MPSL) ->
|
||||
format_struct(Module, Struct, Value, Result, dec(MD), ML, MPSL);
|
||||
format_thrift_value_({struct, union, {Module, Struct}}, Value, Result, MD, ML, MPSL) ->
|
||||
format_union(Module, Struct, Value, Result, dec(MD), ML, MPSL);
|
||||
format_thrift_value_({struct, exception, {Module, Struct}}, Value, Result, MD, ML, MPSL) ->
|
||||
format_struct(Module, Struct, Value, Result, dec(MD), ML, MPSL);
|
||||
format_thrift_value_({enum, {_Module, _Struct}}, Value, Result, _MD, _ML, _MPSL) ->
|
||||
ValueString = to_string(Value),
|
||||
Length = length(ValueString),
|
||||
{ValueString, CL + Length};
|
||||
format_thrift_value(string, Value, _CurDepth, CL, Opts) when is_binary(Value) ->
|
||||
case is_printable(Value, Opts) of
|
||||
{ok, Slice} ->
|
||||
ValueString = value_to_string(Slice),
|
||||
Length = length(ValueString),
|
||||
{["'", ValueString, "'"], CL + Length + 2}; %% 2 = length("''")
|
||||
{error, not_printable} ->
|
||||
Fmt = format_non_printable_string(Value),
|
||||
Length = length(Fmt),
|
||||
{Fmt, CL + Length}
|
||||
<<Result/binary, ValueString/binary>>;
|
||||
format_thrift_value_(string, Value, Result0, _MD, ML, MPSL) when is_binary(Value) ->
|
||||
case is_printable(Value, ML, byte_size(Result0), MPSL) of
|
||||
Slice when is_binary(Slice) ->
|
||||
Result1 = escape_printable_string(Slice, <<Result0/binary, "'">>),
|
||||
case byte_size(Slice) == byte_size(Value) of
|
||||
true -> <<Result1/binary, "'">>;
|
||||
false -> <<Result1/binary, "...'">>
|
||||
end;
|
||||
not_printable ->
|
||||
format_non_printable_string(Value, Result0)
|
||||
end;
|
||||
format_thrift_value(string, Value, _CurDepth, CL, _Opts) ->
|
||||
ValueString = value_to_string(Value),
|
||||
Length = length(ValueString),
|
||||
{["'", ValueString, "'"], CL + Length + 2}; %% 2 = length("''")
|
||||
format_thrift_value({list, _}, _, CurDepth, CL, #{max_depth := MD})
|
||||
when MD >= 0, CurDepth >= MD ->
|
||||
{"[...]", CL + 5}; %% 5 = length("[...]")
|
||||
format_thrift_value({list, Type}, ValueList, CurDepth, CL, Opts) ->
|
||||
{Format, CL1} = format_thrift_list(Type, ValueList, CurDepth + 1, CL + 2, Opts),
|
||||
{["[", Format, "]"], CL1};
|
||||
format_thrift_value({set, _}, _, CurDepth, CL, #{max_depth := MD})
|
||||
when MD >= 0, CurDepth >= MD ->
|
||||
{"{...}", CL + 5}; %% 5 = length("{...}")
|
||||
format_thrift_value({set, Type}, SetofValues, CurDepth, CL, Opts) ->
|
||||
{Format, CL1} = format_thrift_set(Type, SetofValues, CurDepth + 1, CL + 2, Opts),
|
||||
{["{", Format, "}"], CL1};
|
||||
format_thrift_value({map, _}, _, CurDepth, CL, #{max_depth := MD})
|
||||
when MD >= 0, CurDepth >= MD ->
|
||||
{"#{...}", CL + 6}; %% 6 = length("#{...}")
|
||||
format_thrift_value({map, KeyType, ValueType}, Map, CurDepth, CL, Opts) ->
|
||||
{Params, CL1} =
|
||||
format_map(KeyType, ValueType, Map, "", CurDepth + 1, CL + 3, Opts, false),
|
||||
{["#{", Params, "}"], CL1};
|
||||
format_thrift_value(bool, false, _CurDepth, CL, _Opts) ->
|
||||
{"false", CL + 5};
|
||||
format_thrift_value(bool, true, _CurDepth, CL, _Opts) ->
|
||||
{"true", CL + 4};
|
||||
format_thrift_value(_Type, Value, _CurDepth, CL, _Opts) when is_integer(Value) ->
|
||||
ValueStr = integer_to_list(Value),
|
||||
Length = length(ValueStr),
|
||||
{ValueStr, CL + Length};
|
||||
format_thrift_value(_Type, Value, _CurDepth, CL, _Opts) when is_float(Value) ->
|
||||
ValueStr = float_to_list(Value),
|
||||
Length = length(ValueStr),
|
||||
{ValueStr, CL + Length}.
|
||||
format_thrift_value_({list, _}, _, Result, 0, _ML, _MPSL) ->
|
||||
<<Result/binary, "[...]">>;
|
||||
format_thrift_value_({list, Type}, ValueList, Result0, MD, ML, MPSL) ->
|
||||
Result1 = format_thrift_list(Type, ValueList, <<Result0/binary, "[">>, dec(MD), dec(ML), MPSL),
|
||||
<<Result1/binary, "]">>;
|
||||
format_thrift_value_({set, _}, _, Result, 0, _ML, _MPSL) ->
|
||||
<<Result/binary, "{...}">>;
|
||||
format_thrift_value_({set, Type}, SetofValues, Result0, MD, ML, MPSL) ->
|
||||
Result1 = format_thrift_set(Type, SetofValues, <<Result0/binary, "{">>, dec(MD), dec(ML), MPSL),
|
||||
<<Result1/binary, "}">>;
|
||||
format_thrift_value_({map, _}, _, Result, 0, _ML, _MPSL) ->
|
||||
<<Result/binary, "#{...}">>;
|
||||
format_thrift_value_({map, KeyType, ValueType}, Map, Result0, MD, ML, MPSL) ->
|
||||
Result1 = format_map(KeyType, ValueType, Map, <<Result0/binary, "#{">>, dec(MD), dec(ML), MPSL, false),
|
||||
<<Result1/binary, "}">>;
|
||||
format_thrift_value_(bool, false, Result, _MD, _ML, _MPSL) ->
|
||||
<<Result/binary, "false">>;
|
||||
format_thrift_value_(bool, true, Result, _MD, _ML, _MPSL) ->
|
||||
<<Result/binary, "true">>;
|
||||
format_thrift_value_(_Type, Value, Result, _MD, _ML, _MPSL) when is_integer(Value) ->
|
||||
ValueStr = erlang:integer_to_binary(Value),
|
||||
<<Result/binary, ValueStr/binary>>;
|
||||
format_thrift_value_(_Type, Value, Result, _MD, _ML, _MPSL) when is_float(Value) ->
|
||||
ValueStr = erlang:float_to_binary(Value, [{decimals, ?MAX_FLOAT_DECIMALS}, compact]),
|
||||
<<Result/binary, ValueStr/binary>>.
|
||||
|
||||
format_thrift_list(Type, ValueList, CurDepth, CL, Opts) when length(ValueList) =< ?MAX_PRINTABLE_LIST_LENGTH ->
|
||||
format_list(Type, ValueList, "", CurDepth, CL, Opts, false);
|
||||
format_thrift_list(Type, OriginalValueList, CurDepth, CL, #{max_length := ML} = Opts) ->
|
||||
FirstEntry = hd(OriginalValueList),
|
||||
LastEntry = lists:last(OriginalValueList),
|
||||
{FirstEntryFmt, FirstEntryCL} =
|
||||
format_thrift_value(Type, FirstEntry, CurDepth, CL, Opts),
|
||||
case FirstEntryCL < ML orelse ML < 0 of
|
||||
format_thrift_list(Type, ValueList, Result, MD, ML, MPSL) when length(ValueList) =< ?MAX_PRINTABLE_LIST_LENGTH ->
|
||||
format_list(Type, ValueList, Result, MD, ML, MPSL, false);
|
||||
format_thrift_list(Type, [FirstEntry | Rest], Result0, MD, ML, MPSL) ->
|
||||
LastEntry = lists:last(Rest),
|
||||
Result1 = format_thrift_value_(Type, FirstEntry, Result0, MD, ML, MPSL),
|
||||
case byte_size(Result1) < ML of
|
||||
true ->
|
||||
SkippedLength = length(OriginalValueList) - 2,
|
||||
SkippedMsg = ["...", integer_to_list(SkippedLength), " more..."],
|
||||
SkippedMsgLength = erlang:iolist_size(SkippedMsg),
|
||||
case FirstEntryCL + SkippedMsgLength + 2 < ML orelse ML < 0 of
|
||||
SkippedLength = erlang:integer_to_binary(length(Rest) - 1),
|
||||
Result2 = <<Result1/binary, ",...", SkippedLength/binary, " more...,">>,
|
||||
case byte_size(Result2) < ML of
|
||||
true ->
|
||||
{LastEntryFmt, LastEntryCL} =
|
||||
format_thrift_value(Type, LastEntry, CurDepth, FirstEntryCL + SkippedMsgLength + 4, Opts),
|
||||
format_list_result(
|
||||
LastEntryCL < ML orelse ML < 0,
|
||||
[FirstEntryFmt, ",", SkippedMsg, ",", LastEntryFmt],
|
||||
LastEntryCL,
|
||||
[FirstEntryFmt, ",..."],
|
||||
FirstEntryCL + 4
|
||||
);
|
||||
format_thrift_value_(Type, LastEntry, Result2, MD, ML, MPSL);
|
||||
false ->
|
||||
{[FirstEntryFmt, ",..."], FirstEntryCL + 4}
|
||||
<<Result2/binary, "...">>
|
||||
end;
|
||||
false ->
|
||||
{[FirstEntryFmt, ",..."], FirstEntryCL + 4}
|
||||
<<Result1/binary, ",...">>
|
||||
end.
|
||||
|
||||
format_list_result(true, Fmt, CL, _, _) ->
|
||||
{Fmt, CL};
|
||||
format_list_result(false, _, _, Fmt, CL) ->
|
||||
{Fmt, CL}.
|
||||
|
||||
format_thrift_set(Type, SetofValues, CurDepth, CL, Opts) ->
|
||||
format_thrift_set(Type, SetofValues, Result, MD, ML, MPSL) ->
|
||||
%% Actually ordsets is a list so leave this implementation as-is
|
||||
ValueList = ordsets:to_list(SetofValues),
|
||||
format_list(Type, ValueList, "", CurDepth, CL, Opts, false).
|
||||
format_list(Type, ValueList, Result, MD, ML, MPSL, false).
|
||||
|
||||
get_exception_type(ExceptionRecord, ExceptionTypeList) ->
|
||||
Result =
|
||||
@ -241,213 +197,202 @@ get_exception_type(ExceptionRecord, ExceptionTypeList) ->
|
||||
undefined
|
||||
end.
|
||||
|
||||
-spec format_struct(atom(), atom(), term(), non_neg_integer(), non_neg_integer(), opts()) ->
|
||||
{iolist(), non_neg_integer()}.
|
||||
format_struct(_Module, Struct, _StructValue, CurDepth, CL, #{max_depth := MD})
|
||||
when MD >= 0, CurDepth > MD ->
|
||||
{[to_string(Struct), "{...}"], CL + 5}; %% 5 = length("{...}")
|
||||
format_struct(Module, Struct, StructValue, CurDepth, CL, Opts = #{max_length := ML}) ->
|
||||
-spec format_struct(atom(), atom(), term(), binary(), non_neg_integer(), non_neg_integer(), opts()) ->
|
||||
binary().
|
||||
format_struct(_Module, Struct, _StructValue, Result, 0, _ML, _MPSL) ->
|
||||
StructName = atom_to_binary(Struct, unicode),
|
||||
<<Result/binary, StructName/binary, "{...}">>;
|
||||
format_struct(Module, Struct, StructValue, Result0, MD, ML, MPSL) ->
|
||||
%% struct and exception have same structure
|
||||
{struct, _, StructMeta} = Module:struct_info(Struct),
|
||||
ValueList = tl(tuple_to_list(StructValue)), %% Remove record name
|
||||
case length(StructMeta) == length(ValueList) of
|
||||
case length(StructMeta) == tuple_size(StructValue) - 1 of
|
||||
true ->
|
||||
StructName = to_string(Struct),
|
||||
StructNameLength = length(StructName),
|
||||
{Params, NewCL} =
|
||||
format_struct_(
|
||||
StructMeta,
|
||||
ValueList,
|
||||
"",
|
||||
CurDepth + 1,
|
||||
CL + StructNameLength + 2,
|
||||
Opts,
|
||||
false
|
||||
),
|
||||
{[StructName, "{", Params, "}"], NewCL};
|
||||
StructName = atom_to_binary(Struct, unicode),
|
||||
Result1 = format_struct_(
|
||||
StructMeta,
|
||||
StructValue,
|
||||
2,
|
||||
<<Result0/binary, StructName/binary, "{">>,
|
||||
MD,
|
||||
dec(ML),
|
||||
MPSL,
|
||||
false
|
||||
),
|
||||
<<Result1/binary, "}">>;
|
||||
false ->
|
||||
Length = get_length(ML, CL),
|
||||
Params = io_lib:format("~p", [StructValue], [{chars_limit, Length}]),
|
||||
{Params, CL + Length}
|
||||
% TODO when is this possible?
|
||||
format_verbatim(StructValue, Result0, MD, ML)
|
||||
end.
|
||||
|
||||
format_struct_([], [], Acc, _CurDepth, CL, _Opts, _AddDelimiter) ->
|
||||
{Acc, CL};
|
||||
format_struct_(_Types, _Values, Result, _CurDepth, CL, #{max_length := ML}, AddDelimiter)
|
||||
when ML >= 0, CL > ML->
|
||||
Delimiter = maybe_add_delimiter(AddDelimiter),
|
||||
DelimiterLen = length(Delimiter),
|
||||
{[Result, Delimiter, "..."], CL + 3 + DelimiterLen};
|
||||
format_struct_([Type | RestTypes], [Value | RestValues], Acc, CurDepth, CL, Opts, AddDelimiter) ->
|
||||
case format_argument(Type, Value, CurDepth, CL, Opts) of
|
||||
{undefined, CL} ->
|
||||
format_struct_(
|
||||
RestTypes,
|
||||
RestValues,
|
||||
Acc,
|
||||
CurDepth,
|
||||
CL,
|
||||
Opts,
|
||||
AddDelimiter
|
||||
);
|
||||
{F, CL1} ->
|
||||
Delimiter = maybe_add_delimiter(AddDelimiter),
|
||||
DelimiterLen = length(Delimiter),
|
||||
format_struct_(
|
||||
RestTypes,
|
||||
RestValues,
|
||||
[Acc | [Delimiter, F]],
|
||||
CurDepth, CL1 + DelimiterLen,
|
||||
Opts,
|
||||
true
|
||||
)
|
||||
end.
|
||||
format_struct_([], S, I, Result, _MD, _ML, _MPSL, _AD) when I > tuple_size(S) ->
|
||||
Result;
|
||||
format_struct_(_Types, _S, _I, Result0, _MD, ML, _MPSL, AD) when byte_size(Result0) > ML ->
|
||||
Result1 = maybe_add_delimiter(AD, Result0),
|
||||
<<Result1/binary, "...">>;
|
||||
format_struct_([Type | RestTypes], S, I, Result0, MD, ML, MPSL, AD) ->
|
||||
Value = erlang:element(I, S),
|
||||
{Result1, AD1} = format_argument(Type, Value, Result0, MD, ML, MPSL, AD),
|
||||
format_struct_(
|
||||
RestTypes,
|
||||
S,
|
||||
I + 1,
|
||||
Result1,
|
||||
MD,
|
||||
ML,
|
||||
MPSL,
|
||||
AD1
|
||||
).
|
||||
|
||||
-spec format_union(atom(), atom(), term(), non_neg_integer(), non_neg_integer(), opts()) ->
|
||||
{iolist(), non_neg_integer()}.
|
||||
format_union(_Module, Struct, _StructValue, CurDepth, CL, #{max_depth := MD})
|
||||
when MD >= 0, CurDepth > MD ->
|
||||
{[to_string(Struct), "{...}"], CL + 5}; %% 5 = length("{...}"
|
||||
format_union(Module, Struct, {Type, UnionValue}, CurDepth, CL, Opts) ->
|
||||
-spec format_union(atom(), atom(), term(), binary(), non_neg_integer(), non_neg_integer(), opts()) ->
|
||||
binary().
|
||||
format_union(_Module, Struct, _StructValue, Result, 0, _ML, _MPSL) ->
|
||||
StructName = atom_to_binary(Struct, unicode),
|
||||
<<Result/binary, StructName/binary, "{...}">>;
|
||||
format_union(Module, Struct, {Type, UnionValue}, Result0, MD, ML, MPSL) ->
|
||||
{struct, union, StructMeta} = Module:struct_info(Struct),
|
||||
{value, UnionMeta} = lists:keysearch(Type, 4, StructMeta),
|
||||
StructName = to_string(Struct),
|
||||
StructNameLen = length(StructName),
|
||||
{Argument, CL1} = format_argument(
|
||||
UnionMeta,
|
||||
UnionValue,
|
||||
CurDepth,
|
||||
CL + StructNameLen + 2,
|
||||
Opts
|
||||
), %% 2 = length("{}")
|
||||
{[StructName, "{", Argument, "}"], CL1}.
|
||||
UnionMeta = lists:keyfind(Type, 4, StructMeta),
|
||||
StructName = atom_to_binary(Struct, unicode),
|
||||
Result1 = <<Result0/binary, StructName/binary, "{">>,
|
||||
case byte_size(Result1) > ML of
|
||||
false ->
|
||||
{Result2, _} = format_argument(
|
||||
UnionMeta,
|
||||
UnionValue,
|
||||
Result1,
|
||||
MD,
|
||||
dec(ML),
|
||||
MPSL,
|
||||
false
|
||||
),
|
||||
<<Result2/binary, "}">>;
|
||||
true ->
|
||||
<<Result1/binary, "...}">>
|
||||
end.
|
||||
|
||||
format_non_printable_string(Value) ->
|
||||
Size = byte_size(Value),
|
||||
["<<", integer_to_list(Size), " bytes>>"].
|
||||
format_non_printable_string(Value, Result) ->
|
||||
SizeStr = erlang:integer_to_binary(byte_size(Value)),
|
||||
<<Result/binary, "<<", SizeStr/binary, " bytes>>">>.
|
||||
|
||||
is_printable(<<>> = Slice, _Opts) ->
|
||||
{ok, Slice};
|
||||
is_printable(Value, #{max_pritable_string_length := MPSL}) ->
|
||||
is_printable(<<>> = Slice, _ML, _CL, _MPSL) ->
|
||||
Slice;
|
||||
is_printable(Value, ML, CL, MPSL) ->
|
||||
%% Try to get slice of first MPSL bytes from Value,
|
||||
%% assuming success means Value is printable string
|
||||
%% NOTE: Empty result means non-printable Value
|
||||
ValueSize = byte_size(Value),
|
||||
try string:slice(Value, 0, MPSL) of
|
||||
try string:slice(Value, 0, compute_slice_length(ML, CL, MPSL)) of
|
||||
<<>> ->
|
||||
{error, not_printable};
|
||||
Slice when byte_size(Slice) < ValueSize ->
|
||||
{ok, [Slice, "..."]};
|
||||
not_printable;
|
||||
Slice ->
|
||||
{ok, Slice}
|
||||
Slice
|
||||
catch
|
||||
_:_ ->
|
||||
%% Completely wrong binary data drives to crash in string internals,
|
||||
%% mark such data as non-printable instead
|
||||
{error, not_printable}
|
||||
not_printable
|
||||
end.
|
||||
|
||||
-spec value_to_string(list() | binary() | atom()) -> list().
|
||||
value_to_string(S) ->
|
||||
[maybe_replace(C) || C <- string:to_graphemes(to_string(S))].
|
||||
dec(unlimited) -> unlimited;
|
||||
dec(0) -> 0;
|
||||
dec(N) -> N - 1.
|
||||
|
||||
maybe_replace($\') ->
|
||||
[$\\, $\'];
|
||||
maybe_replace(C) ->
|
||||
format_verbatim(Term, Result, #{max_length := ML, max_depth := MD}) ->
|
||||
format_verbatim(Term, Result, MD, ML).
|
||||
|
||||
format_verbatim(Term, Result, MD, ML) ->
|
||||
Opts = [
|
||||
{line_length , 0},
|
||||
{depth , case MD of unlimited -> -1; _ -> MD end},
|
||||
{chars_limit , case ML of unlimited -> -1; _ -> max_(0, ML - byte_size(Result)) end},
|
||||
{encoding , unicode}
|
||||
],
|
||||
Printout = erlang:iolist_to_binary(io_lib_pretty:print(Term, Opts)),
|
||||
<<Result/binary, Printout/binary>>.
|
||||
|
||||
compute_slice_length(unlimited, _CL, MPSL) ->
|
||||
MPSL;
|
||||
compute_slice_length(ML, CL, MPSL) ->
|
||||
max_(12, min_(ML - CL, MPSL)). % length("<<X bytes>>") = 11
|
||||
|
||||
max_(A, B) when A > B -> A;
|
||||
max_(_, B) -> B.
|
||||
|
||||
min_(A, B) when A < B -> A;
|
||||
min_(_, B) -> B.
|
||||
|
||||
-spec escape_printable_string(binary(), binary()) -> binary().
|
||||
escape_printable_string(<<>>, Result) ->
|
||||
Result;
|
||||
escape_printable_string(<<$', String/binary>>, Result) ->
|
||||
escape_printable_string(String, <<Result/binary, "\\'">>);
|
||||
escape_printable_string(<<C/utf8, String/binary>>, Result) ->
|
||||
case unicode_util:is_whitespace(C) of
|
||||
true -> $\s;
|
||||
false -> C
|
||||
true -> escape_printable_string(String, <<Result/binary, " ">>);
|
||||
false -> escape_printable_string(String, <<Result/binary, C/utf8>>)
|
||||
end.
|
||||
|
||||
-spec to_string(list() | binary() | atom()) -> list().
|
||||
%% NOTE: Must match to supported types for `~s`
|
||||
to_string(Value) when is_list(Value) ->
|
||||
Value;
|
||||
-spec to_string(binary() | atom()) -> binary().
|
||||
to_string(Value) when is_binary(Value) ->
|
||||
unicode:characters_to_list(Value);
|
||||
Value;
|
||||
to_string(Value) when is_atom(Value) ->
|
||||
atom_to_list(Value);
|
||||
erlang:atom_to_binary(Value, unicode);
|
||||
to_string(_) ->
|
||||
error(badarg).
|
||||
|
||||
normalize_options(Opts) ->
|
||||
maps:merge(#{
|
||||
max_depth => -1,
|
||||
max_length => -1,
|
||||
max_depth => unlimited,
|
||||
max_length => unlimited,
|
||||
max_pritable_string_length => ?MAX_BIN_SIZE
|
||||
}, Opts).
|
||||
|
||||
maybe_add_delimiter(false) ->
|
||||
"";
|
||||
maybe_add_delimiter(true) ->
|
||||
",".
|
||||
maybe_add_delimiter(false, Result) ->
|
||||
Result;
|
||||
maybe_add_delimiter(true, Result) ->
|
||||
<<Result/binary, ",">>.
|
||||
|
||||
maybe_add_more_marker(false) ->
|
||||
"";
|
||||
maybe_add_more_marker(true) ->
|
||||
"...".
|
||||
maybe_add_more_marker(false, Result) ->
|
||||
Result;
|
||||
maybe_add_more_marker(true, Result) ->
|
||||
<<Result/binary, "...">>.
|
||||
|
||||
get_length(ML, CL) when ML > CL ->
|
||||
ML - CL;
|
||||
get_length(_ML, _CL) ->
|
||||
0.
|
||||
|
||||
format_list(_Type, [], Result, _CurDepth, CL, _Opts, _IsFirst) ->
|
||||
{Result, CL};
|
||||
format_list(Type, [Entry | ValueList], AccFmt, CurDepth, CL, Opts, IsFirst) ->
|
||||
#{max_length := ML} = Opts,
|
||||
Delimiter = maybe_add_delimiter(IsFirst),
|
||||
DelimiterLen = length(Delimiter),
|
||||
{Fmt, CL1} = format_thrift_value(Type, Entry, CurDepth, CL + DelimiterLen, Opts),
|
||||
Result = [AccFmt | [Delimiter, Fmt]],
|
||||
case CL1 of
|
||||
CL1 when ML < 0 ->
|
||||
format_list(Type, ValueList, Result, CurDepth, CL1, Opts, true);
|
||||
CL1 when CL1 =< ML ->
|
||||
format_list(Type, ValueList, Result, CurDepth, CL1, Opts, true);
|
||||
CL1 ->
|
||||
format_list(_Type, [], Result, _MD, _ML, _MPSL, _AD) ->
|
||||
Result;
|
||||
format_list(Type, [Entry | ValueList], Result0, MD, ML, MPSL, AD) ->
|
||||
Result1 = maybe_add_delimiter(AD, Result0),
|
||||
Result2 = format_thrift_value_(Type, Entry, Result1, MD, ML, MPSL),
|
||||
case byte_size(Result2) of
|
||||
CL when ML > CL ->
|
||||
format_list(Type, ValueList, Result2, MD, ML, MPSL, true);
|
||||
_ ->
|
||||
MaybeAddMoreMarker = length(ValueList) =/= 0,
|
||||
stop_format(Result, CL1, MaybeAddMoreMarker)
|
||||
stop_format(MaybeAddMoreMarker, Result2)
|
||||
end.
|
||||
|
||||
format_map(_KeyType, _ValueType, E, Result, _CurDepth, CL, _Opts, _AddDelimiter) when map_size(E) =:= 0 ->
|
||||
{Result, CL};
|
||||
format_map(KeyType, ValueType, Map, AccFmt, CurDepth, CL, Opts, AddDelimiter) ->
|
||||
format_map(_KeyType, _ValueType, Map, Result, _MD, _ML, _MPSL, _AD) when map_size(Map) =:= 0 ->
|
||||
Result;
|
||||
format_map(KeyType, ValueType, Map, Result0, MD, ML, MPSL, AD) ->
|
||||
MapIterator = maps:iterator(Map),
|
||||
format_map_(KeyType, ValueType, maps:next(MapIterator), AccFmt, CurDepth, CL, Opts, AddDelimiter).
|
||||
format_map_(KeyType, ValueType, maps:next(MapIterator), Result0, MD, ML, MPSL, AD).
|
||||
|
||||
format_map_(KeyType, ValueType, {Key, Value, MapIterator}, AccFmt, CurDepth, CL, Opts, AddDelimiter) ->
|
||||
{KeyFmt, CL1} = format_thrift_value(KeyType, Key, CurDepth, CL, Opts),
|
||||
{ValueFmt, CL2} = format_thrift_value(ValueType, Value, CurDepth, CL1, Opts),
|
||||
#{max_length := ML} = Opts,
|
||||
MapStr = "=>",
|
||||
MapStrLen = 2,
|
||||
Delimiter = maybe_add_delimiter(AddDelimiter),
|
||||
DelimiterLen = length(Delimiter),
|
||||
NewCL = CL2 + MapStrLen + DelimiterLen,
|
||||
Result = [AccFmt | [Delimiter, KeyFmt, MapStr, ValueFmt]],
|
||||
format_map_(KeyType, ValueType, {Key, Value, MapIterator}, Result0, MD, ML, MPSL, AD) ->
|
||||
Result1 = maybe_add_delimiter(AD, Result0),
|
||||
Result2 = format_thrift_value_(KeyType, Key, Result1, MD, ML, MPSL),
|
||||
Result3 = format_thrift_value_(ValueType, Value, <<Result2/binary, "=">>, MD, ML, MPSL),
|
||||
Next = maps:next(MapIterator),
|
||||
case NewCL of
|
||||
NewCL when ML < 0 ->
|
||||
format_map_(KeyType, ValueType, Next, Result, CurDepth, NewCL, Opts, true);
|
||||
NewCL when NewCL =< ML ->
|
||||
format_map_(KeyType, ValueType, Next, Result, CurDepth, NewCL, Opts, true);
|
||||
NewCL ->
|
||||
case byte_size(Result3) of
|
||||
CL when ML > CL ->
|
||||
format_map_(KeyType, ValueType, Next, Result3, MD, ML, MPSL, true);
|
||||
_ ->
|
||||
MaybeAddMoreMarker = Next =/= none,
|
||||
stop_format(Result, NewCL, MaybeAddMoreMarker)
|
||||
stop_format(MaybeAddMoreMarker, Result3)
|
||||
end;
|
||||
format_map_(_, _, _, AccFmt, _, CL, _, _) ->
|
||||
{AccFmt, CL}.
|
||||
format_map_(_, _, _, Result, _, _, _, _) ->
|
||||
Result.
|
||||
|
||||
stop_format(Result, CL1, MaybeAddMoreMarker) ->
|
||||
Delimiter1 = maybe_add_delimiter(MaybeAddMoreMarker),
|
||||
DelimiterLen1 = length(Delimiter1),
|
||||
MoreMarker = maybe_add_more_marker(MaybeAddMoreMarker),
|
||||
MoreMarkerLen = length(MoreMarker),
|
||||
{
|
||||
[Result | [Delimiter1, MoreMarker]],
|
||||
CL1 + MoreMarkerLen + DelimiterLen1
|
||||
}.
|
||||
stop_format(MaybeAddMoreMarker, Result0) ->
|
||||
Result1 = maybe_add_delimiter(MaybeAddMoreMarker, Result0),
|
||||
Result2 = maybe_add_more_marker(MaybeAddMoreMarker, Result1),
|
||||
Result2.
|
||||
|
||||
-ifdef(TEST).
|
||||
|
||||
@ -608,7 +553,8 @@ depth_test_() -> [
|
||||
dmsl_payment_processing_thrift,
|
||||
'PartyManagement',
|
||||
'CreateClaim',
|
||||
?ARGS
|
||||
?ARGS,
|
||||
#{}
|
||||
)
|
||||
)
|
||||
),
|
||||
@ -641,21 +587,6 @@ depth_test_() -> [
|
||||
"PartyManagement:CreateClaim(party_id='1CR1Xziml7o',changeset=[PartyModification{"
|
||||
"contract_modification=ContractModificationUnit{...}},...2 more...,"
|
||||
"PartyModification{shop_modification=ShopModificationUnit{...}}])",
|
||||
format_msg(
|
||||
format_call(
|
||||
dmsl_payment_processing_thrift,
|
||||
'PartyManagement',
|
||||
'CreateClaim',
|
||||
?ARGS,
|
||||
#{max_depth => 2}
|
||||
)
|
||||
)
|
||||
),
|
||||
?_assertEqual(
|
||||
"PartyManagement:CreateClaim(party_id='1CR1Xziml7o',changeset=[PartyModification{"
|
||||
"contract_modification=ContractModificationUnit{id='1CR1Y2ZcrA0',modification="
|
||||
"ContractModification{...}}},...2 more...,PartyModification{shop_modification="
|
||||
"ShopModificationUnit{id='1CR1Y2ZcrA2',modification=ShopModification{...}}}])",
|
||||
format_msg(
|
||||
format_call(
|
||||
dmsl_payment_processing_thrift,
|
||||
@ -681,23 +612,35 @@ depth_test_() -> [
|
||||
)
|
||||
)
|
||||
),
|
||||
?_test(
|
||||
begin
|
||||
{Result, _} =
|
||||
format_thrift_value(
|
||||
{set, string},
|
||||
ordsets:from_list(["a", "2", "ddd"]),
|
||||
0, 0, #{max_length => -1, max_depth => -1}
|
||||
),
|
||||
?_assertEqual(
|
||||
"{'a', '2', 'ddd'}",
|
||||
lists:flatten(Result)
|
||||
)
|
||||
end
|
||||
?_assertEqual(
|
||||
"PartyManagement:CreateClaim(party_id='1CR1Xziml7o',changeset=[PartyModification{"
|
||||
"contract_modification=ContractModificationUnit{id='1CR1Y2ZcrA0',modification="
|
||||
"ContractModification{...}}},...2 more...,PartyModification{shop_modification="
|
||||
"ShopModificationUnit{id='1CR1Y2ZcrA2',modification=ShopModification{...}}}])",
|
||||
format_msg(
|
||||
format_call(
|
||||
dmsl_payment_processing_thrift,
|
||||
'PartyManagement',
|
||||
'CreateClaim',
|
||||
?ARGS,
|
||||
#{max_depth => 4}
|
||||
)
|
||||
)
|
||||
),
|
||||
?_assertEqual(
|
||||
<<"{'2','a','ddd'}">>,
|
||||
format_thrift_value(
|
||||
{set, string},
|
||||
ordsets:from_list([<<"a">>, <<"2">>, <<"ddd">>]),
|
||||
<<>>,
|
||||
unlimited,
|
||||
unlimited,
|
||||
?MAX_PRINTABLE_LIST_LENGTH
|
||||
)
|
||||
),
|
||||
?_assertEqual(
|
||||
"Processor:ProcessCall(a=CallArgs{arg=Value{bin=<<732 bytes>>},machine=Machine{ns='party',"
|
||||
"id='1CSHThTEJ84',history=[...],history_range=HistoryRange{...},aux_state=Content{...},"
|
||||
"id='1CSHThTEJ84',history=[Event{...}],history_range=HistoryRange{...},aux_state=Content{...},"
|
||||
"aux_state_legacy=Value{...}}})",
|
||||
format_msg(
|
||||
format_call(
|
||||
@ -705,22 +648,22 @@ depth_test_() -> [
|
||||
'Processor',
|
||||
'ProcessCall',
|
||||
?ARGS2,
|
||||
#{max_depth => 4}
|
||||
#{max_depth => 3}
|
||||
)
|
||||
)
|
||||
),
|
||||
?_assertEqual(
|
||||
"Processor:ProcessCall(a=CallArgs{arg=Value{bin=<<732 bytes>>},machine=Machine{ns='party',"
|
||||
"id='1CSHThTEJ84',history=[Event{...}],history_range=HistoryRange{limit=10,direction="
|
||||
"backward},aux_state=Content{data=Value{...}},aux_state_legacy=Value{obj=#{Value{...}=>"
|
||||
"Value{...},Value{...}=>Value{...}}}}})",
|
||||
"backward},aux_state=Content{data=Value{...}},aux_state_legacy=Value{obj=#{Value{...}="
|
||||
"Value{...},Value{...}=Value{...}}}}})",
|
||||
format_msg(
|
||||
format_call(
|
||||
mg_proto_state_processing_thrift,
|
||||
'Processor',
|
||||
'ProcessCall',
|
||||
?ARGS2,
|
||||
#{max_depth => 5}
|
||||
#{max_depth => 4}
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -748,7 +691,7 @@ length_test_() -> [
|
||||
'PartyManagement',
|
||||
'CreateClaim',
|
||||
?ARGS,
|
||||
#{max_length => -1}
|
||||
#{max_length => unlimited}
|
||||
)
|
||||
)
|
||||
),
|
||||
@ -786,7 +729,10 @@ length_test_() -> [
|
||||
"O\\'Centro, 060...',post_address='NaN',representative_position='Director',"
|
||||
"representative_full_name='Someone',representative_document='100$ banknote',"
|
||||
"russian_bank_account=RussianBankAccount{account='4276300010908312893',bank_name="
|
||||
"'SomeBank',bank_post_account='123129876',bank_bik='66642666'}}}}}}}},...])",
|
||||
"'SomeBank',bank_post_account='123129876',bank_bik='66642666'}}}}}}}},...2 more...,"
|
||||
"PartyModification{shop_modification=ShopModificationUnit{id='1CR1Y2ZcrA2',"
|
||||
"modification=ShopModification{shop_account_creation=ShopAccountParams{"
|
||||
"currency=CurrencyRef{symbolic_code='RUB'}}}}}])",
|
||||
format_msg(
|
||||
format_call(
|
||||
dmsl_payment_processing_thrift,
|
||||
@ -816,7 +762,7 @@ length_test_() -> [
|
||||
"ContractParams{template=ContractTemplateRef{id=1},payment_institution=PaymentInstitutionRef{id=1},"
|
||||
"contractor=Contractor{legal_entity=LegalEntity{russian_legal_entity=RussianLegalEntity{"
|
||||
"registered_name='Hoofs & Horns OJSC',registered_number='1234509876',inn='1213456789012',"
|
||||
"actual_address='Nezahualcoyotl 109 Piso 8, O\\'Centro, 060...',...}}}}}}},...])",
|
||||
"actual_address='Nezahualcoyotl 109 Piso 8, O...',...}}}}}}},...])",
|
||||
format_msg(
|
||||
format_call(
|
||||
dmsl_payment_processing_thrift,
|
||||
@ -830,13 +776,13 @@ length_test_() -> [
|
||||
?_assertEqual(
|
||||
"Processor:ProcessCall(a=CallArgs{arg=Value{bin=<<732 bytes>>},machine=Machine{"
|
||||
"ns='party',id='1CSHThTEJ84',history=[Event{id=1,created_at='2019-08-13T07:52:11.080519Z',"
|
||||
"data=Value{arr=[Value{obj=#{Value{str='ct'}=>Value{str='application/x-erlang-binary'},"
|
||||
"Value{str='vsn'}=>Value{i=6}}},Value{bin=<<249 bytes>>}]}}],history_range="
|
||||
"data=Value{arr=[Value{obj=#{Value{str='ct'}=Value{str='application/x-erlang-binary'},"
|
||||
"Value{str='vsn'}=Value{i=6}}},Value{bin=<<249 bytes>>}]}}],history_range="
|
||||
"HistoryRange{limit=10,"
|
||||
"direction=backward},aux_state=Content{data=Value{obj=#{Value{str='aux_state'}=>"
|
||||
"Value{bin=<<52 bytes>>},Value{str='ct'}=>Value{str='application/x-erlang-binary'}}}},"
|
||||
"aux_state_legacy=Value{obj=#{Value{str='aux_state'}=>Value{bin=<<52 bytes>>},Value{str='ct'}"
|
||||
"=>Value{str='application/x-erlang-binary'}}}}})",
|
||||
"direction=backward},aux_state=Content{data=Value{obj=#{Value{str='aux_state'}="
|
||||
"Value{bin=<<52 bytes>>},Value{str='ct'}=Value{str='application/x-erlang-binary'}}}},"
|
||||
"aux_state_legacy=Value{obj=#{Value{str='aux_state'}=Value{bin=<<52 bytes>>},Value{str='ct'}"
|
||||
"=Value{str='application/x-erlang-binary'}}}}})",
|
||||
format_msg(
|
||||
format_call(
|
||||
mg_proto_state_processing_thrift,
|
||||
@ -850,11 +796,12 @@ length_test_() -> [
|
||||
?_assertEqual(
|
||||
"Processor:ProcessCall(a=CallArgs{arg=Value{bin=<<732 bytes>>},machine=Machine{ns='party',"
|
||||
"id='1CSHThTEJ84',history=[Event{id=1,created_at='2019-08-13T07:52:11.080519Z',data="
|
||||
"Value{arr=[Value{obj=#{Value{str='ct'}=>Value{str='application/x-erlang-binary'},"
|
||||
"Value{str='vsn'}=>Value{i=6}}},Value{bin=<<249 bytes>>}]}}],history_range="
|
||||
"Value{arr=[Value{obj=#{Value{str='ct'}=Value{str='application/x-erlang-binary'},"
|
||||
"Value{str='vsn'}=Value{i=6}}},Value{bin=<<249 bytes>>}]}}],history_range="
|
||||
"HistoryRange{limit=10,"
|
||||
"direction=backward},aux_state=Content{data=Value{obj=#{Value{str='aux_state'}=>"
|
||||
"Value{bin=<<52 bytes>>},Value{str='ct'}=>Value{str='application/x-erlang-binary'}}}},...}})",
|
||||
"direction=backward},aux_state=Content{data=Value{obj=#{Value{str='aux_state'}="
|
||||
"Value{bin=<<52 bytes>>},Value{str='ct'}=Value{str='application/x-erlang-binary'}}}},"
|
||||
"aux_state_legacy=Value{...}}})",
|
||||
format_msg(
|
||||
format_call(
|
||||
mg_proto_state_processing_thrift,
|
||||
@ -868,8 +815,7 @@ length_test_() -> [
|
||||
?_assertEqual(
|
||||
"Processor:ProcessCall(a=CallArgs{arg=Value{bin=<<732 bytes>>},machine=Machine{ns='party',"
|
||||
"id='1CSHThTEJ84',history=[Event{id=1,created_at='2019-08-13T07:52:11.080519Z',data="
|
||||
"Value{arr=[Value{obj=#{Value{str='ct'}=>Value{str='application/x-erlang-binary'},...}},"
|
||||
"...]}}],...}})",
|
||||
"Value{arr=[Value{obj=#{Value{...}=Value{...},...}},...]}}],...}})",
|
||||
format_msg(
|
||||
format_call(
|
||||
mg_proto_state_processing_thrift,
|
||||
@ -895,8 +841,8 @@ length_test_() -> [
|
||||
)
|
||||
].
|
||||
|
||||
-spec depth_and_lenght_test_() -> _.
|
||||
depth_and_lenght_test_() -> [
|
||||
-spec depth_and_length_test_() -> _.
|
||||
depth_and_length_test_() -> [
|
||||
?_assertEqual(
|
||||
"PartyManagement:CreateClaim(party_id='1CR1Xziml7o',changeset=[PartyModification{contract_modification"
|
||||
"=ContractModificationUnit{id='1CR1Y2ZcrA0',modification=ContractModification{creation="
|
||||
@ -926,13 +872,13 @@ depth_and_lenght_test_() -> [
|
||||
'PartyManagement',
|
||||
'CreateClaim',
|
||||
?ARGS,
|
||||
#{max_length => 2048, max_depth => 7}
|
||||
#{max_length => 2048, max_depth => 6}
|
||||
)
|
||||
)
|
||||
),
|
||||
?_assertEqual(
|
||||
"Processor:ProcessCall(a=CallArgs{arg=Value{bin=<<732 bytes>>},machine=Machine{ns='party',"
|
||||
"id='1CSHThTEJ84',history=[...],history_range=HistoryRange{...},aux_state=Content{...},"
|
||||
"id='1CSHThTEJ84',history=[Event{...}],history_range=HistoryRange{...},aux_state=Content{...},"
|
||||
"aux_state_legacy=Value{...}}})",
|
||||
format_msg(
|
||||
format_call(
|
||||
@ -948,14 +894,34 @@ depth_and_lenght_test_() -> [
|
||||
"Processor:ProcessCall(a=CallArgs{arg=Value{bin=<<732 bytes>>},machine=Machine{ns='party',"
|
||||
"id='1CSHThTEJ84',history=[Event{...}],history_range=HistoryRange{limit=10,"
|
||||
"direction=backward},aux_state=Content{data=Value{...}},aux_state_legacy=Value{obj="
|
||||
"#{Value{...}=>Value{...},Value{...}=>Value{...}}}}})",
|
||||
"#{Value{...}=Value{...},Value{...}=Value{...}}}}})",
|
||||
format_msg(
|
||||
format_call(
|
||||
mg_proto_state_processing_thrift,
|
||||
'Processor',
|
||||
'ProcessCall',
|
||||
?ARGS2,
|
||||
#{max_length => 512, max_depth => 5}
|
||||
#{max_length => 512, max_depth => 4}
|
||||
)
|
||||
)
|
||||
)
|
||||
].
|
||||
|
||||
-spec verbatim_test_() -> _.
|
||||
verbatim_test_() -> [
|
||||
?_assertEqual(
|
||||
"PartyManagement:CallMissingFunction("
|
||||
"[undefined,<<\"1CR1Xziml7o\">>,[{contract_modification,{payproc_ContractModificationUnit,"
|
||||
"<<\"1CR1\"...>>,{...}}},{contract_modification,{payproc_ContractModificationUnit,<<...>>,"
|
||||
"...}},{shop_modification,{payproc_ShopModificationUnit,...}}|...]]"
|
||||
")",
|
||||
format_msg(
|
||||
format_call(
|
||||
dmsl_payment_processing_thrift,
|
||||
'PartyManagement',
|
||||
'CallMissingFunction',
|
||||
?ARGS,
|
||||
#{max_length => 256}
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -699,7 +699,9 @@ format_service_request_test_() -> [
|
||||
"Centro, 06082...',post_address='NaN',representative_position='Director',"
|
||||
"representative_full_name='Someone',representative_document='100$ banknote',"
|
||||
"russian_bank_account=RussianBankAccount{account='4276300010908312893',bank_name='SomeBank',"
|
||||
"bank_post_account='123129876',bank_bik='66642666'}}}}}}}},...])",
|
||||
"bank_post_account='123129876',bank_bik='66642666'}}}}}}}},...2 more...,"
|
||||
"PartyModification{shop_modification=ShopModificationUnit{id='1CR1Y2ZcrA2',"
|
||||
"modification=ShopModification{shop_account_creation=ShopA...",
|
||||
format_msg_limited(
|
||||
format_event(
|
||||
?EV_CALL_SERVICE,
|
||||
@ -762,12 +764,12 @@ format_service_request_test_() -> [
|
||||
"[1012689088739803136 1012689108264288256 1012689088534282240][client] calling "
|
||||
"Processor:ProcessCall(a=CallArgs{arg=Value{bin=<<732 bytes>>},machine=Machine{ns='party',"
|
||||
"id='1CSHThTEJ84',history=[Event{id=1,created_at='2019-08-13T07:52:11.080519Z',"
|
||||
"data=Value{arr=[Value{obj=#{Value{str='ct'}=>Value{str='application/x-erlang-binary'},"
|
||||
"Value{str='vsn'}=>Value{i=6}}},Value{bin=<<249 bytes>>}]}}],history_range=HistoryRange{"
|
||||
"data=Value{arr=[Value{obj=#{Value{str='ct'}=Value{str='application/x-erlang-binary'},"
|
||||
"Value{str='vsn'}=Value{i=6}}},Value{bin=<<249 bytes>>}]}}],history_range=HistoryRange{"
|
||||
"limit=10,direction=backward},aux_state=Content{data=Value{obj=#{Value{str='aux_state'}"
|
||||
"=>Value{bin=<<52 bytes>>},Value{str='ct'}=>Value{str='application/x-erlang-binary'}}}},"
|
||||
"aux_state_legacy=Value{obj=#{Value{str='aux_state'}=>Value{bin=<<52 bytes>>},Value{str='ct'}"
|
||||
"=>Value{str='application/x-erlang-binary'}}}}})",
|
||||
"=Value{bin=<<52 bytes>>},Value{str='ct'}=Value{str='application/x-erlang-binary'}}}},"
|
||||
"aux_state_legacy=Value{obj=#{Value{str='aux_state'}=Value{bin=<<52 bytes>>},Value{str='ct'}"
|
||||
"=Value{str='application/x-erlang-binary'}}}}})",
|
||||
format_msg_limited(
|
||||
format_event(
|
||||
?EV_CALL_SERVICE,
|
||||
@ -894,7 +896,7 @@ format_service_request_with_limit_test_() -> [
|
||||
"russian_bank_account=RussianBankAccount{account='4276300010908312893',bank_name='SomeBank',"
|
||||
"bank_post_account='123129876',bank_bik='66642666'}}}}}}}},...2 more...,"
|
||||
"PartyModification{shop_modification=ShopModificationUnit{id='1CR1Y2ZcrA2',modification=ShopModification"
|
||||
"{shop_account_creation=Shop...)",
|
||||
"{shop_account_creation=ShopA...",
|
||||
format_msg_limited(
|
||||
format_event(
|
||||
?EV_CALL_SERVICE,
|
||||
@ -961,9 +963,9 @@ result_test_() -> [
|
||||
?_assertEqual(
|
||||
"[1012689088739803136 1012689108264288256 1012689088534282240][client] request handled successfully: "
|
||||
"CallResult{response=Value{bin=<<6 bytes>>},change=MachineStateChange{aux_state="
|
||||
"Content{data=Value{obj=#{Value{str='aux_state'}=>Value{bin=<<108 bytes>>},Value{str='ct'}=>"
|
||||
"Content{data=Value{obj=#{Value{str='aux_state'}=Value{bin=<<108 bytes>>},Value{str='ct'}="
|
||||
"Value{str='application/x-erlang-binary'}}}},events=[Content{data=Value{arr=[Value{obj="
|
||||
"#{Value{str='ct'}=>Value{str='application/x-erlang-binary'},Value{str='vsn'}=>Value{i=6}}},"
|
||||
"#{Value{str='ct'}=Value{str='application/x-erlang-binary'},Value{str='vsn'}=Value{i=6}}},"
|
||||
"Value{bin=<<240 bytes>>}]}}]},action=ComplexAction{}}",
|
||||
format_msg_limited(
|
||||
format_event(
|
||||
@ -1045,15 +1047,15 @@ result_test_() -> [
|
||||
"Party{id='1CSWG2vduGe',contact_info=PartyContactInfo{email='hg_ct_helper'},created_at="
|
||||
"'2019-08-13T11:19:01.249440Z',blocking=Blocking{unblocked=Unblocked{reason='',since="
|
||||
"'2019-08-13T11:19:02.655869Z'}},suspension=Suspension{active=Active{since="
|
||||
"'2019-08-13T11:19:02.891892Z'}},contractors=#{},contracts=#{'1CSWG8j04wK'=>Contract{id="
|
||||
"'2019-08-13T11:19:02.891892Z'}},contractors=#{},contracts=#{'1CSWG8j04wK'=Contract{id="
|
||||
"'1CSWG8j04wK',payment_institution=PaymentInstitutionRef{id=1},created_at="
|
||||
"'2019-08-13T11:19:01.387269Z',status=ContractStatus{active=ContractActive{}},terms="
|
||||
"TermSetHierarchyRef{id=1},adjustments=[],payout_tools=[PayoutTool{id='1CSWG8j04wL',"
|
||||
"created_at='2019-08-13T11:19:01.387269Z',currency=CurrencyRef{symbolic_code='RUB'},"
|
||||
"payout_tool_info=PayoutToolInfo{russian_bank_account=RussianBankAccount{account="
|
||||
"'4276300010908312893',bank_name='SomeBank',bank_post_account='123129876',bank_bik="
|
||||
"'66642666'}}}],contractor=Contractor{legal_entity=LegalEntity{russian_legal_entity=RussianLegalEntity{...}"
|
||||
"...",
|
||||
"'66642666'}}}],contractor=Contractor{legal_entity=LegalEntity{russian_legal_entity="
|
||||
"RussianLegalEntity{regis...",
|
||||
format_msg_limited(
|
||||
format_event(
|
||||
?EV_SERVICE_RESULT,
|
||||
@ -1126,11 +1128,11 @@ result_test_() -> [
|
||||
"successfully: "
|
||||
"SignalResult{change=MachineStateChange{aux_state=Content{data=Value{obj=#{}}},"
|
||||
"events=[Content{data=Value{arr=[Value{arr=[Value{i=2},Value{obj=#{Value{"
|
||||
"str='change'}=>Value{str='created'},Value{str='contact_info'}=>Value{obj=#{Value{"
|
||||
"str='email'}=>Value{str='create_customer'}}},Value{str='created_at'}=>Value{"
|
||||
"str='2019-08-13T11:19:03.714218Z'},Value{str='customer_id'}=>Value{str='1CSWGJ3N8Ns'},"
|
||||
"Value{str='metadata'}=>Value{nl=Nil{}},Value{str='owner_id'}=>Value{str='1CSWG2vduGe'},"
|
||||
"Value{str='shop_id'}=>Value{str='1CSWG8j04wM'}}}]}]}}]},action=ComplexAction{}}",
|
||||
"str='change'}=Value{str='created'},Value{str='contact_info'}=Value{obj=#{Value{"
|
||||
"str='email'}=Value{str='create_customer'}}},Value{str='created_at'}=Value{"
|
||||
"str='2019-08-13T11:19:03.714218Z'},Value{str='customer_id'}=Value{str='1CSWGJ3N8Ns'},"
|
||||
"Value{str='metadata'}=Value{nl=Nil{}},Value{str='owner_id'}=Value{str='1CSWG2vduGe'},"
|
||||
"Value{str='shop_id'}=Value{str='1CSWG8j04wM'}}}]}]}}]},action=ComplexAction{}}",
|
||||
format_msg_limited(
|
||||
format_event(
|
||||
?EV_SERVICE_RESULT,
|
||||
|
@ -2,113 +2,145 @@
|
||||
|
||||
%% API
|
||||
-export([
|
||||
bench_event_handler/2
|
||||
iolib_formatter/1,
|
||||
bench_iolib_formatter/2,
|
||||
thrift_formatter/1,
|
||||
bench_thrift_formatter/2
|
||||
]).
|
||||
|
||||
-spec bench_event_handler(term(), term()) -> term().
|
||||
bench_event_handler(_, _) ->
|
||||
woody_event_handler:format_event(
|
||||
'call service',
|
||||
#{args =>
|
||||
[{mg_stateproc_CallArgs,
|
||||
-type input() :: {woody_thrift_formatter:event_meta(), woody:rpc_id()}.
|
||||
|
||||
-spec input() ->
|
||||
input().
|
||||
input() ->
|
||||
Meta = #{
|
||||
args => [{mg_stateproc_CallArgs,
|
||||
{bin,
|
||||
<<131, 104, 4, 100, 0, 11, 116, 104, 114, 105, 102, 116, 95, 99, 97, 108, 108,
|
||||
100, 0, 16, 112, 97, 114, 116, 121, 95, 109, 97, 110, 97, 103, 101, 109, 101,
|
||||
110, 116, 104, 2, 100, 0, 15, 80, 97, 114, 116, 121, 77, 97, 110, 97, 103,
|
||||
101, 109, 101, 110, 116, 100, 0, 11, 67, 114, 101, 97, 116, 101, 67, 108, 97,
|
||||
105, 109, 109, 0, 0, 2, 145, 11, 0, 2, 0, 0, 0, 11, 49, 67, 83, 72, 84, 104, 84,
|
||||
69, 74, 56, 52, 15, 0, 3, 12, 0, 0, 0, 4, 12, 0, 4, 11, 0, 1, 0, 0, 0, 11, 49, 67,
|
||||
83, 72, 84, 106, 75, 108, 51, 52, 75, 12, 0, 2, 12, 0, 1, 12, 0, 2, 8, 0, 1, 0, 0,
|
||||
0, 1, 0, 12, 0, 3, 8, 0, 1, 0, 0, 0, 1, 0, 12, 0, 1, 12, 0, 1, 12, 0, 1, 11, 0, 1, 0, 0,
|
||||
0, 18, 72, 111, 111, 102, 115, 32, 38, 32, 72, 111, 114, 110, 115, 32, 79, 74,
|
||||
83, 67, 11, 0, 2, 0, 0, 0, 10, 49, 50, 51, 52, 53, 48, 57, 56, 55, 54, 11, 0, 3, 0,
|
||||
0, 0, 13, 49, 50, 49, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 11, 0, 4, 0, 0, 0,
|
||||
48, 78, 101, 122, 97, 104, 117, 97, 108, 99, 111, 121, 111, 116, 108, 32, 49,
|
||||
48, 57, 32, 80, 105, 115, 111, 32, 56, 44, 32, 67, 101, 110, 116, 114, 111,
|
||||
44, 32, 48, 54, 48, 56, 50, 44, 32, 77, 69, 88, 73, 67, 79, 11, 0, 5, 0, 0, 0, 3,
|
||||
78, 97, 78, 11, 0, 6, 0, 0, 0, 8, 68, 105, 114, 101, 99, 116, 111, 114, 11, 0, 7,
|
||||
0, 0, 0, 7, 83, 111, 109, 101, 111, 110, 101, 11, 0, 8, 0, 0, 0, 13, 49, 48, 48,
|
||||
36, 32, 98, 97, 110, 107, 110, 111, 116, 101, 12, 0, 9, 11, 0, 1, 0, 0, 0, 19,
|
||||
52, 50, 55, 54, 51, 48, 48, 48, 49, 48, 57, 48, 56, 51, 49, 50, 56, 57, 51, 11,
|
||||
0, 2, 0, 0, 0, 8, 83, 111, 109, 101, 66, 97, 110, 107, 11, 0, 3, 0, 0, 0, 9, 49,
|
||||
50, 51, 49, 50, 57, 56, 55, 54, 11, 0, 4, 0, 0, 0, 8, 54, 54, 54, 52, 50, 54, 54,
|
||||
54, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 4, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106,
|
||||
75, 108, 51, 52, 75, 12, 0, 2, 12, 0, 4, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84,
|
||||
106, 75, 108, 51, 52, 76, 12, 0, 2, 12, 0, 1, 12, 0, 1, 11, 0, 1, 0, 0, 0, 3, 82,
|
||||
85, 66, 0, 12, 0, 2, 12, 0, 1, 11, 0, 1, 0, 0, 0, 19, 52, 50, 55, 54, 51, 48, 48,
|
||||
48, 49, 48, 57, 48, 56, 51, 49, 50, 56, 57, 51, 11, 0, 2, 0, 0, 0, 8, 83, 111,
|
||||
109, 101, 66, 97, 110, 107, 11, 0, 3, 0, 0, 0, 9, 49, 50, 51, 49, 50, 57, 56, 55,
|
||||
54, 11, 0, 4, 0, 0, 0, 8, 54, 54, 54, 52, 50, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 12,
|
||||
0, 6, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106, 75, 108, 51, 52, 77, 12, 0,
|
||||
2, 12, 0, 5, 12, 0, 1, 8, 0, 1, 0, 0, 0, 1, 0, 12, 0, 6, 11, 0, 1, 0, 0, 0, 0, 0, 12, 0,
|
||||
2, 11, 0, 1, 0, 0, 0, 17, 66, 97, 116, 116, 108, 101, 32, 82, 101, 97, 100, 121,
|
||||
32, 83, 104, 111, 112, 0, 11, 0, 3, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106, 75,
|
||||
108, 51, 52, 75, 11, 0, 4, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106, 75, 108, 51, 52,
|
||||
76, 0, 0, 0, 0, 12, 0, 6, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106, 75, 108,
|
||||
51, 52, 77, 12, 0, 2, 12, 0, 12, 12, 0, 1, 11, 0, 1, 0, 0, 0, 3, 82, 85, 66, 0, 0, 0,
|
||||
0, 0, 0>>},
|
||||
{mg_stateproc_Machine, <<"party">>, <<"1CSHThTEJ84">>,
|
||||
[{mg_stateproc_Event, 1, <<"2019-08-13T07:52:11.080519Z">>,
|
||||
undefined,
|
||||
{arr,
|
||||
[{obj,
|
||||
#{{str, <<"ct">>} =>
|
||||
{str, <<"application/x-erlang-binary">>},
|
||||
{str, <<"vsn">>} => {i, 6}}},
|
||||
{bin,
|
||||
<<131, 104, 2, 100, 0, 13, 112, 97, 114, 116, 121, 95,
|
||||
99, 104, 97, 110, 103, 101, 115, 108, 0, 0, 0, 2, 104,
|
||||
2, 100, 0, 13, 112, 97, 114, 116, 121, 95, 99, 114,
|
||||
101, 97, 116, 101, 100, 104, 4, 100, 0, 20, 112, 97,
|
||||
121, 112, 114, 111, 99, 95, 80, 97, 114, 116, 121, 67,
|
||||
114, 101, 97, 116, 101, 100, 109, 0, 0, 0, 11, 49, 67,
|
||||
83, 72, 84, 104, 84, 69, 74, 56, 52, 104, 2, 100, 0, 23,
|
||||
100, 111, 109, 97, 105, 110, 95, 80, 97, 114, 116,
|
||||
121, 67, 111, 110, 116, 97, 99, 116, 73, 110, 102,
|
||||
111, 109, 0, 0, 0, 12, 104, 103, 95, 99, 116, 95, 104,
|
||||
101, 108, 112, 101, 114, 109, 0, 0, 0, 27, 50, 48, 49,
|
||||
57, 45, 48, 56, 45, 49, 51, 84, 48, 55, 58, 53, 50, 58,
|
||||
49, 49, 46, 48, 55, 50, 56, 51, 53, 90, 104, 2, 100, 0,
|
||||
16, 114, 101, 118, 105, 115, 105, 111, 110, 95, 99,
|
||||
104, 97, 110, 103, 101, 100, 104, 3, 100, 0, 28, 112,
|
||||
97, 121, 112, 114, 111, 99, 95, 80, 97, 114, 116, 121,
|
||||
82, 101, 118, 105, 115, 105, 111, 110, 67, 104, 97,
|
||||
110, 103, 101, 100, 109, 0, 0, 0, 27, 50, 48, 49, 57,
|
||||
45, 48, 56, 45, 49, 51, 84, 48, 55, 58, 53, 50, 58, 49,
|
||||
49, 46, 48, 55, 50, 56, 51, 53, 90, 97, 0, 106>>}]}}],
|
||||
{mg_stateproc_HistoryRange, undefined, 10, backward},
|
||||
{mg_stateproc_Content, undefined,
|
||||
{obj,
|
||||
#{{str, <<"aux_state">>} =>
|
||||
{bin,
|
||||
<<131, 116, 0, 0, 0, 2, 100, 0, 20, 112, 97, 114, 116,
|
||||
121, 95, 114, 101, 118, 105, 115, 105, 111, 110, 95,
|
||||
105, 110, 100, 101, 120, 116, 0, 0, 0, 0, 100, 0, 14,
|
||||
115, 110, 97, 112, 115, 104, 111, 116, 95, 105, 110,
|
||||
100, 101, 120, 106>>},
|
||||
{str, <<"ct">>} => {str, <<"application/x-erlang-binary">>}}}},
|
||||
100, 0, 16, 112, 97, 114, 116, 121, 95, 109, 97, 110, 97, 103, 101, 109, 101,
|
||||
110, 116, 104, 2, 100, 0, 15, 80, 97, 114, 116, 121, 77, 97, 110, 97, 103,
|
||||
101, 109, 101, 110, 116, 100, 0, 11, 67, 114, 101, 97, 116, 101, 67, 108, 97,
|
||||
105, 109, 109, 0, 0, 2, 145, 11, 0, 2, 0, 0, 0, 11, 49, 67, 83, 72, 84, 104, 84,
|
||||
69, 74, 56, 52, 15, 0, 3, 12, 0, 0, 0, 4, 12, 0, 4, 11, 0, 1, 0, 0, 0, 11, 49, 67,
|
||||
83, 72, 84, 106, 75, 108, 51, 52, 75, 12, 0, 2, 12, 0, 1, 12, 0, 2, 8, 0, 1, 0, 0,
|
||||
0, 1, 0, 12, 0, 3, 8, 0, 1, 0, 0, 0, 1, 0, 12, 0, 1, 12, 0, 1, 12, 0, 1, 11, 0, 1, 0, 0,
|
||||
0, 18, 72, 111, 111, 102, 115, 32, 38, 32, 72, 111, 114, 110, 115, 32, 79, 74,
|
||||
83, 67, 11, 0, 2, 0, 0, 0, 10, 49, 50, 51, 52, 53, 48, 57, 56, 55, 54, 11, 0, 3, 0,
|
||||
0, 0, 13, 49, 50, 49, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 11, 0, 4, 0, 0, 0,
|
||||
48, 78, 101, 122, 97, 104, 117, 97, 108, 99, 111, 121, 111, 116, 108, 32, 49,
|
||||
48, 57, 32, 80, 105, 115, 111, 32, 56, 44, 32, 67, 101, 110, 116, 114, 111,
|
||||
44, 32, 48, 54, 48, 56, 50, 44, 32, 77, 69, 88, 73, 67, 79, 11, 0, 5, 0, 0, 0, 3,
|
||||
78, 97, 78, 11, 0, 6, 0, 0, 0, 8, 68, 105, 114, 101, 99, 116, 111, 114, 11, 0, 7,
|
||||
0, 0, 0, 7, 83, 111, 109, 101, 111, 110, 101, 11, 0, 8, 0, 0, 0, 13, 49, 48, 48,
|
||||
36, 32, 98, 97, 110, 107, 110, 111, 116, 101, 12, 0, 9, 11, 0, 1, 0, 0, 0, 19,
|
||||
52, 50, 55, 54, 51, 48, 48, 48, 49, 48, 57, 48, 56, 51, 49, 50, 56, 57, 51, 11,
|
||||
0, 2, 0, 0, 0, 8, 83, 111, 109, 101, 66, 97, 110, 107, 11, 0, 3, 0, 0, 0, 9, 49,
|
||||
50, 51, 49, 50, 57, 56, 55, 54, 11, 0, 4, 0, 0, 0, 8, 54, 54, 54, 52, 50, 54, 54,
|
||||
54, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 4, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106,
|
||||
75, 108, 51, 52, 75, 12, 0, 2, 12, 0, 4, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84,
|
||||
106, 75, 108, 51, 52, 76, 12, 0, 2, 12, 0, 1, 12, 0, 1, 11, 0, 1, 0, 0, 0, 3, 82,
|
||||
85, 66, 0, 12, 0, 2, 12, 0, 1, 11, 0, 1, 0, 0, 0, 19, 52, 50, 55, 54, 51, 48, 48,
|
||||
48, 49, 48, 57, 48, 56, 51, 49, 50, 56, 57, 51, 11, 0, 2, 0, 0, 0, 8, 83, 111,
|
||||
109, 101, 66, 97, 110, 107, 11, 0, 3, 0, 0, 0, 9, 49, 50, 51, 49, 50, 57, 56, 55,
|
||||
54, 11, 0, 4, 0, 0, 0, 8, 54, 54, 54, 52, 50, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 12,
|
||||
0, 6, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106, 75, 108, 51, 52, 77, 12, 0,
|
||||
2, 12, 0, 5, 12, 0, 1, 8, 0, 1, 0, 0, 0, 1, 0, 12, 0, 6, 11, 0, 1, 0, 0, 0, 0, 0, 12, 0,
|
||||
2, 11, 0, 1, 0, 0, 0, 17, 66, 97, 116, 116, 108, 101, 32, 82, 101, 97, 100, 121,
|
||||
32, 83, 104, 111, 112, 0, 11, 0, 3, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106, 75,
|
||||
108, 51, 52, 75, 11, 0, 4, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106, 75, 108, 51, 52,
|
||||
76, 0, 0, 0, 0, 12, 0, 6, 11, 0, 1, 0, 0, 0, 11, 49, 67, 83, 72, 84, 106, 75, 108,
|
||||
51, 52, 77, 12, 0, 2, 12, 0, 12, 12, 0, 1, 11, 0, 1, 0, 0, 0, 3, 82, 85, 66, 0, 0, 0,
|
||||
0, 0, 0>>},
|
||||
{mg_stateproc_Machine, <<"party">>, <<"1CSHThTEJ84">>,
|
||||
[{mg_stateproc_Event, 1, <<"2019-08-13T07:52:11.080519Z">>,
|
||||
undefined,
|
||||
{arr,
|
||||
[{obj,
|
||||
#{{str, <<"ct">>} =>
|
||||
{str, <<"application/x-erlang-binary">>},
|
||||
{str, <<"vsn">>} => {i, 6}}},
|
||||
{bin,
|
||||
<<131, 104, 2, 100, 0, 13, 112, 97, 114, 116, 121, 95,
|
||||
99, 104, 97, 110, 103, 101, 115, 108, 0, 0, 0, 2, 104,
|
||||
2, 100, 0, 13, 112, 97, 114, 116, 121, 95, 99, 114,
|
||||
101, 97, 116, 101, 100, 104, 4, 100, 0, 20, 112, 97,
|
||||
121, 112, 114, 111, 99, 95, 80, 97, 114, 116, 121, 67,
|
||||
114, 101, 97, 116, 101, 100, 109, 0, 0, 0, 11, 49, 67,
|
||||
83, 72, 84, 104, 84, 69, 74, 56, 52, 104, 2, 100, 0, 23,
|
||||
100, 111, 109, 97, 105, 110, 95, 80, 97, 114, 116,
|
||||
121, 67, 111, 110, 116, 97, 99, 116, 73, 110, 102,
|
||||
111, 109, 0, 0, 0, 12, 104, 103, 95, 99, 116, 95, 104,
|
||||
101, 108, 112, 101, 114, 109, 0, 0, 0, 27, 50, 48, 49,
|
||||
57, 45, 48, 56, 45, 49, 51, 84, 48, 55, 58, 53, 50, 58,
|
||||
49, 49, 46, 48, 55, 50, 56, 51, 53, 90, 104, 2, 100, 0,
|
||||
16, 114, 101, 118, 105, 115, 105, 111, 110, 95, 99,
|
||||
104, 97, 110, 103, 101, 100, 104, 3, 100, 0, 28, 112,
|
||||
97, 121, 112, 114, 111, 99, 95, 80, 97, 114, 116, 121,
|
||||
82, 101, 118, 105, 115, 105, 111, 110, 67, 104, 97,
|
||||
110, 103, 101, 100, 109, 0, 0, 0, 27, 50, 48, 49, 57,
|
||||
45, 48, 56, 45, 49, 51, 84, 48, 55, 58, 53, 50, 58, 49,
|
||||
49, 46, 48, 55, 50, 56, 51, 53, 90, 97, 0, 106>>}]}}],
|
||||
{mg_stateproc_HistoryRange, undefined, 10, backward},
|
||||
{mg_stateproc_Content, undefined,
|
||||
{obj,
|
||||
#{{str, <<"aux_state">>} =>
|
||||
{bin,
|
||||
<<131, 116, 0, 0, 0, 2, 100, 0, 20, 112, 97, 114, 116, 121, 95,
|
||||
114, 101, 118, 105, 115, 105, 111, 110, 95, 105, 110, 100,
|
||||
101, 120, 116, 0, 0, 0, 0, 100, 0, 14, 115, 110, 97, 112,
|
||||
115, 104, 111, 116, 95, 105, 110, 100, 101, 120, 106>>},
|
||||
{str, <<"ct">>} =>
|
||||
{str, <<"application/x-erlang-binary">>}}}}}],
|
||||
deadline => {{{2019, 8, 13}, {7, 52, 41}}, 105},
|
||||
execution_start_time => 1565682731109,
|
||||
function => 'ProcessCall',
|
||||
metadata =>
|
||||
#{<<"user-identity.id">> => <<"1CSHThTEJ84">>,
|
||||
<<"user-identity.realm">> => <<"external">>},
|
||||
role => server,
|
||||
service => 'Processor',
|
||||
service_schema => {mg_proto_state_processing_thrift, 'Processor'},
|
||||
type => call},
|
||||
#{
|
||||
span_id => <<"1012689088534282240">>,
|
||||
trace_id => <<"1012689088739803136">>,
|
||||
parent_id => <<"1012689108264288256">>}
|
||||
).
|
||||
<<131, 116, 0, 0, 0, 2, 100, 0, 20, 112, 97, 114, 116,
|
||||
121, 95, 114, 101, 118, 105, 115, 105, 111, 110, 95,
|
||||
105, 110, 100, 101, 120, 116, 0, 0, 0, 0, 100, 0, 14,
|
||||
115, 110, 97, 112, 115, 104, 111, 116, 95, 105, 110,
|
||||
100, 101, 120, 106>>},
|
||||
{str, <<"ct">>} => {str, <<"application/x-erlang-binary">>}}}},
|
||||
undefined,
|
||||
{obj,
|
||||
#{{str, <<"aux_state">>} =>
|
||||
{bin,
|
||||
<<131, 116, 0, 0, 0, 2, 100, 0, 20, 112, 97, 114, 116, 121, 95,
|
||||
114, 101, 118, 105, 115, 105, 111, 110, 95, 105, 110, 100,
|
||||
101, 120, 116, 0, 0, 0, 0, 100, 0, 14, 115, 110, 97, 112,
|
||||
115, 104, 111, 116, 95, 105, 110, 100, 101, 120, 106>>},
|
||||
{str, <<"ct">>} =>
|
||||
{str, <<"application/x-erlang-binary">>}}}}}],
|
||||
deadline => {{{2019, 8, 13}, {7, 52, 41}}, 105},
|
||||
execution_start_time => 1565682731109,
|
||||
function => 'ProcessCall',
|
||||
metadata =>
|
||||
#{<<"user-identity.id">> => <<"1CSHThTEJ84">>,
|
||||
<<"user-identity.realm">> => <<"external">>},
|
||||
role => server,
|
||||
service => 'Processor',
|
||||
service_schema => {mg_proto_state_processing_thrift, 'Processor'},
|
||||
type => call
|
||||
},
|
||||
RpcID = #{
|
||||
span_id => <<"1012689088534282240">>,
|
||||
trace_id => <<"1012689088739803136">>,
|
||||
parent_id => <<"1012689108264288256">>
|
||||
},
|
||||
{Meta, RpcID}.
|
||||
|
||||
-spec iolib_formatter({input, _State}) ->
|
||||
input().
|
||||
iolib_formatter({input, _}) ->
|
||||
input().
|
||||
|
||||
-spec thrift_formatter({input, _State}) ->
|
||||
input().
|
||||
thrift_formatter({input, _}) ->
|
||||
input().
|
||||
|
||||
-spec bench_iolib_formatter(input(), _State) ->
|
||||
term().
|
||||
bench_iolib_formatter({Meta, RpcID}, _) ->
|
||||
format_msg(format_event_iolib(Meta, RpcID)).
|
||||
|
||||
format_event_iolib(#{service := Service, function := Function, args := Args}, _RpcID) ->
|
||||
{info, {"calling ~0p:~0p(~0tp)", [Service, Function, Args]}}.
|
||||
|
||||
-spec bench_thrift_formatter(input(), _State) ->
|
||||
term().
|
||||
bench_thrift_formatter({Meta, RpcID}, _) ->
|
||||
format_msg(woody_event_handler:format_event('call service', Meta, RpcID)).
|
||||
|
||||
format_msg({_Severity, {Format, Params}}) ->
|
||||
io_lib:format(Format, Params).
|
||||
|
45
test/bench_woody_formatter.erl
Normal file
45
test/bench_woody_formatter.erl
Normal file
@ -0,0 +1,45 @@
|
||||
-module(bench_woody_formatter).
|
||||
|
||||
%% API
|
||||
-export([
|
||||
iolib_formatter/1,
|
||||
bench_iolib_formatter/2,
|
||||
thrift_formatter/1,
|
||||
bench_thrift_formatter/2
|
||||
]).
|
||||
|
||||
-type input() :: term().
|
||||
|
||||
-spec input() ->
|
||||
input().
|
||||
input() ->
|
||||
%% NOTE
|
||||
%% You will need some reasonably complex term following `domain_config.Snapshot` thrift schema
|
||||
%% stored in ETF in `test/snapshot.term` for this benchmark to run. It's NOT supplied by
|
||||
%% default.
|
||||
{ok, Bin} = file:read_file("test/snapshot.term"),
|
||||
erlang:binary_to_term(Bin).
|
||||
|
||||
-spec iolib_formatter({input, _State}) ->
|
||||
input().
|
||||
iolib_formatter({input, _}) ->
|
||||
input().
|
||||
|
||||
-spec thrift_formatter({input, _State}) ->
|
||||
input().
|
||||
thrift_formatter({input, _}) ->
|
||||
input().
|
||||
|
||||
-spec bench_iolib_formatter(input(), _State) ->
|
||||
term().
|
||||
bench_iolib_formatter(Snapshot, _) ->
|
||||
format_msg({"~0tp", [Snapshot]}).
|
||||
|
||||
-spec bench_thrift_formatter(input(), _State) ->
|
||||
term().
|
||||
bench_thrift_formatter(Snapshot, _) ->
|
||||
Service = dmsl_domain_config_thrift,
|
||||
format_msg(woody_event_formatter:format_reply(Service, 'Repository', 'Checkout', Snapshot, #{})).
|
||||
|
||||
format_msg({Format, Params}) ->
|
||||
io_lib:format(Format, Params).
|
60
test/benchmark_memory_pressure.erl
Normal file
60
test/benchmark_memory_pressure.erl
Normal file
@ -0,0 +1,60 @@
|
||||
-module(benchmark_memory_pressure).
|
||||
|
||||
-export([run/0]).
|
||||
|
||||
-spec run() ->
|
||||
ok.
|
||||
run() ->
|
||||
Input = input(),
|
||||
Opts = #{iterations => 10},
|
||||
_ = run(iolib, mk_iolib_runner(Input), Opts),
|
||||
_ = run(thrift, mk_thrift_runner(Input), Opts),
|
||||
ok.
|
||||
|
||||
-spec run(atom(), meter_memory_pressure:runner(), meter_memory_pressure:opts()) ->
|
||||
ok.
|
||||
run(Name, Runner, Opts) ->
|
||||
_ = io:format("Benchmarking '~s' memory pressure...~n", [Name]),
|
||||
_ = io:format("====================================~n", []),
|
||||
Metrics = meter_memory_pressure:measure(Runner, Opts),
|
||||
lists:foreach(
|
||||
fun (Metric) ->
|
||||
io:format("~24s = ~-16b~n", [Metric, maps:get(Metric, Metrics)])
|
||||
end,
|
||||
[
|
||||
minor_gcs,
|
||||
minor_gcs_duration,
|
||||
major_gcs,
|
||||
major_gcs_duration,
|
||||
heap_reclaimed,
|
||||
offheap_bin_reclaimed,
|
||||
stack_min,
|
||||
stack_max
|
||||
]
|
||||
),
|
||||
_ = io:format("====================================~n~n", []),
|
||||
ok.
|
||||
|
||||
-spec input() ->
|
||||
term().
|
||||
input() ->
|
||||
%% NOTE
|
||||
%% You will need some reasonably complex term following `domain_config.Snapshot` thrift schema
|
||||
%% stored in ETF in `test/snapshot.term` for this benchmark to run. It's NOT supplied by
|
||||
%% default.
|
||||
{ok, Binary} = file:read_file("test/snapshot.term"),
|
||||
erlang:binary_to_term(Binary).
|
||||
|
||||
-spec mk_iolib_runner(term()) ->
|
||||
meter_memory_pressure:runner().
|
||||
mk_iolib_runner(Snapshot) ->
|
||||
fun () ->
|
||||
bench_woody_formatter:bench_iolib_formatter(Snapshot, [])
|
||||
end.
|
||||
|
||||
-spec mk_thrift_runner(term()) ->
|
||||
meter_memory_pressure:runner().
|
||||
mk_thrift_runner(Snapshot) ->
|
||||
fun () ->
|
||||
bench_woody_formatter:bench_thrift_formatter(Snapshot, [])
|
||||
end.
|
220
test/meter_memory_pressure.erl
Normal file
220
test/meter_memory_pressure.erl
Normal file
@ -0,0 +1,220 @@
|
||||
-module(meter_memory_pressure).
|
||||
|
||||
-type microseconds() :: non_neg_integer().
|
||||
-type words() :: non_neg_integer().
|
||||
-type metrics() :: #{
|
||||
minor_gcs := non_neg_integer(),
|
||||
minor_gcs_duration := microseconds(),
|
||||
major_gcs := non_neg_integer(),
|
||||
minor_gcs_duration := microseconds(),
|
||||
heap_reclaimed := words(),
|
||||
offheap_bin_reclaimed := words(),
|
||||
stack_min := words(),
|
||||
stack_max := words()
|
||||
}.
|
||||
|
||||
-export([measure/2]).
|
||||
-export([export/3]).
|
||||
|
||||
-export_type([metrics/0]).
|
||||
|
||||
%%
|
||||
|
||||
-type runner() :: fun(() -> _).
|
||||
-type opts() :: #{
|
||||
iterations => pos_integer(),
|
||||
spawn_opts => [{atom(), _}],
|
||||
dump_traces => file:filename()
|
||||
}.
|
||||
|
||||
-export_type([runner/0]).
|
||||
-export_type([opts/0]).
|
||||
|
||||
-spec measure(runner(), opts()) ->
|
||||
metrics().
|
||||
measure(Runner, Opts0) ->
|
||||
Opts = maps:merge(get_default_opts(), Opts0),
|
||||
Token = make_ref(),
|
||||
Tracer = start_tracer(Token, Opts),
|
||||
ok = run(Runner, Tracer, Opts),
|
||||
Metrics = collect_metrics(Tracer, Token),
|
||||
Metrics.
|
||||
|
||||
get_default_opts() ->
|
||||
#{
|
||||
iterations => 100,
|
||||
spawn_opts => [{fullsweep_after, 0}]
|
||||
}.
|
||||
|
||||
run(Runner, Tracer, Opts) ->
|
||||
SpawnOpts = [monitor, {priority, high}] ++ maps:get(spawn_opts, Opts),
|
||||
{Staging, MRef} = erlang:spawn_opt(
|
||||
fun () -> run_staging(Runner, Tracer, Opts) end,
|
||||
SpawnOpts
|
||||
),
|
||||
receive
|
||||
{'DOWN', MRef, process, Staging, normal} ->
|
||||
ok
|
||||
end.
|
||||
|
||||
run_staging(Runner, Tracer, Opts) ->
|
||||
N = maps:get(iterations, Opts),
|
||||
TraceOpts = [garbage_collection, timestamp, {tracer, Tracer}],
|
||||
_ = erlang:trace(self(), true, TraceOpts),
|
||||
iterate(Runner, N).
|
||||
|
||||
iterate(Runner, N) when N > 0 ->
|
||||
_ = Runner(),
|
||||
iterate(Runner, N - 1);
|
||||
iterate(_Runner, 0) ->
|
||||
ok.
|
||||
|
||||
%%
|
||||
|
||||
start_tracer(Token, Opts) ->
|
||||
Self = self(),
|
||||
erlang:spawn_link(fun () -> run_tracer(Self, Token, Opts) end).
|
||||
|
||||
collect_metrics(Tracer, Token) ->
|
||||
_ = Tracer ! Token,
|
||||
receive
|
||||
{?MODULE, {metrics, Metrics}} ->
|
||||
Metrics
|
||||
end.
|
||||
|
||||
run_tracer(MeterPid, Token, Opts) ->
|
||||
_ = receive Token -> ok end,
|
||||
Traces = collect_traces(),
|
||||
Metrics = analyze_traces(Traces),
|
||||
ok = maybe_dump_traces(Traces, Opts),
|
||||
MeterPid ! {?MODULE, {metrics, Metrics}}.
|
||||
|
||||
collect_traces() ->
|
||||
collect_traces([]).
|
||||
collect_traces(Acc) ->
|
||||
receive
|
||||
{trace_ts, _Pid, Trace, Info, Clock} ->
|
||||
collect_traces([{Trace, Info, Clock} | Acc]);
|
||||
Unexpected ->
|
||||
error({unexpected, Unexpected})
|
||||
after
|
||||
0 ->
|
||||
lists:reverse(Acc)
|
||||
end.
|
||||
|
||||
maybe_dump_traces(Traces, #{dump_traces := Filename}) ->
|
||||
file:write_file(Filename, erlang:term_to_binary(Traces));
|
||||
maybe_dump_traces(_, #{}) ->
|
||||
ok.
|
||||
|
||||
analyze_traces(Traces) ->
|
||||
analyze_traces(Traces, #{
|
||||
minor_gcs => 0,
|
||||
minor_gcs_duration => 0,
|
||||
major_gcs => 0,
|
||||
major_gcs_duration => 0,
|
||||
heap_reclaimed => 0,
|
||||
offheap_bin_reclaimed => 0
|
||||
}).
|
||||
|
||||
analyze_traces([{gc_minor_start, InfoStart, C1}, {gc_minor_end, InfoEnd, C2} | Rest], M0) ->
|
||||
M1 = increment(minor_gcs, M0),
|
||||
M2 = increment(minor_gcs_duration, timer:now_diff(C2, C1), M1),
|
||||
analyze_traces(Rest, analyze_gc(InfoStart, InfoEnd, M2));
|
||||
analyze_traces([{gc_major_start, InfoStart, C1}, {gc_major_end, InfoEnd, C2} | Rest], M0) ->
|
||||
M1 = increment(major_gcs, M0),
|
||||
M2 = increment(major_gcs_duration, timer:now_diff(C2, C1), M1),
|
||||
analyze_traces(Rest, analyze_gc(InfoStart, InfoEnd, M2));
|
||||
analyze_traces([], M) ->
|
||||
M.
|
||||
|
||||
analyze_gc(InfoStart, InfoEnd, M0) ->
|
||||
M1 = increment(heap_reclaimed, difference(heap_size, InfoEnd, InfoStart), M0),
|
||||
M2 = increment(offheap_bin_reclaimed, difference(bin_vheap_size, InfoEnd, InfoStart), M1),
|
||||
M3 = update(stack_min, fun erlang:min/2, min(stack_size, InfoStart, InfoEnd), M2),
|
||||
M4 = update(stack_max, fun erlang:max/2, max(stack_size, InfoStart, InfoEnd), M3),
|
||||
M4.
|
||||
|
||||
difference(Name, Info1, Info2) ->
|
||||
combine(Name, fun (V1, V2) -> erlang:max(0, V2 - V1) end, Info1, Info2).
|
||||
|
||||
min(Name, Info1, Info2) ->
|
||||
combine(Name, fun erlang:min/2, Info1, Info2).
|
||||
|
||||
max(Name, Info1, Info2) ->
|
||||
combine(Name, fun erlang:max/2, Info1, Info2).
|
||||
|
||||
combine(Name, Fun, Info1, Info2) ->
|
||||
{_Name, V1} = lists:keyfind(Name, 1, Info1),
|
||||
{_Name, V2} = lists:keyfind(Name, 1, Info2),
|
||||
Fun(V1, V2).
|
||||
|
||||
increment(Name, Metrics) ->
|
||||
increment(Name, 1, Metrics).
|
||||
|
||||
increment(Name, Delta, Metrics) ->
|
||||
maps:update_with(Name, fun (V) -> V + Delta end, Metrics).
|
||||
|
||||
update(Name, Fun, I, Metrics) ->
|
||||
maps:update_with(Name, fun (V) -> Fun(V, I) end, I, Metrics).
|
||||
|
||||
%%
|
||||
|
||||
-spec export(file:filename(), file:filename(), csv) -> ok.
|
||||
|
||||
export(FilenameIn, FilenameOut, Format) ->
|
||||
{ok, Content} = file:read_file(FilenameIn),
|
||||
Traces = erlang:binary_to_term(Content),
|
||||
{ok, FileOut} = file:open(FilenameOut, [write, binary]),
|
||||
ok = format_traces(Traces, Format, FileOut),
|
||||
ok = file:close(FileOut).
|
||||
|
||||
format_traces(Traces, csv, FileOut) ->
|
||||
_ = format_csv_header(FileOut),
|
||||
_ = lists:foreach(fun (T) -> format_csv_trace(T, FileOut) end, Traces),
|
||||
ok.
|
||||
|
||||
format_csv_header(Out) ->
|
||||
Line = " ~s , ~s , ~s , ~s , ~s , ~s , ~s , ~s , ~s , ~s , ~s , ~s ~n",
|
||||
io:fwrite(Out, Line, [
|
||||
"Time",
|
||||
"End?",
|
||||
"Major?",
|
||||
"Stack",
|
||||
"Heap",
|
||||
"HeapBlock",
|
||||
"BinHeap",
|
||||
"BinHeapBlock",
|
||||
"OldHeap",
|
||||
"OldHeapBlock",
|
||||
"OldBinHeap",
|
||||
"OldBinHeapBlock"
|
||||
]).
|
||||
|
||||
format_csv_trace({Event, Info, Clock}, Out) ->
|
||||
Line = " ~B , ~B , ~B , ~B , ~B , ~B , ~B , ~B , ~B , ~B , ~B , ~B ~n",
|
||||
io:fwrite(Out, Line, [
|
||||
clock_to_mcs(Clock),
|
||||
bool_to_integer(lists:member(Event, [gc_minor_end, gc_major_end])),
|
||||
bool_to_integer(lists:member(Event, [gc_major_start, gc_major_end])),
|
||||
get_info(stack_size, Info),
|
||||
get_info(heap_size, Info),
|
||||
get_info(heap_block_size, Info),
|
||||
get_info(bin_vheap_size, Info),
|
||||
get_info(bin_vheap_block_size, Info),
|
||||
get_info(old_heap_size, Info),
|
||||
get_info(old_heap_block_size, Info),
|
||||
get_info(bin_old_vheap_size, Info),
|
||||
get_info(bin_old_vheap_block_size, Info)
|
||||
]).
|
||||
|
||||
get_info(Name, Info) ->
|
||||
{_Name, V} = lists:keyfind(Name, 1, Info), V.
|
||||
|
||||
clock_to_mcs({MSec, Sec, USec}) ->
|
||||
(MSec * 1000000 + Sec) * 1000000 + USec.
|
||||
|
||||
bool_to_integer(false) ->
|
||||
0;
|
||||
bool_to_integer(true) ->
|
||||
1.
|
@ -1,6 +1,7 @@
|
||||
-module(woody_tests_SUITE).
|
||||
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("stdlib/include/assert.hrl").
|
||||
-include_lib("hackney/include/hackney_lib.hrl").
|
||||
|
||||
-include("woody_test_thrift.hrl").
|
||||
@ -617,10 +618,10 @@ call_ok_test(_) ->
|
||||
|
||||
call_resolver_nxdomain(_) ->
|
||||
Context = make_context(<<"nxdomain">>),
|
||||
try call(Context, 'The Void', get_weapon, [<<"Enforcer">>, self_to_bin()])
|
||||
catch
|
||||
error:{woody_error, {internal, resource_unavailable, <<"{resolve_failed,nxdomain}">>}} -> ok
|
||||
end.
|
||||
?assertError(
|
||||
{woody_error, {internal, resource_unavailable, <<"{resolve_failed,nxdomain}">>}},
|
||||
call(Context, 'The Void', get_weapon, [<<"Enforcer">>, self_to_bin()])
|
||||
).
|
||||
|
||||
call3_ok_test(_) ->
|
||||
{Url, Service} = get_service_endpoint('Weapons'),
|
||||
@ -647,37 +648,35 @@ call_throw_unexpected_test(_) ->
|
||||
Id = <<"call_throw_unexpected">>,
|
||||
Current = genlib_map:get(<<"Rocket Launcher">>, ?WEAPONS),
|
||||
Context = make_context(Id),
|
||||
try call(Context, 'Weapons', switch_weapon, [Current, next, 1, self_to_bin()])
|
||||
catch
|
||||
error:{woody_error, {external, result_unexpected, _}} -> ok
|
||||
end,
|
||||
{ok, _} = receive_msg(Current, Context).
|
||||
?assertError(
|
||||
{woody_error, {external, result_unexpected, _}},
|
||||
call(Context, 'Weapons', switch_weapon, [Current, next, 1, self_to_bin()])
|
||||
).
|
||||
|
||||
call_system_external_error_test(_) ->
|
||||
Id = <<"call_system_external_error">>,
|
||||
Gun = <<"The Ultimate Super Mega Destroyer">>,
|
||||
Context = make_context(Id),
|
||||
try call(Context, 'Weapons', get_weapon, [Gun, self_to_bin()])
|
||||
catch
|
||||
error:{woody_error, {external, result_unexpected, _}} -> ok
|
||||
end,
|
||||
{ok, _} = receive_msg(Gun, Context).
|
||||
?assertError(
|
||||
{woody_error, {external, result_unexpected, _}},
|
||||
call(Context, 'Weapons', get_weapon, [Gun, self_to_bin()])
|
||||
).
|
||||
|
||||
call_client_error_test(_) ->
|
||||
Gun = 'Wrong Type of Mega Destroyer',
|
||||
Context = make_context(<<"call_client_error">>),
|
||||
try call(Context, 'Weapons', get_weapon, [Gun, self_to_bin()])
|
||||
catch
|
||||
error:{woody_error, {internal, result_unexpected, <<"client thrift error: ", _/binary>>}} -> ok
|
||||
end.
|
||||
?assertError(
|
||||
{woody_error, {internal, result_unexpected, <<"client thrift error: ", _/binary>>}},
|
||||
call(Context, 'Weapons', get_weapon, [Gun, self_to_bin()])
|
||||
).
|
||||
|
||||
call_server_internal_error_test(_) ->
|
||||
Armor = <<"Helmet">>,
|
||||
Context = make_context(<<"call_server_internal_error">>),
|
||||
try call(Context, 'Powerups', get_powerup, [Armor, self_to_bin()])
|
||||
catch
|
||||
error:{woody_error, {external, result_unexpected, _}} -> ok
|
||||
end,
|
||||
?assertError(
|
||||
{woody_error, {external, result_unexpected, _}},
|
||||
call(Context, 'Powerups', get_powerup, [Armor, self_to_bin()])
|
||||
),
|
||||
{ok, _} = receive_msg(Armor, Context).
|
||||
|
||||
call_oneway_void_test(_) ->
|
||||
@ -717,21 +716,19 @@ call_pass_thru_except_test(_) ->
|
||||
call_pass_thru_bad_result_test(_) ->
|
||||
Armor = <<"AntiGrav Boots">>,
|
||||
Context = make_context(<<"call_pass_thru_bad_result">>),
|
||||
try call(Context, 'Powerups', bad_proxy_get_powerup, [Armor, self_to_bin()])
|
||||
catch
|
||||
error:{woody_error, {external, result_unexpected, _}} ->
|
||||
ok
|
||||
end,
|
||||
?assertError(
|
||||
{woody_error, {external, result_unexpected, _}},
|
||||
call(Context, 'Powerups', bad_proxy_get_powerup, [Armor, self_to_bin()])
|
||||
),
|
||||
{ok, _} = receive_msg(Armor, Context).
|
||||
|
||||
call_pass_thru_bad_except_test(_) ->
|
||||
Armor = <<"Shield Belt">>,
|
||||
Context = make_context(<<"call_pass_thru_bad_except">>),
|
||||
try call(Context, 'Powerups', bad_proxy_get_powerup, [Armor, self_to_bin()])
|
||||
catch
|
||||
error:{woody_error, {external, result_unexpected, _}} ->
|
||||
ok
|
||||
end,
|
||||
?assertError(
|
||||
{woody_error, {external, result_unexpected, _}},
|
||||
call(Context, 'Powerups', bad_proxy_get_powerup, [Armor, self_to_bin()])
|
||||
),
|
||||
{ok, _} = receive_msg(Armor, Context).
|
||||
|
||||
call_pass_thru_result_unexpected_test(_) ->
|
||||
@ -749,11 +746,11 @@ call_pass_thru_result_unknown_test(_) ->
|
||||
call_pass_thru_error(Id, Powerup, ExceptClass, ErrClass) ->
|
||||
RpcId = woody_context:new_rpc_id(?ROOT_REQ_PARENT_ID, Id, Id),
|
||||
Context = woody_context:new(RpcId),
|
||||
try call(Context, 'Powerups', proxy_get_powerup, [Powerup, self_to_bin()])
|
||||
catch
|
||||
ExceptClass:{woody_error, {external, ErrClass, _}} ->
|
||||
ok
|
||||
end,
|
||||
?assertException(
|
||||
ExceptClass,
|
||||
{woody_error, {external, ErrClass, _}},
|
||||
call(Context, 'Powerups', proxy_get_powerup, [Powerup, self_to_bin()])
|
||||
),
|
||||
{ok, _} = receive_msg(Powerup, Context).
|
||||
|
||||
call_no_headers_404_test(_) ->
|
||||
@ -776,12 +773,11 @@ call_fail_w_no_headers(Id, Class, Code) ->
|
||||
Context = make_context(Id),
|
||||
{Url, Service} = get_service_endpoint('Weapons'),
|
||||
BinCode = integer_to_binary(Code),
|
||||
try woody_client:call({Service, get_weapon, [Gun, self_to_bin()]},
|
||||
?assertError(
|
||||
{woody_error, {external, Class, <<"got response with http code ", BinCode:3/binary, _/binary>>}},
|
||||
woody_client:call({Service, get_weapon, [Gun, self_to_bin()]},
|
||||
#{url => Url, event_handler => ?MODULE}, Context)
|
||||
catch
|
||||
error:{woody_error, {external, Class, <<"got response with http code ", BinCode:3/binary, _/binary>>}} ->
|
||||
ok
|
||||
end.
|
||||
).
|
||||
|
||||
find_multiple_pools_test(_) ->
|
||||
true = is_pid(hackney_pool:find_pool(swords)),
|
||||
@ -824,10 +820,10 @@ call_deadline_reached_on_client_test(_) ->
|
||||
Opts = #{url => Url, event_handler => ?MODULE},
|
||||
Deadline = woody_deadline:from_timeout(0),
|
||||
Context = woody_context:new(Id, #{<<"sleep">> => <<"1000">>}, Deadline),
|
||||
try woody_client:call(Request, Opts, Context)
|
||||
catch
|
||||
error:{woody_error, {internal, resource_unavailable, <<"deadline reached">>}} -> ok
|
||||
end.
|
||||
?assertError(
|
||||
{woody_error, {internal, resource_unavailable, <<"deadline reached">>}},
|
||||
woody_client:call(Request, Opts, Context)
|
||||
).
|
||||
|
||||
call_deadline_timeout_test(_) ->
|
||||
Id = <<"call_deadline_timeout">>,
|
||||
@ -837,16 +833,14 @@ call_deadline_timeout_test(_) ->
|
||||
Opts = #{url => Url, event_handler => ?MODULE},
|
||||
Deadline = woody_deadline:from_timeout(500),
|
||||
Context = woody_context:new(Id, #{<<"sleep">> => <<"3000">>}, Deadline),
|
||||
|
||||
try woody_client:call(Request, Opts, Context)
|
||||
catch
|
||||
error:{woody_error, {external, result_unknown, <<"timeout">>}} -> ok
|
||||
end,
|
||||
|
||||
try woody_client:call(Request, Opts, Context)
|
||||
catch
|
||||
error:{woody_error, {internal, resource_unavailable, <<"deadline reached">>}} -> ok
|
||||
end.
|
||||
?assertError(
|
||||
{woody_error, {external, result_unknown, <<"timeout">>}},
|
||||
woody_client:call(Request, Opts, Context)
|
||||
),
|
||||
?assertError(
|
||||
{woody_error, {internal, resource_unavailable, <<"deadline reached">>}},
|
||||
woody_client:call(Request, Opts, Context)
|
||||
).
|
||||
|
||||
server_http_req_validation_test(Config) ->
|
||||
HeadersMode = proplists:get_value(client_headers_mode, Config),
|
||||
|
Loading…
Reference in New Issue
Block a user